Javascript 计算负数的立方根

Javascript 计算负数的立方根,javascript,math,Javascript,Math,简而言之 三,√(-8)=-8 1/3 但当我测试它时,它会输出 NaN 为什么??它是一个bug还是一开始就被认为是这样的?我正在使用JavaScript绘制图形,但这会弄乱图形。没有错误;你把一个负数提升到分数次幂;因此,南 谷歌最受欢迎的是它的解释很好。它说对于实数(无论如何不是复数),一个负数提升到分数次幂可能不是实数。最简单的例子可能是 -4^(1/2) 基本上是计算-4的平方根。尽管-8的立方根确实有实解,但我认为大多数软件库发现,不做所有的复数运算,只在虚部不为零时返回NaN更有

简而言之

三,√(-8)=-8 1/3

但当我测试它时,它会输出

NaN

为什么??它是一个bug还是一开始就被认为是这样的?我正在使用JavaScript绘制图形,但这会弄乱图形。

没有错误;你把一个负数提升到分数次幂;因此,南

谷歌最受欢迎的是它的解释很好。它说对于实数(无论如何不是复数),一个负数提升到分数次幂可能不是实数。最简单的例子可能是

-4^(1/2)

基本上是计算-4的平方根。尽管-8的立方根确实有实解,但我认为大多数软件库发现,不做所有的复数运算,只在虚部不为零时返回NaN更有效,否则会给出很好的实解

编辑

只是要明确指出,
NaN
是预期结果。它说:


如果x,您可以使用此代码段来计算它。它也适用于其他电源,例如
1/4
1/5

function nthroot(x, n) {
  try {
    var negate = n % 2 == 1 && x < 0;
    if(negate)
      x = -x;
    var possible = Math.pow(x, 1 / n);
    n = Math.pow(possible, n);
    if(Math.abs(x - n) < 1 && (x > 0 == n > 0))
      return negate ? -possible : possible;
  } catch(e){}
}

nthroot(-8, 3);
更新

要查找基于整数的立方根,可以使用以下函数,其灵感来自:


它首先假设收敛到最接近的整数
a
,对于这个整数
a^3我喜欢其他答案,但是覆盖
Math.pow
怎么样,这样它就可以处理负数的所有第n个根:

//keep the original method for proxying
Math.pow_ = Math.pow;

//redefine the method
Math.pow = function(_base, _exponent) {
  if (_base < 0) {
    if (Math.abs(_exponent) < 1) {
      //we're calculating nth root of _base, where n === 1/_exponent
      if (1 / _exponent % 2 === 0) {
        //nth root of a negative number is imaginary when n is even, we could return
        //a string like "123i" but this would completely mess up further computation
        return NaN;
      }/*else if (1 / _exponent % 2 !== 0)*/
      //nth root of a negative number when n is odd
      return -Math.pow_(Math.abs(_base), _exponent);
    }
  }/*else if (_base >=0)*/
  //run the original method, nothing will go wrong
  return Math.pow_(_base, _exponent);
};
//保留代理的原始方法
Math.pow=Math.pow;
//重新定义方法
Math.pow=函数(_基,_指数){
如果(_base<0){
if(数学绝对值(_指数)<1){
//我们正在计算_基的n根,其中n==1/_指数
如果(1/_指数%2==0){
//负数的n根是虚数,当n为偶数时,我们可以返回
//类似于“123i”的字符串,但这将完全扰乱进一步的计算
返回NaN;
}/*否则如果(1/_指数%2!==0)*/
//n为奇数时负数的n根
return-Math.pow(Math.abs(_base),_指数);
}
}/*如果(_base>=0),则为else*/
//运行原始方法,不会出错
返回Math.pow(基数,指数);
};
对于一些测试用例,如果你发现一个bug,请给我一个提示

