【问题标题】:Find zero points with recursion递归查找零点
【发布时间】:2011-05-04 10:29:31
【问题描述】:

我想找到正弦函数的零点。参数是一个区间 [a,b]。我必须类似于二进制搜索。

实现一个函数,在 a 和 b 之间的区间内搜索 sinus 函数中的空点。 search-interval[lower limit, upper limit] 应该减半,直到下限和上限之间的距离小于 0.0001。

这是我的代码:

public class Aufg3 {
    public static void main(String[] args) {

        System.out.println(zeropoint(5,8));
    }

    private static double zeropoint(double a, double b){
        double middle = (a + b)/2;

        if(Math.sin(middle) < 0){
            return zeropoint(a,middle);
        }else if(Math.sin(middle) > 0){
            return zeropoint(middle,b);
        }else{
            return middle;
        }       
    }
}

它给了我很多错误 return zeropoint(middle,b);

第一步,我只想找到区间中的第一个零点。

有什么想法吗?

【问题讨论】:

  • 也许您应该为当前交替命名为middlemitte 的变量的所有用途使用相同的名称?
  • 另外,您目前正在呼叫Math.sin 两次。这是一个昂贵的函数,将调用分解为一个是一种很好的风格。
  • 另外,你永远不会得到 Math.sin() == 0。它是 double 和 double 几乎永远不能与 0 进行比较。像 abs(Math.sin(mitte))
  • @Vladimir,好吧,练习说直到下限和上限彼此之间的距离小于 0.0001 所以我会在b-a &lt; 0.0001 时终止递归。 (见my answer。)
  • 是的,弗拉基米尔说的。如果 b-a

标签: java math recursion binary-search


【解决方案1】:

大家都忽略的基本问题:

  • 我们并不总是希望返回结果(想象一下,在 pi/4 和 3pi/4 之间找到正弦函数的零点,没有)。
  • 在任意范围范围内都可能有多个零。

显然需要的是一组(可能是空的)值。

所以真的要求的函数的伪代码(不使用Java,因为这是家庭作业):

Set zeropoint(double a, double b)
{
    double middle = mid point of a and b;
    if a and be less than 0.0001 apart
    {
        if (sin(a) and sin(b) are on opposite sides of 0)
        { 
            return set containing middle
        }
        else
        {
            return empty set
        }
    }
    else
    {
        return union of zeropoint(a, middle) and zeropoint(middle, b)
    }
} 

【讨论】:

  • 我相信有一个不需要“一些小的限制”的解决方案
  • @aepryus:“在一些小的限制内”只是意味着你不能检查是否完全为零,因为 double 的性质意味着你很幸运能够达到 sin(middle) 恰好是的点0.00000000000000.
  • @aepryus 再次:刚刚意识到你的意思。将相应地编辑我的答案。
  • 是的,但是有办法解决这个问题。基本上,只是想看看函数是否在区间内穿过 x 轴,这不需要公差检查。
  • sin(a) 和 sin(b) 在 0 的对边是什么意思?
【解决方案2】:

简单地说“它给了我错误”并不是很有帮助。什么样的错误?在运行时编译错误或未捕获的异常?

对于您的代码,有两件事可能会引起问题:

  1. 变量mitte 似乎没有在任何地方声明。
  2. 您正在使用 > 和 ,以避免因浮点精度引起的问题。出于所有实际目的,-0.000000000001 为 0。

可能还有其他问题,我只是把第一眼跳出来的写下来。

编辑:

显然mitte 是由于 OP 在粘贴代码时出错(现已更正)。正如其他答案所指出的那样,代码陷入无限递归。这是因为递归调用的间隔错误。

需要注意的一点是,sin 函数对于 a 和 b 的一个选择可以单调递增,而在其他某个区间单调递减。例如它在 [0,pi/2] 上增加,在 [pi/2,3*pi/2] 上减少。因此,递归调用需要根据进行搜索的原始间隔进行更改。对于一个间隔 Math.sin(middle)

【讨论】:

  • 我从命令行运行这段代码,它所说的一切都是“在 Aufg3.nullstelle(Aufg3.java:18),重复 1 次
  • @ArtWorkAD:看起来像抛出堆栈溢出异常。这很可能是因为您已经将逻辑倒退(即搜索 [a,middle] 代替 [middle,b] 反之亦然),正如其他答案已经指出的那样。您的函数永远不会收敛于答案,而是陷入无限递归。我真的很惊讶它完全可以编译。变量mitte 从未声明过。还是您已经更正并发布了旧版本的代码?
  • 对不起,mitte 应该是中间的,在我的代码中它都是中间而不是 mitte
