获取 BigInteger 前 5 位数字的最快方法

问题描述

假设 n一个 BigInteger 并且我想要它的前 5 位数字。我的想法有两种:

  1. n 除以 10 log10(n)-5 次(因此只保留前 5 位数字)。
  2. 获取 Substring(0,5) 字符串的 n

我不知道哪个更快,或者是否还有其他选项可能比这些更好。

在我测试这些选项之前,我想听听有关它的考虑(您如何看待它们,如果有更好的东西等)。

解决方法

如果我们想找出前5个前导数字,我们可以利用整数除法:

   123     / 1   == 123
   12345   / 1   == 12345
   1234567 / 100 == 12345

朴素的方法是将原始值除以 10,而它大于或等于 1_000_000

   1234567 => 123456 => 12345

然而,除法是很昂贵的,特别是如果我们有一个巨大的数字(如果我们有一个约 1000000 位的数字,我们必须将这个数字除以约 1000000 次)。创建更快的解决方案 我们可以计算一个合适的 10 幂(幂计算速度很快),然后只除一次:

  1234567 / Pow(10,Log10(1234567) - 5 + 1)

所以原始想法是

  result = source / BigInteger.Pow(10,(int)BigInteger.Log10(source) - 4);

这里有两个难点:

  1. 负数(我们至少在计算对数时应该取绝对值 source
  2. source 的四舍五入错误(如果我们四舍五入并且只有 4 位数字怎么办?)

为了解决这两个问题,我自己将 Log10 计算为 Log10(2) * Log2(source)

  value.GetBitLength() * 0.30102999566398

这保证我至少有 5 位数字,但在出现舍入错误的情况下可能是 6(注意 0.30102999566398 < Log10(2))。

综合:

private static int FirstDigits(BigInteger value) {
  if (value.IsZero)
    return 0; 

  int digits = 5;

  int result = (int)(value / 
    BigInteger.Pow(10,(int)(value.GetBitLength() * 0.30102999566398 - digits + 1)));

  while (result >= 1_000_000 || result <= -1_000_000)
    result /= 10;  

  return result;   
}