BigInt 的对数

Mie*_*oli 39 javascript logarithm biginteger bigint

有没有办法在 JavaScript 中获取BigInt的对数?

对于普通数字,您可以使用以下代码:

const largeNumber = 1000;
const result = Math.log(largeNumber);
Run Code Online (Sandbox Code Playgroud)

但是,我需要使用阶乘数字,可能高于 170!,因此常规数字类型不起作用。Math.log不适用于 BigInt。那么如何得到对数呢?

const largeNumber = BigInt(1000);
const result = ???
Run Code Online (Sandbox Code Playgroud)

car*_*10m 33

如果您不想返回 a 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
Run Code Online (Sandbox Code Playgroud)

log10()BigInt将为您作为参数输入的任何数字或 Int 数字返回标准精度浮点数。


正如@Mielipuoli 非常正确地提到的,自然对数可以计算为

function log(bigint) {
  return log10(bigint) / Math.log10(Math.E);
}
Run Code Online (Sandbox Code Playgroud)

或者,甚至更简单,如上面我的代码片段所示,如log10(bigint) * Math.log(10).

@Nat 已经在下面的评论中解释了这种方法的工作原理,即通过分别计算对数的整数部分和小数部分并将它们相加。关于结果的精度:它Math.log10()适用于具有通常 13 到 14 位小数精度的浮点数,因此,对于结果来说,这也是您所期望的。

因此,我将 BigInt 数字的字符串表示形式截断为 15 个字符。无论如何,在隐式类型转换为 float 时,任何其他小数位都会被忽略。

我还在这里添加了十六进制字符串版本,由 @PeterCordes 建议并由 @somebody 进一步开发为natlog(). 它的工作原理 - 可能比我原来的解决方案更快 - 并产生“相同”的结果(只有最后显示的数字在两个结果之间存在偏差)!

  • @Mielipuoli:如果您实际上并不想要 log10,那么基数 10 是一个不必要昂贵的转换基数选择。假设 BigInt 内部使用二进制块,转换为十六进制应该便宜得多,因为每个十六进制数字仅依赖于 4 位,而不依赖于所有较高位(这需要 BigInt 除法)。但获取小数部分就变得更加棘手;除非 JS 允许使用非十进制数的小数点,例如“0x0.abc123”。 (4认同)
  • 谢谢,这有效!小注意事项:它返回 log_10 而不是自然对数,但这可以通过将结果除以 Math.log10(Math.E) 来解决。另外,如果 bigint &lt; 0,我会返回 NaN 而不是 null。 (3认同)
  • 解释一下算法:log(a*b)=log(a)+log(b) ==&gt; log(a/b)=log(a)-log(b) ==&gt; log(a)=log( a/b)+log(b) ==&gt; log10(BigInt)=log10("0."+BigInt)+log10(10^BigInt.Length) ==&gt; log10(BigInt)=log10("0."+ BigInt)+BigInt.Length。 (3认同)
  • 这是关于为什么使用此计算以及近似值有多正确的解释性评论的地方之一,这将是一件非常好的事情。 (2认同)

Jac*_*ker 25

其他答案已经充分解决了您在标题中给出的问题,即:“如何计算 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));
Run Code Online (Sandbox Code Playgroud)

  • 如果您想要阶乘的对数,那么斯特林近似:log(n!)~ n log n - n + O(log n) 就足够了。您可以锐化近似值。参见例如 https://en.wikipedia.org/wiki/Stirling%27s_approximation (5认同)
  • 请注意,这会累积一些错误。这个错误有多严重……谁知道呢。但可能值得尝试其他方法。就像对 n 以下的每个质数的 log10 或 log 求和,例如 `Math.log(2) * (Math.floor(n / 2) + Math.floor(n / 4) + Math.floor(n / 8) .. .等)` (4认同)
  • 这实际上太棒了!那么我根本不需要 BigInt 。从技术上讲,它没有回答我发布的问题,因此我无法将其标记为这样。但这正是我所需要的,所以非常感谢!:) (3认同)
  • 是的,它看起来几乎像 https://www.google.com/search?q=xy+question 。对数相加比使用非常大的整数更有意义! (3认同)
  • 作为微优化,您可以从 2 开始循环,因为 log(1) = 0。 (2认同)