当前位置: 首页 > 知识库问答 >
问题:

BigInteger:在可扩展的方法中计算小数位数

咸臻
2023-03-14

我需要一个大整数的小数位数。例如:

  • 99返回2
  • 1234返回4
  • 9999返回4
  • 12345678901234567890返回20

我需要对biginger184948十进制数字及更多数字执行此操作。我如何快速且可扩展地完成这项工作?

转换为字符串的方法很慢:

public String getWritableNumber(BigInteger number) {
   // Takes over 30 seconds for 184948 decimal digits
   return "10^" + (number.toString().length() - 1);
}

这种10倍循环的方法更慢:

public String getWritableNumber(BigInteger number) {
    int digitSize = 0;
    while (!number.equals(BigInteger.ZERO)) {
        number = number.divide(BigInteger.TEN);
        digitSize++;
    }
    return "10^" + (digitSize - 1);
}

有没有更快的方法?

共有3个答案

微生德运
2023-03-14

我认为可以使用bitLength()获得log2值,然后将基数改为10。

然而,结果可能错了一位数,所以这只是一个近似值。

然而,如果这是可以接受的,你总是可以给结果加1,并将其限制为最多。或者,减去1,至少得到。

李睿
2023-03-14

这看起来很有效。我还没有运行详尽的测试,也没有运行任何时间测试,但它似乎有一个合理的运行时间。

public class Test {
  /**
   * Optimised for huge numbers.
   *
   * http://en.wikipedia.org/wiki/Logarithm#Change_of_base
   *
   * States that log[b](x) = log[k](x)/log[k](b)
   *
   * We can get log[2](x) as the bitCount of the number so what we need is
   * essentially bitCount/log[2](10). Sadly that will lead to inaccuracies so
   * here I will attempt an iterative process that should achieve accuracy.
   *
   * log[2](10) = 3.32192809488736234787 so if I divide by 10^(bitCount/4) we
   * should not go too far. In fact repeating that process while adding (bitCount/4)
   * to the running count of the digits will end up with an accurate figure
   * given some twiddling at the end.
   * 
   * So here's the scheme:
   * 
   * While there are more than 4 bits in the number
   *   Divide by 10^(bits/4)
   *   Increase digit count by (bits/4)
   * 
   * Fiddle around to accommodate the remaining digit - if there is one.
   * 
   * Essentially - each time around the loop we remove a number of decimal 
   * digits (by dividing by 10^n) keeping a count of how many we've removed.
   * 
   * The number of digits we remove is estimated from the number of bits in the 
   * number (i.e. log[2](x) / 4). The perfect figure for the reduction would be
   * log[2](x) / 3.3219... so dividing by 4 is a good under-estimate. We 
   * don't go too far but it does mean we have to repeat it just a few times.
   */
  private int log10(BigInteger huge) {
    int digits = 0;
    int bits = huge.bitLength();
    // Serious reductions.
    while (bits > 4) {
      // 4 > log[2](10) so we should not reduce it too far.
      int reduce = bits / 4;
      // Divide by 10^reduce
      huge = huge.divide(BigInteger.TEN.pow(reduce));
      // Removed that many decimal digits.
      digits += reduce;
      // Recalculate bitLength
      bits = huge.bitLength();
    }
    // Now 4 bits or less - add 1 if necessary.
    if ( huge.intValue() > 9 ) {
      digits += 1;
    }
    return digits;
  }

  // Random tests.
  Random rnd = new Random();
  // Limit the bit length.
  int maxBits = BigInteger.TEN.pow(200000).bitLength();

  public void test() {
    // 100 tests.
    for (int i = 1; i <= 100; i++) {
      BigInteger huge = new BigInteger((int)(Math.random() * maxBits), rnd);
      // Note start time.
      long start = System.currentTimeMillis();
      // Do my method.
      int myLength = log10(huge);
      // Record my result.
      System.out.println("Digits: " + myLength+ " Took: " + (System.currentTimeMillis() - start));
      // Check the result.
      int trueLength = huge.toString().length() - 1;
      if (trueLength != myLength) {
        System.out.println("WRONG!! " + (myLength - trueLength));
      }
    }
  }

