Buscando una implementación realmente rápida de la función factorial en JavaScript. ¿Alguna sugerencia?
javascript
math
factorial
Conocido
fuente
fuente
Respuestas:
¡Puede buscar (1 ... 100)! en Wolfram | Alpha para calcular previamente la secuencia factorial.
Los primeros 100 números son:
Si aún desea calcular los valores usted mismo, puede utilizar la memorización :
Editar: 21.08.2014
Solucion 2
Pensé que sería útil agregar un ejemplo funcional de función factorial iterativa perezosa que usa números grandes para obtener un resultado exacto con memorización y caché como comparación
Supongo que usaría algún tipo de cierre para limitar la visibilidad del nombre de la variable.
Ref : BigNumber Sandbox : JsFiddle
fuente
function factorial (n) { for (var i = f.length; i <= n; i++) f.push(f[i - 1].multiply(i.toString())); return f[n]; }
ver también mi respuesta, que utiliza laBigInt
biblioteca incorporada más reciente en lugar de una biblioteca de terceros.Deberías usar un bucle.
Aquí hay dos versiones comparadas al calcular el factorial de 100 por 10.000 veces.
Recursivo
Iterativo
En vivo en: http://jsfiddle.net/xMpTv/
Mis resultados muestran:
- Recursivo ~ 150 milisegundos
- Iterativo ~ 5 milisegundos ..
fuente
rval = rval * i;
usted podría escribirrval *= i;
Sigo pensando que la respuesta de Margus es la mejor. Sin embargo, si también desea calcular los factoriales de números dentro del rango de 0 a 1 (es decir, la función gamma), entonces no puede usar ese enfoque porque la tabla de búsqueda tendrá que contener valores infinitos.
Sin embargo, puede aproximar los valores de los factoriales, y es bastante rápido, más rápido que llamarse a sí mismo de forma recursiva o hacer un bucle al menos (especialmente cuando los valores comienzan a aumentar).
Un buen método de aproximación es el de Lanczos.
Aquí hay una implementación en JavaScript (portado desde una calculadora que escribí hace meses):
Ahora puede hacer cosas interesantes como
factorial(0.41)
, etc., sin embargo, la precisión puede estar un poco fuera de lugar, después de todo, es una aproximación del resultado.fuente
var d3d4 = Math.exp((z + 0.5) * Math.log(z + 5.5) - z - 5.5); return d1 * d2 * d3d4;
. ¡Esto le permite calcular factoriales hasta 169! en lugar de actualmente solo 140 !. Esto está bastante cerca del factorial máximo representable usando elNumber
tipo de datos, que es 170 !.La tabla de búsqueda es la forma obvia de hacerlo, si está trabajando con números naturales. Para calcular cualquier factorial en tiempo real, puede acelerarlo con un caché, guardando los números que ha calculado antes. Algo como:
Puede calcular previamente algunos valores para acelerarlo aún más.
fuente
Aquí está mi solución:
Es la forma más sencilla (menos caracteres / líneas) que he encontrado, solo una función con una línea de código.
Editar:
si realmente desea guardar algunos caracteres, puede ir con una función de flecha (21 bytes) :
fuente
f=n=>n?f(n-1)*n:1
...Solo una línea con ES6
Mostrar fragmento de código
fuente
factorial = n => n <= 1 ? 1 : factorial(n - 1) * n
función recursiva corta y fácil (también podría hacerlo con un bucle, pero no creo que eso haga ninguna diferencia en el rendimiento):
para una n muy grande, podría usar la aproximación de Stirlings , pero eso solo le dará un valor aproximado.
EDITAR: un comentario sobre por qué recibo un voto negativo por esto hubiera sido bueno ...
EDIT2: esta sería la solución usando un bucle (que sería la mejor opción):
Creo que la mejor solución sería usar los valores en caché, como mencionó Margus, y usar la aproximación de Stirlings para valores más grandes (asumiendo que tienes que ser muy rápido y no tienes que ser tan exacto en números tan grandes).
fuente
He aquí, el memorizador, que toma cualquier función de un solo argumento y la memoriza. Resulta ser marginalmente más rápido que la solución de @ xPheRe , incluido el límite en el tamaño de la caché y la verificación asociada, porque uso cortocircuitos, etc.
Aproximadamente 25 veces más rápido en mi máquina en Chrome que la versión recursiva, y 10% más rápido que xPheRe.
fuente
Función factorial más rápida
Creo que esta versión basada en bucles podría ser la función factorial más rápida.
Y aquí está mi razonamiento:
for
bucles y loswhile
bucles tienen un rendimiento similar, unfor
bucle sin una expresión de inicialización y una expresión final parece extraño; probablemente sea mejor escribirfor(; n > 0;)
comowhile(n > 0)
n
yr
, por lo que, en teoría, menos parámetros significa menos tiempo dedicado a la asignación de memorian
es cero: he escuchado teorías de que las computadoras son mejores para verificar números binarios (0 y 1) que para verificar otros enterosfuente
Me encontré con esta publicación. Inspirado por todas las contribuciones aquí, se me ocurrió mi propia versión, que tiene dos características que no había visto antes discutidas: 1) Una verificación para asegurar que el argumento sea un número entero no negativo 2) Hacer una unidad del caché y la función para convertirlo en un bit de código autónomo. Para divertirme, traté de hacerlo lo más compacto posible. Algunos pueden encontrar eso elegante, otros pueden pensar que es terriblemente oscuro. De todos modos, aquí está:
Puede llenar previamente el caché o permitir que se llene a medida que pasan las llamadas. Pero el elemento inicial (de hecho (0) debe estar presente o se romperá.
Disfruta :)
fuente
Es muy simple usar ES6
const factorial = n => n ? (n * factorial(n-1)) : 1;
Vea un ejemplo aquí
fuente
Aquí hay una solución:
fuente
Usando ES6 puede lograrlo rápido y corto:
fuente
El código para calcular factorial depende de sus requisitos.
Con respecto a los puntos 1 y 4, a menudo es más útil tener una función para evaluar el logaritmo del factorial directamente que tener una función para evaluar el factorial en sí.
Aquí hay una publicación de blog que analiza estos problemas. Aquí hay algo de código C # para calcular el factorial de registro que sería trivial para migrar a JavaScript. Pero puede que no sea lo mejor para sus necesidades dependiendo de sus respuestas a las preguntas anteriores.
fuente
Esta es una versión compacta basada en bucle
O puede anular el objeto Math (versión recursiva):
O únete a ambos enfoques ...
fuente
Aprovechando el hecho de que
Number.MAX_VALUE < 171!
, simplemente podemos usar una tabla de búsqueda completa que consta de solo 171 elementos de matriz compacta que ocupan menos de 1.4 kilobytes de memoria.Una función de búsqueda rápida con complejidad en tiempo de ejecución O (1) y una sobrecarga mínima de acceso a la matriz se vería de la siguiente manera:
Esto es tan preciso y rápido como se obtiene utilizando el
Number
tipo de datos. Calcular la tabla de búsqueda en Javascript, como sugieren otras respuestas, reducirá la precisión cuandon! > Number.MAX_SAFE_INTEGER
.Comprimir la tabla de tiempo de ejecución a través de gzip reduce su tamaño en el disco de aproximadamente 3.6 a 1.8 kilobytes.
fuente
Respuesta de una línea:
fuente
Factorial iterativo con
BigInt
seguridadEste es un ejemplo de uso
BigInt
, porque muchas respuestas aquí escapan del límite seguro deNumber
(MDN) casi de inmediato. No es el más rápido, pero es simple y, por lo tanto, más claro para adaptar otras optimizaciones (como un caché de los primeros 100 números).Ejemplo de uso
n
al final de un literal numérico como1303n
indica que es unBigInt
tipo.BigInt
con aNumber
menos que los obligue explícitamente, y que hacerlo podría causar una pérdida de precisión.fuente
Usando las funciones de ES6, puede escribir código en UNA línea y sin recursividad :
Mostrar fragmento de código
fuente
Solo para completar, aquí hay una versión recursiva que permitiría la optimización de llamadas de cola. Sin embargo, no estoy seguro de si las optimizaciones de llamadas de cola se realizan en JavaScript ...
Para llamarlo:
fuente
Esta es una solución iterativa que usa menos espacio de pila y guarda los valores calculados previamente de una manera auto-memorizada:
También tenga en cuenta que estoy agregando esto al objeto Math, que es un objeto literal, por lo que no hay prototipo. En lugar de eso, simplemente vincularlos a la función directamente.
fuente
Math.factorial(100); Math.factorial(500);
calculará la multiplicación del 1 ... 100 dos veces.Creo que el siguiente es el fragmento de código más sostenible y eficiente de los comentarios anteriores. Puede usar esto en la arquitectura js de su aplicación global ... y no se preocupe por escribirlo en múltiples espacios de nombres (ya que es una tarea que probablemente no necesite mucho aumento). He incluido 2 nombres de métodos (según la preferencia) pero ambos se pueden usar ya que son solo referencias.
fuente
n * (n-1) * (n-2) * ... * 1
lugar de al revés, pierde hasta 4 dígitos en precisión para n >> 20.Esto hace el almacenamiento en caché de los primeros 100 valores sobre la marcha y no introduce una variable externa en el alcance de la caché, almacenando los valores como propiedades del objeto de función en sí, lo que significa que si sabe que
factorial(n)
ya se ha calculado, puede simplemente refiérase a él comofactorial[n]
, que es un poco más eficiente. Ejecutar estos primeros 100 valores tomará menos de milisegundos en los navegadores modernos.fuente
21! > Number.MAX_SAFE_INTEGER
, por lo tanto, no se puede representar con seguridad como un flotante de 64 bits.Aquí hay una implementación que calcula factoriales positivos y negativos. Es rápido y sencillo.
fuente
Aquí hay uno que hice yo mismo, no use números mayores de 170 o menores de 2.
fuente
i
y realiza demasiadasNumber
conversiones y da resultados incorrectos para 0! (como dijiste, pero ¿por qué?).Aqui esta mi codigo
fuente
factorial(0)
. Además, al comenzar su multiplicación con n * (n-1) * (n-2) * ... * 1 en lugar de al revés, pierde hasta 4 dígitos en precisión para n >> 20. @prime:170! > Number.MAX_VALUE
y se representa mejor conInfinity
.El bucle en caché debe ser más rápido (al menos cuando se llama varias veces)
fuente
Después de revisar la información de otros miembros (excluyendo el consejo de Log, aunque puedo implementarlo más adelante), seguí adelante y preparé un guión que es bastante simple. Comencé con un ejemplo simple de programación orientada a objetos de JavaScript sin educación y construí una pequeña clase para manejar factoriales. Luego implementé mi versión de la Memoización que se sugirió anteriormente. También implementé la abreviatura Factorialización, sin embargo hice un pequeño ajuste de error; Cambié "n <2" a "n <3". "n <2" seguiría procesando n = 2, lo que sería un desperdicio, porque iteraría para un 2 * 1 = 2; esto es un desperdicio en mi opinión. Lo modifiqué a "n <3"; porque si n es 1 o 2 simplemente devolverá n, si es 3 o más se evaluará normalmente. Por supuesto, a medida que se aplican las reglas, coloqué mis funciones en orden descendente de ejecución asumida. Agregué la opción bool (verdadero | falso) para permitir la alteración rápida entre la ejecución normal y la memorizada (nunca se sabe cuándo desea cambiar de página sin necesidad de cambiar el "estilo") Como dije antes de La variable factorial memorizada se establece con las 3 posiciones iniciales, tomando 4 caracteres y minimizando los cálculos innecesarios. Todo lo que pasa después de la tercera iteración está manejando matemáticas de dos dígitos más. Me imagino que si fueras lo suficientemente riguroso al respecto, ejecutarías en una tabla factorial (como se implementó). tomando 4 caracteres y minimizando los cálculos innecesarios. Todo lo que pasa después de la tercera iteración está manejando matemáticas de dos dígitos más. Me imagino que si fueras lo suficientemente riguroso al respecto, ejecutarías en una tabla factorial (como se implementó). tomando 4 caracteres y minimizando los cálculos innecesarios. Todo lo que pasa después de la tercera iteración está manejando matemáticas de dos dígitos más. Me imagino que si fueras lo suficientemente riguroso al respecto, ejecutarías en una tabla factorial (como se implementó).
¿Qué he planeado después de esto? almacenamiento local & | sesión para permitir un caché caso por caso de las iteraciones necesarias, esencialmente manejando el problema de la "tabla" mencionado anteriormente. Esto también ahorraría enormemente el espacio del lado del servidor y la base de datos. Sin embargo, si opta por localStorage, esencialmente estaría absorbiendo espacio en la computadora de sus usuarios simplemente para almacenar una lista de números y hacer que su pantalla se vea más rápido, sin embargo, durante un largo período de tiempo con una inmensa necesidad, esto sería lento. Estoy pensando que sessionStorage (limpiar después de que Tab se vaya) sería una ruta mucho mejor. ¿Posiblemente combinar esto con un servidor autoequilibrado / caché dependiente local? El usuario A necesita X iteraciones. El usuario B necesita Y iteraciones. X + Y / 2 = Cantidad necesaria almacenada en caché local. Luego, simplemente detecte y juegue con los puntos de referencia de tiempo de carga y tiempo de ejecución en vivo para cada usuario hasta que se ajuste a la optimización del sitio en sí. ¡Gracias!
Edición 3:
fuente
undefined
por 0 !. ES6 permite reemplazarisNumeric
conNumber.isInteger
. Las líneas comofactorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);
son totalmente ilegibles.Aquí hay uno que usa funciones de JavaScript más nuevas , llenar , asignar , reducir y construir (y sintaxis de flecha gruesa):
Editar: actualizado para manejar n === 0
fuente
n === 0
?Math.factorial = n => Array.from({ length: n }).reduce((product, _, i) => product * (i + 1), 1)
fuente