【问题标题】:How can I find the Square Root of a Java BigInteger?如何找到 Java BigInteger 的平方根?
【发布时间】:2011-05-23 09:53:07
【问题描述】:

是否有可以找到 BigInteger 平方根的库?我希望它离线计算 - 只计算一次,而不是在任何循环内。所以即使是计算昂贵的解决方案也可以。

我不想找到一些算法并实现。一个现成的解决方案将是完美的。

【问题讨论】:

  • 是将 BigInteger 转换为 java.lang.Math 可以使用的东西,还是需要保持为 BigInteger?
  • 600851475143 是数字。它可以用数学可以使用的东西来表示吗?我做不到,所以求助于 BigInteger。如果您想知道,这与 ProjectEuler 的问题有关 :)
  • 你的意思是一个数字一次吗?然后硬编码从说 wolframalpha 计算的值?
  • 欧拉项目问题 3 =) 我认为数字 (600851475143) 可以存储为长 (long n = 600851475143L)。
  • 这里已经有这样的答案,但它远远落后于列表,所以我将它作为评论发布以提高可见性:使用 Guava 的 BigIntegerMath。 sqrt。这是一个经过大量测试和优化的解决方案。

标签: java biginteger square-root


【解决方案1】:

只是为了好玩:

public static BigInteger sqrt(BigInteger x) {
    BigInteger div = BigInteger.ZERO.setBit(x.bitLength()/2);
    BigInteger div2 = div;
    // Loop until we hit the same value twice in a row, or wind
    // up alternating.
    for(;;) {
        BigInteger y = div.add(x.divide(div)).shiftRight(1);
        if (y.equals(div) || y.equals(div2))
            return y;
        div2 = div;
        div = y;
    }
}