  public static void main(String args[]) {
    new Test().test();
  }

}

在我的Celeron M笔记本电脑上花了大约3秒,所以在一些像样的装备上应该会达到2秒以下。

洪琦
2023-03-14

下面是一个基于Dariusz答案的快速方法:

public static int getDigitCount(BigInteger number) {
  double factor = Math.log(2) / Math.log(10);
  int digitCount = (int) (factor * number.bitLength() + 1);
  if (BigInteger.TEN.pow(digitCount - 1).compareTo(number) > 0) {
    return digitCount - 1;
  }
  return digitCount;
}

以下代码测试数字1、9、10、99、100、999、1000等,一直测试到一万位:

public static void test() {
  for (int i = 0; i < 10000; i++) {
    BigInteger n = BigInteger.TEN.pow(i);
    if (getDigitCount(n.subtract(BigInteger.ONE)) != i || getDigitCount(n) != i + 1) {
      System.out.println("Failure: " + i);
    }
  }
  System.out.println("Done");
}

这可以在一秒钟内检查具有184,948十进制数字和更多数字的BigIntger

 类似资料:
  • 问题内容: 我需要计算的小数位数。例如: 退货 退货 退货 退货 我需要做的这 一个与十进制数字多。 我该如何快速且可扩展? 在 转换到字符串的 方法是缓慢的: 这种 按十个循环进行循环的 方法甚至更慢: 有没有更快的方法? 问题答案: 这看起来像在工作。我还没有进行详尽的测试,也没有进行任何时间的测试,但似乎运行时间合理。 在我的Celeron M笔记本电脑上花了大约3秒钟,因此它在某些不错的工

  • 我正在尝试在旁边使用值方法。 不幸的是,编译器说不兼容的类型。 如果我将s更改为s,它仍然不喜欢它。

  • 问题内容: 用MySQL计算中位数的最简单方法(希望不是太慢)是什么?我一直在寻找均值,但是我很难找到一种简单的计算中位数的方法。现在,我将所有行返回给PHP,进行排序,然后选择中间行,但是肯定必须有一个简单的方法可以在单个MySQL查询中完成。 示例数据: 排序给出,因此中位数应为,而其中== 。 问题答案: 在MariaDB / MySQL中: 史蒂夫·科恩(Steve Cohen)指出,在第

  • 我试图使用值在C#中实现以下Java函数,以便不再适用64位限制。作为一项健全性检查,我将使用的原始函数也转换为C#代码。然而,问题是,使用的版本工作时,使用的版本并不总是返回相同的结果。 C#中原始函数的实现。 而不是像原始的Java代码那样打印所有的值,我打算使用它们,这样我就可以单独返回每个值。 在组合学中,使用choose函数可以很容易地验证生成的数字集合是否具有正确的值数: 从52张扑克

  • 本文向大家介绍C#中的扩展方法,包括了C#中的扩展方法的使用技巧和注意事项,需要的朋友参考一下 扩展方法是静态方法,就像它们是扩展类型的实例方法一样被调用。使用扩展方法,您可以将方法添加到现有类型中,而无需创建新的派生类型,重新编译或修改原始类型。 以下是我们创建的扩展方法。 让我们看一个使用扩展方法的例子。 示例 输出结果

  • 本文向大家介绍Laravel框架中扩展函数、扩展自定义类的方法,包括了Laravel框架中扩展函数、扩展自定义类的方法的使用技巧和注意事项,需要的朋友参考一下 一、扩展自己的类 在app/ 下建立目录 libraries\class  然后myTest.php 类名格式 驼峰 myTest 在 app/start/global.php 用 make 载入 二、扩展自己的函数 在app/ 下建立目录