【解决方案3】:

我猜您在运行时遇到堆栈溢出错误。 符号相反。此外,您应该使用 .0001 而不是 0 进行比较。

编辑 1: 实际上,您的基本算法有问题。如果间隔中有多个零会发生什么?如果 sin(a) 和 sin(mitte) 有相同的符号会发生什么?如果区间中没有零会怎样?

编辑 2: 好的,所以我做了这个问题,从根本上说,你的解决方案是有问题的;我会尝试重新开始思考如何解决它。

主要问题是间隔中可能有多个零,而您正试图找到它们中的每一个。创建一个返回 double 类型的函数只能返回一个解决方案。因此,与其创建一个返回 double 的函数,不如返回 void 并在找到它们时打印出零。

另一个提示:您应该继续搜索,直到 a 和 b 彼此相距在 0​​.0001 以内。您的最终解决方案不会以任何其他方式使用 .0001。 (即,您检查是否找到零不应使用 .0001 容差,也不会完全使用 0。想想当 abs(ab) 小于 0.0001 时,您将如何真正知道是否找到零.

【讨论】:

  • 我必须找到一个零点,所以找到一个算法必须终止
  • 好吧,从技术上讲,在您的原始帖子中,您说“我想找到零点”。但是,除了考虑到问题的措辞和性质之外,我怀疑你的教授希望你能够找到所有的零。 JeremyP 的上述解决方案与我制定的解决方案非常相似,我强烈怀疑这就是教授在设计问题时的想法。我可能会简化它,不打扰 Set 机制,但您的最终解决方案应该非常相似。无论如何,祝你好运!
【解决方案4】:

你把作业读到最后了吗?它说:

搜索间隔[下限,上限 限制]应该减半直到更低 限制和上限小于然后 相距 0.0001。

因此,由于浮点精度问题,您不能指望Math.sin(middle) 准确返回零。相反,您需要在达到 0.0001 精度时停止递归。

【讨论】:

    【解决方案5】:

    我猜你遇到了StackOverflowError。这是因为您在递归中永远不会达到基本情况。 (Math.sin(middle) 可能永远不会正好等于 0!)

    你的练习说

    [...] 直到下限和上限之间的距离小于 0.0001。

    所以,试着把它放在你的方法之上:

    double middle = (a + b)/2;
    if (b - a < 0.0001)
        return middle;
    

    【讨论】:

      【解决方案6】:

      除了其他人提到的一些浮点问题外,您的算法似乎基于以下隐含假设:

      1. sin(a) 为正
      2. sin(b) 是负数,并且
      3. sin(x) 是区间 [a,b] 上的递减函数。

      我认为这些假设没有根据。当其中任何一个为假时,我不希望您的算法起作用。当 a=5 和 b=8 时,它们都是错误的。

      【讨论】:

        【解决方案7】:
        if(Math.sin(mitte) < 0){
        

        mitte 在哪里声明?不是mittemiddle吗?

        【讨论】:

        • 当发帖人将他的变量名翻译成英文时,这几乎肯定是丢失了。
        【解决方案8】:
        private static double zeropoint(double a, double b){
            double middle = (a + b)/2;
            double result = middle;
        
            if (Math.abs(a - b) > 0.0001) {
                double sin = Math.sin(middle);
                if (Math.abs(sin) < 0.0001) {
                   result = middle;
                } else if (sin > 0) {
                   result = zeropoint(a, middle);
                } else {
                   result = zeropoint(middle, b);
                }
            }
            return result;   
        }
        

        我认为是这样的——只是为了修复第一个错误

        【讨论】:

        • 如果 a = 0 且 b = PI/2 会怎样?那么结果是 PI/4?
        • 为什么你认为它是 PI/4 ? sin(PI/4) 是 sqrt(2)/2,而不是零
        • 是的,我还以为你在接受 a 和 b 的罪。
        • 无论如何,如果间隔中有多个零怎么办?因为这是家庭作业,所以我很犹豫给出正确的解决方案,但我怀疑这个解决方案会让他走错方向。
        • 我试过 [5,8] 它给了我 7,9999955 但我确信这不是零点
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-02
        • 1970-01-01
        • 2021-10-23
        • 2011-04-24
        • 1970-01-01
        • 2011-10-02
        相关资源
        最近更新 更多