两个关键问题:

  • 从数学上讲,负数有多个立方根:-2,但也有两个复数根(请参阅)
  • Javascript的
    Math
    对象(以及大多数其他标准数学库)不会执行负数的分数幂运算。它会在函数接收分数幂之前将分数幂转换为浮点,因此您要求函数计算负数的浮点幂,这可能有也可能没有实数解。因此,它做了实事求是的事情,拒绝尝试计算这样的价值
  • 如果你想得到正确的答案,你需要决定你想要的数学正确程度,并将这些规则写入
    pow
    的非标准实现中


    所有库函数都受到限制,以避免过多的计算时间和不必要的复杂性

    它在Chrome控制台中工作

    function cubeRoot(number) {
        var num = number;
        var temp = 1;
        var inverse = 1 / 3;
        if (num < 0) {
            num = -num;
            temp = -1;
        }
        var res = Math.pow(num, inverse);
        var acc = res - Math.floor(res);
        if (acc <= 0.00001)
            res = Math.floor(res);
        else if (acc >= 0.99999)
            res = Math.ceil(res);
        return (temp * res);
    }
    
    cubeRoot(-64) // -4
    cubeRoot(64)  //  4
    
    函数cubeRoot(编号){
    var num=数字;
    var-temp=1;
    var逆=1/3;
    if(num<0){
    num=-num;
    温度=-1;
    }
    var res=数学功率(num,逆);
    var acc=res-数学地板(res);
    如果(acc=0.99999)
    res=数学单元(res);
    返回(临时*res);
    }
    cubeRoot(-64)/-4
    cubeRoot(64)//4
    
    //除了符号之外,负数的立方根不是与正数相同吗

    Math.cubeRoot= function(n, r){
        var sign= (n<0)? -1: 1;
        return sign*Math.pow(Math.abs(n), 1/3);
    }
    Math.cubeRoot(-8)
    
    /*  returned value: (Number)
    -2
    */
    
    Math.cubeRoot=函数(n,r){
    
    var sign=(n所以我看到了一系列围绕
    Math.pow(…)
    的方法,这很酷,但基于赏金的措辞,我提出了一种稍微不同的方法

    求解根有几种计算近似方法,有些方法比其他方法更快。最终,停止点会下降到所需的精度(取决于您/正在解决的问题)

    我不打算详细解释数学,但以下是通过目标测试(赏金测试-由于问题标题,还增加了负范围)的立方根近似的实现。循环中的每个迭代(参见
    while(math.abs(xi-xi0)>precision)
    每个方法中的循环)获取更接近所需精度的一步。一旦达到精度,将对数字应用格式,使其与从迭代中导出的计算一样精确

    var precision = 0.0000000000001;
    function test_cuberoot_fn(fn) { 
        var tested = 0, 
        failed = 0; 
        for (var i = -100; i < 100; i++) { 
            var root = fn(i*i*i); 
            if (i !== root) { 
                console.log(i, root); 
                failed++; 
            } 
            tested++; 
        } 
        if (failed) { 
            console.log("failed %d / %d", failed, tested); 
        }else{
            console.log("Passed test");
        }
    }
    test_cuberoot_fn(newtonMethod);
    test_cuberoot_fn(halleysMethod);
    
    哈雷近似实现 请注意,哈雷近似法求解立方体的步骤更快,因此计算速度比牛顿近似法更快

    function halleysMethod(cube){
        if(cube == 0){//only John Skeet and Chuck Norris
            return 0; //can divide by zero, we'll have
        }             //to settle for check and return
        var xi = 1;
        var xi0 = -1;
        while(Math.abs(xi-xi0)>precision){//precision = 0.0000000000001
            xi0=xi;
            xi = xi*((xi*xi*xi + 2*cube)/(2*xi*xi*xi+cube));
        }
        return Number(xi.toPrecision(12));
    }
    

    首先,ES6中现在有一个Math.cbrt函数

    在我在谷歌chrome上的测试中,它的运行速度几乎是Math.pow的两倍。有趣的是,我不得不将结果相加,否则chrome在优化pow函数方面做得更好

    //do a performance test on the cube root function from es6
    var start=0, end=0, k=0;
    start = performance.now();
    k=0;
    for (var i=0.0; i<10000000.0; i+=1.0)
    {
        var j = Math.cbrt(i);
        //k+=j;
    }
    end = performance.now();
    console.log("cbrt took:" + (end-start),k);
    k=0;
    start = performance.now();
    for (var i=0.0; i<10000000.0; i+=1.0)
    {
        var j = Math.pow(i,0.33333333);
        //k+=j;
    }
    end = performance.now();
    console.log("pow took:" + (end-start),k);
    k=0;
    start = performance.now();
    for (var i=0.0; i<10000000.0; i+=1.0)
    {
        var j = Math.cbrt(i);
        k+=j;
    }
    end = performance.now();
    console.log("cbrt took:" + (end-start),k);
    k=0;
    start = performance.now();
    for (var i=0.0; i<10000000.0; i+=1.0)
    {
        var j = Math.pow(i,0.33333333);
        k+=j;
    }
    end = performance.now();
    console.log("pow took:" + (end-start),k);
    

    只想强调ES6中有一个函数,所以你可以这样做(检查支持)


    Math.cbrt(-8)
    将返回您的
    -2

    这适用于负数和负指数:

    函数n根(x=0,r=1){
    if(x<0){
    if(r%2==1)返回-nthRoot(-x,r)
    if(r%2==-1)返回-1/n根(-x,-r)
    }
    返回x**(1/r)
    }
    
    示例:

    nthRoot( 16,  2)   4
    nthRoot( 16, -2)   0.25
    nthRoot(-16,  2)   NaN
    nthRoot(-16, -2)   NaN
    nthRoot( 27,  3)   3
    nthRoot( 27, -3)   0.3333333333333333
    nthRoot(-27,  3)   -3
    nthRoot(-27, -3)   -0.3333333333333333
    

    那我怎么做
    3呢√(-8)
    不做
    8^(1/3)
    ?您可以切换到Wolfram Alpha,它比JavaScript更擅长处理这些事情:)认真地说,JavaScript只是简单地解决了这些问题,并对它们说NaN,因为它们通常是复数,JavaScript本机不支持它们
    function newtonMethod(cube){
        if(cube == 0){//only John Skeet and Chuck Norris
            return 0; //can divide by zero, we'll have
        }             //to settle for check and return
        var xi = 1;
        var xi0 = -1;
        while(Math.abs(xi-xi0)>precision){//precision = 0.0000000000001
            xi0=xi;
            xi = (1/3)*((cube/(xi*xi))+2*xi);
        }
        return Number(xi.toPrecision(12));
    }
    
    function halleysMethod(cube){
        if(cube == 0){//only John Skeet and Chuck Norris
            return 0; //can divide by zero, we'll have
        }             //to settle for check and return
        var xi = 1;
        var xi0 = -1;
        while(Math.abs(xi-xi0)>precision){//precision = 0.0000000000001
            xi0=xi;
            xi = xi*((xi*xi*xi + 2*cube)/(2*xi*xi*xi+cube));
        }
        return Number(xi.toPrecision(12));
    }
    
    //do a performance test on the cube root function from es6
    var start=0, end=0, k=0;
    start = performance.now();
    k=0;
    for (var i=0.0; i<10000000.0; i+=1.0)
    {
        var j = Math.cbrt(i);
        //k+=j;
    }
    end = performance.now();
    console.log("cbrt took:" + (end-start),k);
    k=0;
    start = performance.now();
    for (var i=0.0; i<10000000.0; i+=1.0)
    {
        var j = Math.pow(i,0.33333333);
        //k+=j;
    }
    end = performance.now();
    console.log("pow took:" + (end-start),k);
    k=0;
    start = performance.now();
    for (var i=0.0; i<10000000.0; i+=1.0)
    {
        var j = Math.cbrt(i);
        k+=j;
    }
    end = performance.now();
    console.log("cbrt took:" + (end-start),k);
    k=0;
    start = performance.now();
    for (var i=0.0; i<10000000.0; i+=1.0)
    {
        var j = Math.pow(i,0.33333333);
        k+=j;
    }
    end = performance.now();
    console.log("pow took:" + (end-start),k);
    
    cbrt took:468.28200000163633 0
    pow took:77.21999999921536 0
    cbrt took:546.8039999977918 1615825909.5248165
    pow took:869.1149999940535 1615825826.7510242
    
    nthRoot( 16,  2)   4
    nthRoot( 16, -2)   0.25
    nthRoot(-16,  2)   NaN
    nthRoot(-16, -2)   NaN
    nthRoot( 27,  3)   3
    nthRoot( 27, -3)   0.3333333333333333
    nthRoot(-27,  3)   -3
    nthRoot(-27, -3)   -0.3333333333333333