有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java BigInteger:计算可伸缩方法中的小数位数

我需要一个BigInteger的十进制数字计数。例如:

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

我需要对一个BigInteger184948个十进制数字和更多的的BigInteger执行此操作如何快速且可扩展地执行此操作

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

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

这种十次循环的方法甚至更慢:

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

有没有更快的方法


共 (5) 个答案

  1. # 1 楼答案

    这是比转换为字符串方法更快的另一种方法。虽然不是最佳运行时间,但仍然是合理的0.65秒,而使用Convert to String方法时为2.46秒(180000位)

    此方法根据给定值计算以10为底对数的整数部分。然而,它没有使用循环除法,而是使用了一种类似于平方求幂的技术

    下面是实现前面提到的运行时的粗略实现:

    public static BigInteger log(BigInteger base,BigInteger num)
    {
        /* The technique tries to get the products among the squares of base
         * close to the actual value as much as possible without exceeding it.
         * */
        BigInteger resultSet = BigInteger.ZERO;
        BigInteger actMult = BigInteger.ONE;
        BigInteger lastMult = BigInteger.ONE;
        BigInteger actor = base;
        BigInteger incrementor = BigInteger.ONE;
        while(actMult.multiply(base).compareTo(num)<1)
        {
            int count = 0;
            while(actMult.multiply(actor).compareTo(num)<1)
            {
                lastMult = actor; //Keep the old squares
                actor = actor.multiply(actor); //Square the base repeatedly until the value exceeds 
                if(count>0) incrementor = incrementor.multiply(BigInteger.valueOf(2));
                //Update the current exponent of the base
                count++;
            }
            if(count == 0) break;
    
            /* If there is no way to multiply the "actMult"
             * with squares of the base (including the base itself)
             * without keeping it below the actual value, 
             * it is the end of the computation 
             */
            actMult = actMult.multiply(lastMult);
            resultSet = resultSet.add(incrementor);
            /* Update the product and the exponent
             * */
            actor = base;
            incrementor = BigInteger.ONE;
            //Reset the values for another iteration
        }
        return resultSet;
    }
    public static int digits(BigInteger num)
    {
        if(num.equals(BigInteger.ZERO)) return 1;
        if(num.compareTo(BigInteger.ZERO)<0) num = num.multiply(BigInteger.valueOf(-1));
        return log(BigInteger.valueOf(10),num).intValue()+1;
    }
    

    希望这会有所帮助

  2. # 2 楼答案

    下面是一个基于Dariusz's answer的快速方法:

    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");
    }
    

    这可以检查BigInteger184,948十进制数字,以及一秒以内的更多数字

  3. # 3 楼答案

    您可以首先将BigInteger转换为BigDecimal,然后使用这个answer来计算位数。这似乎比使用BigInteger.toString()更有效,因为这将为String表示分配内存

        private static int numberOfDigits(BigInteger value) {
            return significantDigits(new BigDecimal(value));
        }
    
        private static int significantDigits(BigDecimal value) {
            return value.scale() < 0
                   ? value.precision() - value.scale()
                   : value.precision();
        }
    
  4. # 4 楼答案

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

    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秒以下

  5. # 5 楼答案

    我认为可以使用bitLength()获得log2值,然后使用change the base to 10

    结果可能是错误的,但是,一个数字,所以这只是一个近似值

    但是,如果这是可以接受的,您可以始终将1添加到结果中,并将其绑定为最多。或者,减去1,得到至少