【讨论】:

  • 是的,这对于任何(正)BigInteger 来说都是一个很好的通用解决方案,而且速度也不慢。对于具有一千位数字的 BigInteger,它只需要该循环的 11 次迭代就可以得到它的平方根,这对我来说似乎很神奇。我想知道为什么 BigInteger 的 API 中没有平方根方法?
  • 我怀疑 BigInteger 的存在是为了支持不需要平方根的加密。不过,只是猜测。
  • 根据上面的算法,sqrt(8) = 3...不要按原样使用,终止条件不太正确! (我们想要两个周期的 /lesser/,当它发生时......即。x = k^2-1
  • 虽然可以说,3 比 2 更接近正确答案
  • 我认为你可以返回 min(div, div2) 并且你会很高兴。
【解决方案2】:

我知道您的问题没有图书馆解决方案。您必须从某处导入外部库解决方案。我在下面给您的内容比获取外部库要简单。

您可以使用如下所示的两个静态方法在一个类中创建自己的外部库解决方案,并将其添加到您的外部库集合中。这些方法不需要是实例方法,因此它们是静态的,而且方便的是,您不必实例化类来使用它们。整数平方根的范数是一个底值(即小于或等于平方根的最大整数),因此您可能只需要下面类中的一个静态方法,底值方法,并且可以选择忽略上限(即大于或等于平方根的最小整数)方法版本。现在,它们在默认包中,但您可以添加一个包语句将它们放在您认为方便的任何包中。

这些方法非常简单,迭代非常非常快地收敛到最接近的整数答案。如果你试图给他们一个否定的论点,他们会抛出一个 IllegalArgumentException。您可以将异常更改为另一个异常,但您必须确保否定参数引发某种异常或至少不尝试计算。负数的整数平方根不存在,因为我们不在虚数领域。

这些来自非常著名的简单迭代整数平方根算法,这些算法已在手动计算中使用了几个世纪。它通过平均高估和低估来收敛到更好的估计。这可以重复,直到估计值尽可能接近。

它们基于 y1 = ((x/y0) + y0) / 2 收敛到最大整数 yn,其中 yn * yn

这将为您提供 x 的 BigInteger 平方根 y 的底值,其中 y * y x。

调整可以为您提供 x 的 BigInteger 平方根 y 的上限,其中 y * y >= x 和 (y - 1) * (y - 1)

这两种方法都已经过测试并且有效。他们在这里:

import java.math.BigInteger;

public class BigIntSqRoot {

public static BigInteger bigIntSqRootFloor(BigInteger x)
        throws IllegalArgumentException {
    if (x.compareTo(BigInteger.ZERO) < 0) {
        throw new IllegalArgumentException("Negative argument.");
    }
    // square roots of 0 and 1 are trivial and
    // y == 0 will cause a divide-by-zero exception
    if (x .equals(BigInteger.ZERO) || x.equals(BigInteger.ONE)) {
        return x;
    } // end if
    BigInteger two = BigInteger.valueOf(2L);
    BigInteger y;
    // starting with y = x / 2 avoids magnitude issues with x squared
    for (y = x.divide(two);
            y.compareTo(x.divide(y)) > 0;
            y = ((x.divide(y)).add(y)).divide(two));
    return y;
} // end bigIntSqRootFloor

public static BigInteger bigIntSqRootCeil(BigInteger x)
        throws IllegalArgumentException {
    if (x.compareTo(BigInteger.ZERO) < 0) {
        throw new IllegalArgumentException("Negative argument.");
    }
    // square roots of 0 and 1 are trivial and
    // y == 0 will cause a divide-by-zero exception
    if (x == BigInteger.ZERO || x == BigInteger.ONE) {
        return x;
    } // end if
    BigInteger two = BigInteger.valueOf(2L);
    BigInteger y;
    // starting with y = x / 2 avoids magnitude issues with x squared
    for (y = x.divide(two);
            y.compareTo(x.divide(y)) > 0;
            y = ((x.divide(y)).add(y)).divide(two));
    if (x.compareTo(y.multiply(y)) == 0) {
        return y;
    } else {
        return y.add(BigInteger.ONE);
    }
} // end bigIntSqRootCeil
} // end class bigIntSqRoot

【讨论】:

  • 好方法。但我有一个问题。在对 BigInteger 进行一些操作后,我得到了一个“1”。这应该完全没有问题,因为您使用 BigInteger.ONE 进行了检查,但这不是我的情况。该方法继续,我得到被零除的异常。该值是否可能实际上不是 1,但略有不同?在控制台上我得到一个“value = 1”,所以它似乎是正确的。
  • 我正在使用这些在我的项目中实现this
  • 这个近似值有自己的名字吗?谢谢。
  • 是的。我从 Abramowitz 和 Stegun 的数学函数手册(NBS 数学出版物)中得到它(并且已经使用了几十年)。我不记得手册上的名字。维基百科说它被称为巴比伦方法,或者,英雄的方法(亚历山大的英雄)。根据维基百科,它比牛顿的方法早了大约 16 个世纪,这使得它比泥土还要古老。它很可能早于火的发明。它是,或许曾经是最常用的手动计算平方根的方法,只需要算术和书写工具。
【解决方案3】:

奇怪的是之前没有人提到它,但是在 java 9 中你在 BigInteger 中有 sqrt,所以你可以像这样使用它:

BigInteger nine = BigInteger.valueOf(9);
BigInteger three = nine.sqrt();

https://docs.oracle.com/javase/9/docs/api/java/math/BigInteger.html#sqrt--


EDIT-1

此外,此函数还有另一种风格,除了 求平方根,还返回 余数

sqrtAndRemainder() BigInteger[]

Returns an array of two BigIntegers containing the integer square root s
of this and its remainder this - s*s, respectively.

【讨论】:

  • 我投了赞成票,因为这是一个不错的答案,但您仍然只能得到平方根的 BigInteger 表示(例如 eight.sqrt() == 2
【解决方案4】:

我无法验证它们的准确性,但在谷歌搜索时有几种自制的解决方案。其中最好的似乎是这个:http://www.merriampark.com/bigsqrt.htm

还可以尝试 Apache commons Math 项目(一旦 Apache 在 JCP 博客文章之后从轰炸中恢复过来)。

【讨论】:

  • 替换为 archive.org 的链接
【解决方案5】:

正如Jigar 所说,Newton's iteration 既易于理解又易于实现。我将由其他人决定它是否是求平方根最有效的算法。

使用递归,只需大约两行即可完成。

private static BigInteger newtonIteration(BigInteger n, BigInteger x0)
{
    final BigInteger x1 = n.divide(x0).add(x0).shiftRight(1);
    return x0.equals(x1)||x0.equals(x1.subtract(BigInteger.ONE)) ? x0 : newtonIteration(n, x1);
}

其中 n 是我们要求平方根的数字,x0 是上次调用的数字,第一次调用时始终为 1从另一个方法调用。所以最好你也用这样的东西来补充它;

public static BigInteger sqrt(final BigInteger number)
{
    if(number.signum() == -1)
        throw new ArithmeticException("We can only calculate the square root of positive numbers.");
    return newtonIteration(number, BigInteger.ONE);
}

【讨论】:

    【解决方案6】:

    对于初步猜测,我会使用Math.sqrt(bi.doubleValue()),您可以使用已经建议的链接来使答案更准确。

    【讨论】:

    • 太棒了。我真的很生气,我没有想到这一点。
    • 其实我只是想出了另一种计算初始猜测的方法:BigInteger.ZERO.setBit(bi.bitLength()/2)
    • 但是,如果您的 BigInteger 的值恰好在 double 的可能值范围内,这只是一种可行的方法,对吧?
    • @EdwardFalk 仅限于 16 位数字和大指数。
    • @EdwardFalk 您可以随时检查 Double.isFinite(x) 以查看 sqrt 之前的值是否足够小。
    【解决方案7】:

    我需要 BigIntegers 的平方根来实现二次筛。我在这里使用了一些解决方案,但迄今为止绝对最快和最好的解决方案来自 Google Guava 的 BigInteger 库。

    可以在here找到文档。

    【讨论】:

      【解决方案8】:

      另一种方法,它很轻。速度方面,曼托诺的答案使用牛顿法,在某些情况下可能更可取。

      这是我的方法...

      public static BigInteger sqrt(BigInteger n) {
          BigInteger a = BigInteger.ONE;
          BigInteger b = n.shiftRight(1).add(new BigInteger("2")); // (n >> 1) + 2 (ensure 0 doesn't show up)
          while (b.compareTo(a) >= 0) {
              BigInteger mid = a.add(b).shiftRight(1); // (a+b) >> 1
              if (mid.multiply(mid).compareTo(n) > 0)
                  b = mid.subtract(BigInteger.ONE);
              else
                  a = mid.add(BigInteger.ONE);
          }
          return a.subtract(BigInteger.ONE);
      }
      

      【讨论】:

      • 我测试了这种方法并且它有效,蒙塔诺的回答给了我大多数数字的错误结果。注意:这里的根是向下舍入的。
      【解决方案9】:

      这是我找到的最好(也是最短)的工作解决方案

      http://faruk.akgul.org/blog/javas-missing-algorithm-biginteger-sqrt/

      代码如下:

        public static BigInteger sqrt(BigInteger n) {
          BigInteger a = BigInteger.ONE;
          BigInteger b = new BigInteger(n.shiftRight(5).add(new BigInteger("8")).toString());
          while(b.compareTo(a) >= 0) {
            BigInteger mid = new BigInteger(a.add(b).shiftRight(1).toString());
            if(mid.multiply(mid).compareTo(n) > 0) b = mid.subtract(BigInteger.ONE);
            else a = mid.add(BigInteger.ONE);
          }
          return a.subtract(BigInteger.ONE);
        }
      

      我已经对其进行了测试,它工作正常(而且看起来很快)

      【讨论】:

      • 这很短,但它的效率是 O(nnlog(n)) 其中 n 是 BigInteger 的长度。这是二进制搜索和乘法,适用于 O(n*n)。
      • 他还来回转换为字符串。为什么?
      【解决方案10】:
          BigDecimal BDtwo = new BigDecimal("2");
          BigDecimal BDtol = new BigDecimal(".000000001");    
      private BigDecimal bigIntSQRT(BigDecimal lNew, BigDecimal lOld, BigDecimal n) {
              lNew = lOld.add(n.divide(lOld, 9, BigDecimal.ROUND_FLOOR)).divide(BDtwo, 9, BigDecimal.ROUND_FLOOR);
              if (lOld.subtract(lNew).abs().compareTo(BDtol) == 1) {
                  lNew = bigIntSQRT(lNew, lNew, n);
              }
          return lNew;
      }
      

      我刚刚解决了这个问题,并成功地用 Java 编写了一个递归平方根查找器。您可以将 BDtol 更改为您想要的任何内容,但这运行得相当快,结果给了我以下示例:

      原号146783911423364576743092537299333563769268393112173908757133540102089006265925538868650825438150202201473025

      SQRT --> 383123885216472214589586756787577295328224028242477055.000000000

      然后进行确认 146783911423364576743092537299333563769268393112173908757133540102089006265925538868650825438150202201473025.000000000p>00000em>

      【讨论】:

        【解决方案11】:

        简化了Jim answer 并提高了性能。

        public class BigIntSqRoot {
            private static BigInteger two = BigInteger.valueOf(2L);
        
            public static BigInteger bigIntSqRootFloor(BigInteger x)
                    throws IllegalArgumentException {
                if (checkTrivial(x)) {
                    return x;
                }
                if (x.bitLength() < 64) { // Can be cast to long
                    double sqrt = Math.sqrt(x.longValue());
                    return BigInteger.valueOf(Math.round(sqrt));
                }
                // starting with y = x / 2 avoids magnitude issues with x squared
                BigInteger y = x.divide(two);
                BigInteger value = x.divide(y);
                while (y.compareTo(value) > 0) {
                    y = value.add(y).divide(two);
                    value = x.divide(y);
                }
                return y;
            }
        
            public static BigInteger bigIntSqRootCeil(BigInteger x)
                    throws IllegalArgumentException {
                BigInteger y = bigIntSqRootFloor(x);
                if (x.compareTo(y.multiply(y)) == 0) {
                    return y;
                }
                return y.add(BigInteger.ONE);
            }
        
            private static boolean checkTrivial(BigInteger x) {
                if (x == null) {
                    throw new NullPointerException("x can't be null");
                }
                if (x.compareTo(BigInteger.ZERO) < 0) {
                    throw new IllegalArgumentException("Negative argument.");
                }
        
                // square roots of 0 and 1 are trivial and
                // y == 0 will cause a divide-by-zero exception
                if (x.equals(BigInteger.ZERO) || x.equals(BigInteger.ONE)) {
                    return true;
                } // end if
                return false;
            }
        }
        

        【讨论】:

          【解决方案12】:

          更新(2018 年 7 月 23 日):此技术不适用于较大的值。在下面发布了一种基于二分搜索的不同技术。


          我正在研究因式分解并最终写了这个。

          package com.example.so.math;
          
          import java.math.BigInteger;
          
          /**
           * 
           * <p>https://stackoverflow.com/questions/4407839/how-can-i-find-the-square-root-of-a-java-biginteger</p>
           * @author Ravindra
           * @since 06August2017
           *
           */
          public class BigIntegerSquareRoot {
          
              public static void main(String[] args) {
          
                  int[] values = {5,11,25,31,36,42,49,64,100,121};
          
                  for (int i : values) {
                      BigInteger result = handleSquareRoot(BigInteger.valueOf(i));
                      System.out.println(i+":"+result);
                  }
          
          
              }
          
          
              private static BigInteger handleSquareRoot(BigInteger modulus) {
          
                  int MAX_LOOP_COUNT = 100; // arbitrary for now.. but needs to be proportional to sqrt(modulus)
          
                  BigInteger result = null;
          
                  if( modulus.equals(BigInteger.ONE) ) {
                      result = BigInteger.ONE;
                      return result;
                  }
          
                  for(int i=2;i<MAX_LOOP_COUNT && i<modulus.intValue();i++) { // base-values can be list of primes...
                      //System.out.println("i"+i);
                      BigInteger bigIntegerBaseTemp = BigInteger.valueOf(i);
                      BigInteger bigIntegerRemainderTemp = bigIntegerBaseTemp.modPow(modulus, modulus);
                      BigInteger bigIntegerRemainderSubtractedByBase = bigIntegerRemainderTemp.subtract(bigIntegerBaseTemp);
                      BigInteger bigIntegerRemainderSubtractedByBaseFinal = bigIntegerRemainderSubtractedByBase;
          
                      BigInteger resultTemp = null;
                      if(bigIntegerRemainderSubtractedByBase.signum() == -1 || bigIntegerRemainderSubtractedByBase.signum() == 1) {
          
                          bigIntegerRemainderSubtractedByBaseFinal = bigIntegerRemainderSubtractedByBase.add(modulus);
                          resultTemp = bigIntegerRemainderSubtractedByBaseFinal.gcd(modulus);
          
                      } else if(bigIntegerRemainderSubtractedByBase.signum() == 0) {
                          resultTemp = bigIntegerBaseTemp.gcd(modulus);
                      }
          
                      if( resultTemp.multiply(resultTemp).equals(modulus) ) {
                          System.out.println("Found square root for modulus :"+modulus);
                          result = resultTemp;
                          break;
                      }
                  }
          
                  return result;
              }
          
          
          }
          

          该方法可以像这样可视化:

          希望这会有所帮助!

          【讨论】:

          • 你能解释一下彩色表吗?
          • 这被称为“整数模数的幂”表。在这种情况下,'n' 是 25。您可以在 PPT pertaining to a book called Cryptography and Network Security - Author William Stallings 中找到更多信息。如需更多信息,请参阅本书本身!
          • PS:为了解释表格本身,基本上如果观察到与'n'对应的列,在这种情况下'25',如果你减去它的底数,那么 - 的gcd(减去剩余部分由基数)并且'n'本身是非零的。我正在使用该属性来确定数字是否为正方形,如果是,则通过 gcd 确定该数字是多少。它只是我随着时间的推移发现的东西......但如果你在任何论文中遇到它,请发布链接!
          • 更新:此技术不适用于较大的值。在下面发布了一种基于二分搜索的不同技术。
          【解决方案13】:

          我只研究平方根的整数部分,但你可以修改这个粗略的算法以达到你想要的更高的精度:

            public static void main(String args[]) {
              BigInteger N = new BigInteger(
                      "17976931348623159077293051907890247336179769789423065727343008115"
                              + "77326758055056206869853794492129829595855013875371640157101398586"
                              + "47833778606925583497541085196591615128057575940752635007475935288"
                              + "71082364994994077189561705436114947486504671101510156394068052754"
                              + "0071584560878577663743040086340742855278549092581");
              System.out.println(N.toString(10).length());
              String sqrt = "";
              BigInteger divisor = BigInteger.ZERO;
              BigInteger toDivide = BigInteger.ZERO;
              String Nstr = N.toString(10);
              if (Nstr.length() % 2 == 1)
                  Nstr = "0" + Nstr;
              for (int digitCount = 0; digitCount < Nstr.length(); digitCount += 2) {
                  toDivide = toDivide.multiply(BigInteger.TEN).multiply(
                          BigInteger.TEN);
                  toDivide = toDivide.add(new BigInteger(Nstr.substring(digitCount,
                          digitCount + 2)));
                  String div = divisor.toString(10);
                  divisor = divisor.add(new BigInteger(
                          div.substring(div.length() - 1)));
                  int into = tryMax(divisor, toDivide);
                  divisor = divisor.multiply(BigInteger.TEN).add(
                          BigInteger.valueOf(into));
                  toDivide = toDivide.subtract(divisor.multiply(BigInteger
                          .valueOf(into)));
                  sqrt = sqrt + into;
              }
              System.out.println(String.format("Sqrt(%s) = %s", N, sqrt));
          }
          
          private static int tryMax(final BigInteger divisor,
                  final BigInteger toDivide) {
              for (int i = 9; i > 0; i--) {
                  BigInteger div = divisor.multiply(BigInteger.TEN).add(
                          BigInteger.valueOf(i));
                  if (div.multiply(BigInteger.valueOf(i)).compareTo(toDivide) <= 0)
                      return i;
              }
              return 0;
          }
          

          【讨论】:

          • 由于我只显示整数部分,它平方根的底。
          【解决方案14】:

          你也可以使用二分搜索来找到 x 的平方根 您也可以将它乘以例如 10^10 并通过二进制搜索找到像 m 这样的整数,因为 m^2

          System.out.println(m.divide(10^5)+"."+m.mod(10^5));

          【讨论】:

            【解决方案15】:

            这是一个不使用 BigInteger.multiply 或 BigInteger.divide 的解决方案:

                private static final BigInteger ZERO  = BigInteger.ZERO;
                private static final BigInteger ONE   = BigInteger.ONE;
                private static final BigInteger TWO   = BigInteger.valueOf(2);
                private static final BigInteger THREE = BigInteger.valueOf(3);
            
                /**
                 * This method computes sqrt(n) in O(n.bitLength()) time,
                 * and computes it exactly. By "exactly", I mean it returns
                 * not only the (floor of the) square root s, but also the
                 * remainder r, such that r >= 0, n = s^2 + r, and
                 * n < (s + 1)^2.
                 *
                 * @param n The argument n, as described above.
                 *
                 * @return An array of two values, where the first element
                 *         of the array is s and the second is r, as
                 *         described above.
                 *
                 * @throws IllegalArgumentException if n is not nonnegative.
                 */
                public static BigInteger[] sqrt(BigInteger n) {
                    if (n == null || n.signum() < 0) {
                        throw new IllegalArgumentException();
                    }
            
                    int bl = n.bitLength();
                    if ((bl & 1) != 0) {
                        ++ bl;
                    }
            
                    BigInteger s = ZERO;
                    BigInteger r = ZERO;
            
                    while (bl >= 2) {
                        s = s.shiftLeft(1);
            
                        BigInteger crumb = n.testBit(-- bl)
                                            ? (n.testBit(-- bl) ? THREE : TWO)
                                            : (n.testBit(-- bl) ? ONE : ZERO);
                        r = r.shiftLeft(2).add(crumb);
            
                        BigInteger d = s.shiftLeft(1);
                        if (d.compareTo(r) < 0) {
                            s = s.add(ONE);
                            r = r.subtract(d).subtract(ONE);
                        }
                    }
            
                    assert r.signum() >= 0;
                    assert n.equals(s.multiply(s).add(r));
                    assert n.compareTo(s.add(ONE).multiply(s.add(ONE))) < 0;
            
                    return new BigInteger[] {s, r};
                }
            

            【讨论】:

              【解决方案16】:

              我在上面发布的答案不适用于大量数字(但有趣的是!)。因此发布了一种二进制搜索方法来确定平方根的正确性。

              package com.example.so.squareroot;
              
              import java.math.BigInteger;
              import java.util.ArrayList;
              import java.util.List;
              
              /**
               * <p>https://stackoverflow.com/questions/4407839/how-can-i-find-the-square-root-of-a-java-biginteger</p>
               * <p> Determine square-root of a number or its closest whole number (binary-search-approach) </p>
               * @author Ravindra
               * @since 07-July-2018
               * 
               */
              public class BigIntegerSquareRootV2 {
              
                  public static void main(String[] args) {
              
                      List<BigInteger> listOfSquares = new ArrayList<BigInteger>();
                      listOfSquares.add(BigInteger.valueOf(5).multiply(BigInteger.valueOf(5)).pow(2));
                      listOfSquares.add(BigInteger.valueOf(11).multiply(BigInteger.valueOf(11)).pow(2));
                      listOfSquares.add(BigInteger.valueOf(15485863).multiply(BigInteger.valueOf(10000019)).pow(2));
                      listOfSquares.add(BigInteger.valueOf(533000401).multiply(BigInteger.valueOf(982451653)).pow(2));
                      listOfSquares.add(BigInteger.valueOf(11).multiply(BigInteger.valueOf(23)));
                      listOfSquares.add(BigInteger.valueOf(11).multiply(BigInteger.valueOf(23)).pow(2));
              
              
                      for (BigInteger bigIntegerNumber : listOfSquares) {
              
                          BigInteger squareRoot = calculateSquareRoot(bigIntegerNumber);
              
                          System.out.println("Result :"+bigIntegerNumber+":"+squareRoot);
                      }
              
              
                      System.out.println("*********************************************************************");
              
                      for (BigInteger bigIntegerNumber : listOfSquares) {
              
                          BigInteger squareRoot = determineClosestWholeNumberSquareRoot(bigIntegerNumber);
              
                          System.out.println("Result :"+bigIntegerNumber+":"+squareRoot);
                      }
              
                  }
              
              
                  /*
              Result :625:25
              Result :14641:121
              Result :23981286414105556927200571609:154858924231397
              Result :274206311533451346298141971207799609:523647125012112853
              Result :253:null
              Result :64009:253
                   */
              
                  public static BigInteger calculateSquareRoot(BigInteger number) { 
              
                      /*
                       * Can be optimized by passing a bean to store the comparison result and avoid having to re-calculate.
                       */
                      BigInteger squareRootResult = determineClosestWholeNumberSquareRoot(number);
                      if( squareRootResult.pow(2).equals(number)) {
                          return squareRootResult;
                      }
              
                      return null;
                  }
              
              
                  /*
              Result :625:25
              Result :14641:121
              Result :23981286414105556927200571609:154858924231397
              Result :274206311533451346298141971207799609:523647125012112853
              Result :253:15
              Result :64009:253
                   */
                  private static BigInteger determineClosestWholeNumberSquareRoot(BigInteger number) {
              
                      BigInteger result = null;
              
                      if(number.equals(BigInteger.ONE)) {
                          return BigInteger.ONE;
                      } else if( number.equals(BigInteger.valueOf(2)) ) {
                          return BigInteger.ONE;
                      } else if( number.equals(BigInteger.valueOf(3)) ) {
                          return BigInteger.ONE;
                      } else if( number.equals(BigInteger.valueOf(4)) ) {
                          return BigInteger.valueOf(2);
                      }
              
                      BigInteger tempBaseLow = BigInteger.valueOf(2);
                      BigInteger tempBaseHigh = number.shiftRight(1); // divide by 2
              
                      int loopCount = 11;
              
                      while(true) {
              
                          if( tempBaseHigh.subtract(tempBaseLow).compareTo(BigInteger.valueOf(loopCount)) == -1 ) { // for lower numbers use for-loop
                              //System.out.println("Breaking out of while-loop.."); // uncomment-for-debugging
                              break;
                          }
              
                          BigInteger tempBaseMid = tempBaseHigh.subtract(tempBaseLow).shiftRight(1).add(tempBaseLow); // effectively mid = [(high-low)/2]+low
                          BigInteger tempBaseMidSquared = tempBaseMid.pow(2);
                          int comparisonResultTemp = tempBaseMidSquared.compareTo(number);
              
              
                          if(comparisonResultTemp == -1) { // move mid towards higher number
                              tempBaseLow = tempBaseMid;
                          } else if( comparisonResultTemp == 0 ) { // number is a square ! return the same !
                                  return tempBaseMid;
                          } else { // move mid towards lower number
                              tempBaseHigh = tempBaseMid;
                          }
              
                      }
              
                      BigInteger tempBasePrevious = tempBaseLow;
                      BigInteger tempBaseCurrent = tempBaseLow;
                      for(int i=0;i<(loopCount+1);i++) {
                          BigInteger tempBaseSquared = tempBaseCurrent.pow(2);
                          //System.out.println("Squared :"+tempBaseSquared); // uncomment-for-debugging
                          int comparisonResultTempTwo = tempBaseSquared.compareTo(number);
              
                          if( comparisonResultTempTwo == -1 ) { // move current to previous and increment current...
                              tempBasePrevious = tempBaseCurrent;
                              tempBaseCurrent = tempBaseCurrent.add(BigInteger.ONE);
                          } else if( comparisonResultTempTwo == 0 ) { // is an exact match!
                              tempBasePrevious = tempBaseCurrent;
                              break;
                          } else { // we've identified the point of deviation.. break..
                              //System.out.println("breaking out of for-loop for square root..."); // uncomment-for-debugging
                              break;
                          }
                      }
              
                      result = tempBasePrevious;
              
                      //System.out.println("Returning :"+result); // uncomment-for-debugging
                      return result;
              
                  }
              
              
              }
              

              问候 拉文德拉

              【讨论】:

                【解决方案17】:

                这是一种易于理解的方法,它可能没有最佳性能,但它可以在不到一秒的时间内为单个 BigInteger 提供解决方案。

                public static BigInteger sqrt(BigInteger n) {
                    BigInteger bottom = BigInteger.ONE;
                    BigInteger top = n;
                    BigInteger mid;
                    while(true) {
                        mid = top.add(bottom).divide(new BigInteger(""+2));
                        top = mid;
                        bottom = n.divide(top);
                //            System.out.println("top:    "+top);
                //            System.out.println("mid:    "+mid);
                //            System.out.println("bottom: "+bottom);
                        if(top.equals(bottom)) {
                            return top;
                        }
                    }
                }
                

                【讨论】:

                  【解决方案18】:

                  C# 语言的语法与 Java 相似。我写了这个递归解决方案。

                      static BigInteger fsqrt(BigInteger n)
                      {
                          string sn = n.ToString();
                          return guess(n, BigInteger.Parse(sn.Substring(0, sn.Length >> 1)), 0);          
                      }
                      static BigInteger guess(BigInteger n, BigInteger g, BigInteger last)
                      {
                          if (last >= g - 1 && last <= g + 1) return g;
                          else return guess(n, (g + (n / g)) >> 1, g);
                      }
                  

                  像这样调用这段代码(在 Java 中我猜它应该是“System.out.print”)。

                  Console.WriteLine(fsqrt(BigInteger.Parse("783648276815623658365871365876257862874628734627835648726")));
                  

                  答案是: 27993718524262253829858552106

                  免责声明:我了解此方法不适用于小于 10 的数字;这是一个 BigInteger 平方根方法。

                  这很容易解决。将第一种方法更改为以下方法,为递归部分提供一些喘息的空间。

                      static BigInteger fsqrt(BigInteger n)
                      {
                          if (n > 999)
                          {
                             string sn = n.ToString();
                             return guess(n, BigInteger.Parse(sn.Substring(0, sn.Length >> 1)), 0);
                          }
                          else return guess(n, n >> 1, 0);            
                      }
                  

                  【讨论】:

                    【解决方案19】:

                    单行就可以完成我认为的工作。

                    Math.pow(bigInt.doubleValue(), (1/n));
                    

                    【讨论】:

                    • 对于大于 double 可以表示的数字(大约 1.7e308)将失败,并且只会返回与 double 一样多的精度。
                    猜你喜欢
                    • 2014-03-03
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多