2012-10-10 62 views
14

lo tanto, para ser breve,Cálculo de la raíz cúbica de número negativo

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

console.log(Math.pow(-8,1/3)); 
//Should be -2 

Pero cuando Lo pruebo, emite

NaN 

¿Por qué? ¿Es un error o se espera que sea así en primer lugar? Estoy usando JavaScript para dibujar gráficos, pero esto arruina el gráfico.

+3

multiplican 8^1/3 por -1 :-) – noel

+0

Hay una [raíz cúbica en ES6] nativo (http: //stackoverflow.com/a/29740048/1090562) –

Respuesta

17

Puede usar este fragmento para calcularlo. También funciona para otros poderes, p. 1/4, 1/5, etc.

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

Fuente: http://gotochriswest.com/blog/2011/05/06/cube-root-an-beyond/

Un enfoque más rápido por sólo calcular la raíz cúbica:

Math.cbrt = function(x) { 
    var sign = x === 0 ? 0 : x > 0 ? 1 : -1; 

    return sign * Math.pow(Math.abs(x), 1/3); 
} 

Math.cbrt(-8); 

actualización

Para encontrar un número entero de la raíz cúbica con base, se puede utilizar el siguiente función, inspirada en this answer:

// positive-only cubic root approximation 
function cbrt(n) 
{ 
    var a = n; // note: this is a non optimized assumption 

    while (a * a * a > n) { 
     a = Math.floor((2 * a + (n/(a * a)))/3); 
    } 

    return a; 
} 

Se inicia con una suposición de que converge al número entero más cercano a para los que a^3 <= n. Esta función se puede ajustar de la misma manera para admitir una base negativa.

+0

Gracias, esto funciona perfectamente. –

+0

@Derek Genial, he agregado una función más pequeña solo para la raíz cúbica. –

+1

Seguramente por coherencia debería devolver 'NaN' en lugar de tirar. Por cierto, ¿cuándo lanzaría ese código? –

10

No hay errores; estás elevando un número negativo a una potencia fraccionaria; por lo tanto, el NaN.

El mejor golpe en google para esto es de Dr Math, la explicación es bastante buena. Dice que para números reales (no números complejos de todos modos), un número negativo elevado a una potencia fraccionaria puede no ser un número real. El ejemplo más simple es probablemente

-4^(1/2)

que está computando esencialmente la raíz cuadrada de -4. Aunque la raíz cúbica de -8 tiene soluciones reales, creo que la mayoría de las bibliotecas de software encuentran más eficiente no hacer toda la aritmética compleja y devolver NaN solo cuando la parte imaginaria es distinta de cero y darle la respuesta real agradable de lo contrario.

EDITAR

sólo para hacer absolutamente claro que NaN es el resultado previsto, see the official ECMAScript 5.1 Specification, Section 15.8.2.13. Dice:

Si x < 0 yx es finito e y es finito y y no es un número entero, el resultado es NaN.

De nuevo, aunque ALGUNAS instancias de elevar números negativos a potencias fraccionarias tienen exactamente una raíz real, muchos lenguajes simplemente hacen lo mismo para todos los casos de números negativos que para raíces fraccionarias.

No crea que JavaScript es el único idioma. C++ does the same thing:

Si x es negativo finito y y es finito pero no tiene un valor entero, causa un error de dominio.

+0

Entonces, ¿cómo hago '3√ (-8)' sin hacer '8^(1/3)'? –

+1

Puede cambiar a Wolfram Alpha, que es mejor para manejar estas cosas que JavaScript: http://www.wolframalpha.com/input/?i=-8+%5E+%281%2F3%29 :) En serio, JavaScript simplemente toma la salida más fácil y dice NaN para estos, porque a menudo son números complejos, que JavaScript no admite de forma nativa. –

+0

+1, parte de la * respuesta * del foro de matemáticas probablemente debería ser replicada como parte de * su * –

4

me gustan las otras respuestas, pero, ¿y anulando Math.pow por lo que sería capaz de trabajar con todos raíces n-ésimas de los números negativos:

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

Fiddled con algunos casos de prueba, dame un grito si ¡Detectas un error!

+0

Realmente no me gusta 'pow_' flotando ... ¿Es posible poner el' pow' original dentro del nuevo 'pow'? –

+0

@Derek: sí, eso es lo que estoy haciendo.'pow' se convierte en' pow_', o podría ser 'pow_legacy' o lo que sea que necesites –

+0

No, quiero decir que no quiero una variable extra' Math.pow_'. Estoy pensando algo como esto: http://jsfiddle.net/DerekL/D32Td/ –

6

dos problemas clave:

  1. Matemáticamente, hay múltiples raíces cúbicos de un número negativo: -2, pero también 2 raíces complejas (véase cube roots of unity).
  2. El objeto Math de Javascript (y la mayoría de las demás bibliotecas matemáticas estándar) no hará potencias fraccionarias de números negativos. Convierte la potencia fraccional en un flotante antes de que la función la reciba, por lo que le pide a la función que calcule una potencia de punto flotante de un número negativo, que puede o no tener una solución real. Entonces hace lo pragmático y se niega a intentar calcular tal valor.

Si quiere obtener la respuesta correcta, tendrá que decidir qué tan matemáticamente correcta quiere ser y escribir esas reglas en una implementación no estándar de pow.

Todas las funciones de la biblioteca están limitadas para evitar tiempos de cálculo excesivos y una complejidad innecesaria.

+1

También hay múltiples raíces de cubos de cualquier número positivo. Para ser precisos, para cualquier número real 'x' y entero positivo' n' hay exactamente 'n' valores únicos para' y' sobre el plano complejo que satisface 'x = y^n'. Esto se deriva directamente del teorema fundamental del álgebra. – AJMansfield

2

que está trabajando en Chrome consola

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 
1

// no están al cubo raíces de números negativos como positivos los mismos, a excepción de la señal?

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 
*/ 
3

Así que ver un montón de métodos que giran en torno Math.pow(...) que es fresco, pero en base a la redacción de la generosidad que estoy proponiendo un enfoque ligeramente diferente.

Existen varias aproximaciones computacionales para resolver raíces, algunas dan pasos más rápidos que otras. En última instancia, el punto de detención se reduce al grado de precisión deseado (depende de usted/del problema que se resuelva).

No voy a explicar la matemáticas con gran detalle, pero los siguientes son puestas en práctica de aproximaciones de raíces cortadas en cubos que pasaron la prueba de destino (prueba de recompensa - rango negativo también agregó, debido al título de la pregunta). Cada iteración en el ciclo (ver los bucles while(Math.abs(xi-xi0)>precision) en cada método) se acerca más a la precisión deseada. Una vez que se logra la precisión, se aplica un formato al número, por lo que es tan preciso como el cálculo derivado de la iteración.

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

aproximación Implementación de Newton

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

Implementación aproximación Halley nota aproximación Halley toma medidas rápidas para resolver el cubo, por lo que es computacionalmente más rápido que la aproximación de Newton.

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

Como un aviso, en ES6 ahora hay una función Math.cbrt.

En mis pruebas en Google Chrome, parece que funciona casi el doble de rápido que Math.pow. Curiosamente, tuve que sumar los resultados; de lo contrario, Chrome hizo un mejor trabajo optimizando la función de 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); 

Resultado:

cbrt took:468.28200000163633 0 
pow took:77.21999999921536 0 
cbrt took:546.8039999977918 1615825909.5248165 
pow took:869.1149999940535 1615825826.7510242 
0

Sólo quiero destacar que en ES6 hay una función native cubic root. Por lo que sólo puede hacer esto (comprobar el apoyo here)

Math.cbrt(-8) le devolverá -2

Cuestiones relacionadas