¿Hay alguna forma de obtener el logaritmo de un BigInt en JavaScript?
Con números normales, usaría este código:
const largeNumber = 1000; const result = Math.log(largeNumber);
Sin embargo, necesito trabajar con números factoriales, ¡potencialmente superiores a 170!, por lo que el tipo de número normal no funciona. Math.log
no funciona con BigInt. Entonces, ¿cómo obtengo el logaritmo?
const largeNumber = BigInt(1000); const result = ???
¿Podrías comprobar si esto te funciona? La función devuelve un BigInt.
function log10(bigint) { const n = bigint.toString(10).length; return bigint > 0n ? BigInt(n - 1) : null; } const largeNumber = BigInt('9039845039485903949384755723427863486200719925474009384509283489374539477777093824750398247503894750384750238947502389475029384755555555555555555555555555555555555555554444444444444444444444444222222222222222222222255666666666666938475938475938475938408932475023847502384750923847502389475023987450238947509238475092384750923847502389457028394750293847509384570238497575938475938475938475938475555555555559843991') console.log(log10(largeNumber).toString())
Para Log2 sería este respectivamente:
const largeNumber = BigInt('9039845039485903949384755723427863486200719925474009384509283489374539477777093824750398247503894750384750238947502389475029384755555555555555555555555555555555555555554444444444444444444444444222222222222222222222255666666666666938475938475938475938408932475023847502384750923847502389475023987450238947509238475092384750923847502389457028394750293847509384570238497575938475938475938475938475555555555559843991') function log2(bigint) { const n = bigint.toString(2).length; return bigint > 0n ? BigInt(n - 1) : null; } console.log(log2(largeNumber).toString())
En caso de que no desee devolver un BigInt
, lo siguiente también podría funcionar para usted:
function log10(bigint) { if (bigint < 0) return NaN; const s = bigint.toString(10); return s.length + Math.log10("0." + s.substring(0, 15)) } function log(bigint) { return log10(bigint) * Math.log(10); } function natlog(bigint) { if (bigint < 0) return NaN; const s = bigint.toString(16); const s15 = s.substring(0, 15); return Math.log(16) * (s.length - s15.length) + Math.log("0x" + s15); } const largeNumber = BigInt('9039845039485903949384755723427863486200719925474009384509283489374539477777093824750398247503894750384750238947502389475029384755555555555555555555555555555555555555554444444444444444444444444222222222222222222222255666666666666938475938475938475938408932475023847502384750923847502389475023987450238947509238475092384750923847502389457028394750293847509384570238497575938475938475938475938475555555555559843991'); console.log(natlog(largeNumber)); // 948.5641152531601 console.log(log10(largeNumber), log(largeNumber), log(-1)) // 411.95616098588766 // 948.5641152531603 // NaN
log10()
devolverá un valor flotante de precisión estándar para cualquier número BigInt
o Int que ingrese como argumento.
Como mencionó acertadamente @Mielipuoli, el logaritmo natural se puede calcular como
function log(bigint) { return log10(bigint) / Math.log10(Math.E); }
O, incluso más simple, como se muestra en mi fragmento anterior, como log10(bigint) * Math.log(10)
.
@Nat ya explicó en un comentario a continuación cómo funciona este enfoque, es decir, calculando las partes enteras y fraccionarias del logaritmo por separado y resumiéndolas. Con respecto a la precisión del resultado: Math.log10()
funciona en un número flotante con su precisión habitual de 13 a 14 dígitos decimales, por lo que, para un resultado, esto es todo lo que puede esperar también.
Por esta razón, trunqué la representación de cadena del número BigInt a 15 caracteres. Cualquier lugar decimal adicional se habría ignorado en la conversión de tipo implícita para flotar de todos modos.
También agregué la versión de cadena hexadecimal aquí, sugerida por @PeterCordes y desarrollada por @somebody como natlog()
. Funciona, probablemente más rápido que mi solución original, y produce el "mismo" resultado (¡solo el último dígito mostrado se desvía entre los dos resultados)!
Inspirándose en la respuesta de MWO, podría simplemente convertir BigInt en una cadena con la misma base que el logaritmo que desea calcular y obtener la longitud de la cadena.
Por ejemplo, para calcular floor(log2(9007199254740991))
puede hacer BigInt("9007199254740991").toString(2).length - 1
.
Tenga en cuenta que toString solo permite bases de 2 a 36.
Las otras respuestas han abordado adecuadamente la pregunta que da en el título, a saber: "¿cómo calculo el logaritmo de un BigInt?". Sin embargo, también menciona que está particularmente interesado en los logaritmos de factoriales, para los cuales un algoritmo diferente evita sus dificultades de rango.
Aplicando log(ab) = log(a) + log(b), la siguiente función calcula el logaritmo de un factorial:
function logFactorial(n) { let total = 0; for (let current = 1; current <= n; ++current) { total += Math.log10(current); } return total; } console.log(logFactorial(170));
Continuando con mi comentario anterior, si alguna vez se encuentra buscando un logaritmo de precisión realmente alta, hay un par de grandes paquetes decimales disponibles que ofrecen esta capacidad. Por ejemplo, el fragmento de código a continuación utiliza decimal.js con una precisión de 1000 dígitos para calcular...
<style> textarea { width: 100%; height: 100vh; } </style> <textarea id=result width:"100%" height:"100vh"></textarea> <script src="https://cdnjs.cloudflare.com/ajax/libs/decimal.js/10.3.1/decimal.min.js"></script> <script> let result = document.getElementById( 'result' ); Decimal.precision = 1000; Decimal.toExpPos = 1000; b = BigInt( 1 ); d = new Decimal( 1 ); for ( let di = 2, bi = 2n; di <= 170; di++, bi++ ) { d = Decimal.mul( d, di ); b = b * bi; } result.value = `BigInt 170! = ${b}\n\n`; result.value += `decimal.js 170! = ${d.toString()}\n\n`; result.value += `ln( 170! ) = ${Decimal.ln( d ).toString()}\n\n`; result.value += `log10( 170! ) = ${Decimal.log10( d ).toString()}\n\n`; result.value += `exp( ln ( 170! ) ) = ${Decimal.exp( Decimal.ln( d ) ).toString()}\n\n`; result.value += `round( exp( ln ( 170! ) ) ) = ${Decimal.round( Decimal.exp( Decimal.ln( d ) ) ).toString()}\n\n`; </script>
Aparte, curiosamente, incluso con 1000 dígitos, todavía hay errores de redondeo. Por lo general, uno hará los cálculos con cierta precisión adicional al incluir algunos lugares decimales "ocultos" más, y luego redondeará a la precisión deseada.