【问题标题】:Fastest way to get the first 5 digits of a BigInteger获取 BigInteger 前 5 位数字的最快方法
【发布时间】:2021-08-27 01:39:23
【问题描述】:

假设 nBigInteger,我想要它的前 5 位数字。我在考虑两种方式:

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

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

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

【问题讨论】:

  • 第一个除法/模数选项应该优于字符串选项,因为创建字符串(和获取子字符串)的成本往往很高。
  • this answer 使用字符串和 log10 进行测试,发现字符串解决方案要慢 3 倍(尽管他们尝试做 2 件不同的事情)
  • @Hayden,在您发送的链接中,您能理解 Russ 的回答吗?我看不出他如何评估 BigInteger 的位数(他的答案似乎是最快的),因为他的函数接收一个整数。
  • Russ 的答案接收到一个整数作为数字的 number,计算的 BigInteger 是从那里创建的。您可以跳过它并从参数中获取 BigInteger。
  • BigInteger.TryFormat(Span ... 将避免字符串分配。

标签: c# algorithm optimization biginteger


【解决方案1】:

如果我们想找出前 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 数字怎么办?)

为了解决这两个问题,我自己计算Log10Log10(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;   
}

【讨论】:

    猜你喜欢
    • 2013-03-13
    • 2014-09-17
    • 2015-05-20
    • 1970-01-01
    • 2020-12-26
    • 2017-07-25
    • 1970-01-01
    • 1970-01-01
    • 2016-06-22
    相关资源
    最近更新 更多