SCALA:BigInt平方根的函数

时间:2018-07-06 15:05:34

标签: scala square-root

我在Internet上搜索了一种使用scala编程语言查找BigInt确切平方根的函数。我没有一个,但是看到一个Java程序,然后将该函数转换为Scala版本。它正在工作,但是我不确定它是否可以处理很大的BigInt。但是它仅返回BigInt。不是BigDecimal作为平方根。它显示了在代码中完成了一些操作,例如对数字shiftRight(5), BigInt("8") and shiftRight(1)进行了硬编码。我可以清楚地理解逻辑,但是不能对这些移位数字和数字8进行硬编码。也许这些移位函数在scala中不可用,这就是为什么在几个地方都需要将其转换为Java BigInteger的原因。这些硬编码的数字可能会影响结果的精度。我只是将Java代码更改为scala代码,只是复制了精确的算法。这是我在scala中编写的代码:

   def sqt(n:BigInt):BigInt = {
      var a = BigInt(1)
      var b = (n>>5)+BigInt(8)
      while((b-a) >= 0) {
          var mid:BigInt = (a+b)>>1
          if(mid*mid-n> 0) b = mid-1
          else a = mid+1
         }
      a-1
   }

我的积分是:

  1. 我们不能返回BigDecimal而不是BigInt吗?我们该怎么做?
  2. 这些硬编码数字shiftRight(5), shiftRight(1) and 8是如何关联的 精确到结果。

我在scala REPL中测试了一个数字:函数sqt给出平方数的确切平方根。而不是实际数字,如下所示:

scala> sqt(BigInt("19928937494873929279191794189"))
res9: BigInt = 141169888768369

scala> res9*res9
res10: scala.math.BigInt = 19928937494873675935734920161

scala> sqt(res10)
res11: BigInt = 141169888768369

scala>

我了解shiftRight(5) means divide by 2^5 ie.by 32 in decimal等等。但是为什么在移位操作后在此处添加8?为什么恰好是5个班次?作为第一个猜测?

2 个答案:

答案 0 :(得分:3)

您的问题1和问题3实际上是同一个问题。

  
      
  1. 这些移位如何影响结果的精度?
  2.   

他们没有。

  
      
  1. 这些硬编码数字与结果的精确度有何关系?
  2.   

不是。

有许多不同的方法/算法可用于估算/计算数字的平方根(如here所示)。您发布的算法似乎是非常简单的二进制搜索。

  1. 选择一个数字a,保证它小于目标数(n的平方根)。
  2. 选择一个数字b,保证其大于目标值(n的平方根)。
  3. 计算mid,即ab之间的整数中点。
  4. 如果mid大于(或等于)目标,则将b移至mid(-1,因为我们知道它太大了)。
  5. 如果mid小于目标,则将a移至mid(+ 1,因为我们知道它太小了。)
  6. 重复3、4、5,直到a不小于b
  7. a-1的平方根取整后返回n

位移和硬编码数字用于选择b的初始值。但是b仅大于目标。我们本可以完成var b = n。为什么要打扰?

一切都与效率有关。 b离目标越近,找到结果所需的迭代次数就越少。为什么在班次后加8?因为31 >> 5为零,因此不大于目标值。作者选择了(n>>5)+8,但是他/她可能选择了(n>>7)+12。需要权衡。

  
      
  1. 我们不能返回BigDecimal而不是BigInt吗?我们该怎么做?
  2.   

这是做到这一点的一种方法。

def sqt(n:BigInt) :BigDecimal = {
  val d = BigDecimal(n)
  var a = BigDecimal(1.0)
  var b = d
  while(b-a >= 0) {
    val mid = (a+b)/2
    if (mid*mid-d > 0) b = mid-0.0001  //adjust down
    else               a = mid+0.0001  //adjust up
  }
  b
}

有更好的算法来计算浮点平方根值。在这种情况下,使用较小的调整值可以提高精度,但效率会大大降低。

答案 1 :(得分:0)

  

我们不能返回BigDecimal而不是BigInt吗?我们该怎么做?

如果您想要精确的根,则没有任何意义:如果BigInt的平方根可以由BigDecimal精确表示,则可以由BigInt表示。如果您不希望确切的根,则需要指定精度并修改算法(在大多数情况下,Double会足够好,并且比BigDecimal快得多)。

  

我理解shiftRight(5)表示除以2 ^ 5,即以十进制除以32,依此类推..但是为什么在进行移位运算后在此处加上8?为什么恰好是5个班次?作为第一个猜测?

这些不是唯一的选择。关键是对于每个正数nn/32 + 8 >= sqrt(n)(其中sqrt是数学平方根)。通过一点演算(或仅通过建立差异图)就可以最容易地显示出这一点。因此,一开始我们就知道a <= sqrt(n) <= b(除非n == 0可以单独检查),并且您可以在每一步中验证其是否成立。