BigInt 的对数
Logarithm of a BigInt
有没有办法得到 JavaScript 中 BigInt 的对数?
对于普通数字,您将使用此代码:
const largeNumber = 1000;
const result = Math.log(largeNumber);
但是,我需要使用阶乘数,可能高于 170!所以常规数字类型不起作用。 Math.log
不适用于 BigInt。那么如何得到对数呢?
const largeNumber = BigInt(1000);
const result = ???
你能看看这对你有用吗?函数returns一个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())
Log2 分别是:
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())
如果您不想 return BigInt
,那么以下方法也可能对您有用:
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()
将为您作为参数输入的任何 BigInt
或 Int 数字 return 一个标准精度浮点数。
正如@Mielipuoli 非常正确地提到的,自然对数可以计算为
function log(bigint) {
return log10(bigint) / Math.log10(Math.E);
}
或者,更简单,如我上面的代码片段所示,如 log10(bigint) * Math.log(10)
。
@Nat 已经在下面的评论中解释了这种方法的工作原理,即分别计算对数的整数和小数部分并将它们相加。关于结果的精度:Math.log10()
以其通常的 13 到 14 位十进制数字精度处理浮点数,因此,对于结果,这也是您所期望的。
出于这个原因,我将 BigInt 数字的字符串表示形式截断为 15 个字符。在隐式类型转换为 float 时,任何其他小数位都将被忽略。
我还在此处添加了十六进制字符串版本,由@PeterCordes 建议并由@somebody 进一步开发为natlog()
。它工作 - 可能比我原来的解决方案更快 - 并产生“相同”的结果(只有最后显示的数字在两个结果之间有偏差)!
受 MWO 回答的启发,您可以简单地将 BigInt 转换为与您要计算的对数具有相同底数的字符串,并获得字符串长度。
例如要计算 floor(log2(9007199254740991))
你可以 BigInt("9007199254740991").toString(2).length - 1
.
注意 toString 只允许从 2 到 36 的碱基。
其他答案已经充分解决了您在标题中提出的问题,即:“我如何计算 BigInt 的对数?”。但是,您还提到您对阶乘的对数特别感兴趣,对此不同的算法避免了范围困难。
应用 log(ab) = log(a) + log(b),以下函数计算阶乘的对数:
function logFactorial(n) {
let total = 0;
for (let current = 1; current <= n; ++current) {
total += Math.log10(current);
}
return total;
}
console.log(logFactorial(170));
根据我之前的评论,如果有人发现自己正在寻找一个非常高精度的对数,可以使用几个提供此功能的大十进制包。例如,下面的代码片段使用 decimal.js 来计算 1000 位的精度...
- 170!使用 BigInt 验证 170!使用 decimal.js
时
- 170!使用 decimal.js
- ln( 170!)
- log10( 170!)
- exp(ln(170!))
- round( exp( ln( 170! ) ) )
<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>
顺便说一句,有趣的是,即使是 1000 位数字,仍然存在舍入误差。通常情况下,人们会通过包括更多“隐藏”的小数位来进行一些加法精度的计算,然后四舍五入到所需的精度。
如果是纯字符串形式,我的就偷懒
- log() here means natural-log ln()
- length() here means string length, sometimes called len()
- input bigint_str x
( length(x) * log(10) ) + log( "0." x )
单行,没有循环,没有递归,没有专门的bigint
库——什么都没有。
当然,它的精度上限为 IEEE 64-bit double precision FP
,因此它精确到 15 or so
有效小数 digits
。
因为一个在前面加上“0”。在第二部分,该部分不会溢出或下溢,除非您的字符串太长,例如比如超过 500k 位等
如果是这样,trim 它下降到前 300 位左右 - 这已经足够了,因为总的来说,它由描述数量级的左侧术语主导,右侧侧仅执行较小的精度调整
有没有办法得到 JavaScript 中 BigInt 的对数?
对于普通数字,您将使用此代码:
const largeNumber = 1000;
const result = Math.log(largeNumber);
但是,我需要使用阶乘数,可能高于 170!所以常规数字类型不起作用。 Math.log
不适用于 BigInt。那么如何得到对数呢?
const largeNumber = BigInt(1000);
const result = ???
你能看看这对你有用吗?函数returns一个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())
Log2 分别是:
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())
如果您不想 return BigInt
,那么以下方法也可能对您有用:
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()
将为您作为参数输入的任何 BigInt
或 Int 数字 return 一个标准精度浮点数。
正如@Mielipuoli 非常正确地提到的,自然对数可以计算为
function log(bigint) {
return log10(bigint) / Math.log10(Math.E);
}
或者,更简单,如我上面的代码片段所示,如 log10(bigint) * Math.log(10)
。
@Nat 已经在下面的评论中解释了这种方法的工作原理,即分别计算对数的整数和小数部分并将它们相加。关于结果的精度:Math.log10()
以其通常的 13 到 14 位十进制数字精度处理浮点数,因此,对于结果,这也是您所期望的。
出于这个原因,我将 BigInt 数字的字符串表示形式截断为 15 个字符。在隐式类型转换为 float 时,任何其他小数位都将被忽略。
我还在此处添加了十六进制字符串版本,由@PeterCordes 建议并由@somebody 进一步开发为natlog()
。它工作 - 可能比我原来的解决方案更快 - 并产生“相同”的结果(只有最后显示的数字在两个结果之间有偏差)!
受 MWO 回答的启发,您可以简单地将 BigInt 转换为与您要计算的对数具有相同底数的字符串,并获得字符串长度。
例如要计算 floor(log2(9007199254740991))
你可以 BigInt("9007199254740991").toString(2).length - 1
.
注意 toString 只允许从 2 到 36 的碱基。
其他答案已经充分解决了您在标题中提出的问题,即:“我如何计算 BigInt 的对数?”。但是,您还提到您对阶乘的对数特别感兴趣,对此不同的算法避免了范围困难。
应用 log(ab) = log(a) + log(b),以下函数计算阶乘的对数:
function logFactorial(n) {
let total = 0;
for (let current = 1; current <= n; ++current) {
total += Math.log10(current);
}
return total;
}
console.log(logFactorial(170));
根据我之前的评论,如果有人发现自己正在寻找一个非常高精度的对数,可以使用几个提供此功能的大十进制包。例如,下面的代码片段使用 decimal.js 来计算 1000 位的精度...
- 170!使用 BigInt 验证 170!使用 decimal.js 时
- 170!使用 decimal.js
- ln( 170!)
- log10( 170!)
- exp(ln(170!))
- round( exp( ln( 170! ) ) )
<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>
顺便说一句,有趣的是,即使是 1000 位数字,仍然存在舍入误差。通常情况下,人们会通过包括更多“隐藏”的小数位来进行一些加法精度的计算,然后四舍五入到所需的精度。
如果是纯字符串形式,我的就偷懒
- log() here means natural-log ln()
- length() here means string length, sometimes called len()
- input bigint_str x
( length(x) * log(10) ) + log( "0." x )
单行,没有循环,没有递归,没有专门的bigint
库——什么都没有。
当然,它的精度上限为 IEEE 64-bit double precision FP
,因此它精确到 15 or so
有效小数 digits
。
因为一个在前面加上“0”。在第二部分,该部分不会溢出或下溢,除非您的字符串太长,例如比如超过 500k 位等
如果是这样,trim 它下降到前 300 位左右 - 这已经足够了,因为总的来说,它由描述数量级的左侧术语主导,右侧侧仅执行较小的精度调整