通过递归找到零点

时间:2010-11-03 11:04:43

标签: java math recursion binary-search

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

实现一个在a和b之间的间隔中搜索窦函数中的空点的函数。搜索间隔[下限,上限]应减半,直到下限和上限小于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;
        }       
    }
}

它在返回zeropoint(中间,b)的行中给了我很多错误;

在第一步中,我想找到间隔中的第一个零点。

有什么想法吗?

8 个答案:

答案 0 :(得分:6)

每个人都忽视的基本问题:

  • 我们并不总是希望返回结果(想象一下在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)
    }
} 

答案 1 :(得分:3)

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

对于您的代码,有两个问题可能会出现问题:

  1. 变量mitte似乎没有在任何地方声明。
  2. 您正在使用&gt;和&lt;比较实数。虽然这本身就可以,但最好使用容差检查0而不是依赖&lt;和&gt;,以避免由浮点精度引起的问题。出于所有实际目的,-0.000000000001为0。
  3. 也可能存在其他问题,我只是写下乍看之下跳出来的那些。

    编辑:

    显然mitte是由于OP通过粘贴代码时出错(并且已经被纠正)。正如其他答案所指出的那样,代码属于无限递归。这是因为递归调用的时间间隔错误。

    有一点需要注意,对于a和b的一个选择,sin函数可以单调递增,而在某个其他区间单调递减。例如它增加超过[0,pi / 2]并且在[pi / 2,3 * pi / 2]上减小。因此,递归调用需要根据进行搜索的原始间隔而改变。对于一个间隔,Math.sin(中)<0意味着Math.sin(x)<0,对于[a,middle]中的所有x ],但对于其他一些区间则相反。这可能是为什么这会在您尝试的间隔内进入无限递归。我认为这适用于罪恶实际减少的其他一些区间。尝试通过[pi / 2,3 * pi / 2]调用你的函数。

答案 2 :(得分:3)

我猜你在运行时遇到堆栈溢出错误。 &lt;和&gt;迹象是逆转的。此外,您应该使用.0001而不是0来比较。

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

编辑2: 好的,所以我解决了问题,从根本上说,你的解决方案存在问题;我会尝试重新思考如何解决它。

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

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

答案 3 :(得分:2)

你到底读完作业了吗?它说:

  

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

因为浮点精度问题,所以你不能指望Math.sin(middle)正好返回零。相反,当你达到0.0001精度时,你需要停止递归。

答案 4 :(得分:2)

我的猜测是你遇到了StackOverflowError。这是因为您在递归时从未达到基本情况。 (Math.sin(middle)可能永远不会等于完全 0!)

你的运动说

  

[...]直到下限和上限相距0.0001以下。

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

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

答案 5 :(得分:1)

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

  1. 罪(a)是正面的
  2. 罪(b)是否定的,
  3. sin(x)是区间[a,b]上的递减函数。
  4. 我认为这些假设没有根据。如果其中任何一个是假的,我不希望你的算法工作。当a = 5且b = 8时,它们都是假的。

答案 6 :(得分:0)

if(Math.sin(mitte) < 0){

mitte在哪里宣布?不是mitte middle

答案 7 :(得分:0)

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;   
}

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

相关问题