大数字的动力和模数

时间:2010-05-17 06:35:33

标签: scala modulo

我向幂p提出一些基础b并取其模数。

假设b = 55170或55172且m = 3043839241(恰好是55171的平方)。 linux-calculator bc给出结果(我们需要这个用于控制):

echo "p=5606;b=55171;m=b*b;((b-1)^p)%m;((b+1)^p)%m" | bc
2734550616
309288627

现在计算55170 ^ 5606给出了一个相当大的数字,但由于我必须做模数操作,我可以绕过BigInt的使用,我想,因为:

(a*b) % c == ((a%c) * (b%c))%c i.e.
(9*7) % 5 == ((9%5) * (7%5))%5 =>
63 % 5    == (4     *    2) %5 =>
3         == 8 % 5

...和a ^ d = a ^(b + c)= a ^ b * a ^ c,因此我可以将b + c除以2,这得到偶数或奇数ds d / 2和d - (d / 2),所以对于8 ^ 5我可以计算8 ^ 2 * 8 ^ 3。

所以我的(有缺陷的)方法总是在飞行中削减除数看起来像这样:

def powMod (b: Long, pot: Int, mod: Long) : Long = { 
      if (pot == 1) b % mod else {
          val pot2 = pot/2
          val pm1 = powMod (b, pot2, mod)             
          val pm2 = powMod (b, pot-pot2, mod)           
          (pm1 * pm2) % mod 
      } 
}

并且满足一些价值观,

powMod (55170, 5606, 3043839241L) 
res2: Long = 1885539617
powMod (55172, 5606, 3043839241L) 
res4: Long = 309288627

正如我们所看到的,第二个结果与上面的结果完全相同,但第一个结果看起来很安静。我做了很多这样的计算,只要它们保持在Int的范围内它们似乎是准确的,但我看不出任何错误。使用BigInt也可以,但速度太慢了:

def calc2 (n: Int, pri: Long) = {
    val p: BigInt = pri
    val p3 = p * p
    val p1 = (p-1).pow (n) % (p3)
    val p2 = (p+1).pow (n) % (p3)
    print ("p1: " + p1 + " p2: " + p2)
}

calc2 (5606, 55171) 
p1: 2734550616 p2: 309288627

(与bc相同的结果)有人可以在powMod中看到错误吗?

3 个答案:

答案 0 :(得分:3)

我认为答案就在这里:

scala> math.sqrt(Long.MaxValue).toLong < 3043839241L
res9: Boolean = true

这意味着即使对于小于特定模块值的数字,您也可能有很长的溢出。让我们试着抓住它:

scala> def powMod (b: Long, pot: Int, mod: Long) : Long = {
     |       if (pot == 1) b % mod else {
     |           val pot2 = pot/2
     |           val pm1 = powMod (b, pot2, mod)
     |           val pm2 = powMod (b, pot-pot2, mod)
     |           val partial = ((pm1 % mod) * (pm2 % mod)).ensuring(res =>
     |             res > pm1 % mod && res > pm2 % mod, "Long overflow multiplying "+pm1+" by "+pm2)
     |           partial % mod
     |       }
     | }
powMod: (b: Long,pot: Int,mod: Long)Long

scala> powMod (55170, 5606, 3043839241L)
java.lang.AssertionError: assertion failed: Long overflow multiplying 3042625480 by 3042625480

你有它。

答案 1 :(得分:2)

不熟悉Scala,但是......

def powMod (b: Long, pot: Int, mod: Long) : Long = {  
      if (pot == 1) b % mod else { 
          val pot2 = pot/2 
          val pm1 = powMod (b, pot, mod)              
          val pm2 = powMod (b, pot-pot2, mod)            
          (pm1 * pm2) % mod  
      }  
} 

你的意思是

          val pm1 = powMod (b, pot2, mod) 

注意pot2而不是pot。

奇怪的是,似乎这应该永远循环/溢出堆栈,但是谁知道Scala正在做什么。

答案 2 :(得分:1)

好的研究员,我花了一些时间,最后摧毁了一个长期但未经证实的假设,即如果你乘以两个64位正整数值(又名:Longs,实际上只有63位,之后)所有),你可以超支,并获得负值,但不会超支再次达到积极(但错误)的价值。

所以我试图把守卫放到我的代码中,用BigInt计算我的价值,它太大了,但是后卫不够,因为我测试了res < 0res < pm1 && res < pm2也不够。

为了提高速度,我使用了mutable.HashMap,现在代码如下:

val MVL : Long = Integer.MAX_VALUE
var modPow = new scala.collection.mutable.HashMap [(Long, Int, Long), Long ] () 

def powMod (b: Long, pot: Int, mod: Long) : Long = { 
      if (pot == 1) b % mod else modPow.getOrElseUpdate ((b, pot, mod), {
    val pot2= pot/2
    val pm1 = powMod (b, pot2, mod)             
    val pm2 = powMod (b, pot-pot2, mod)
    val res = (pm1 * pm2) 
    // avoid Long-overrun
    if (pm1 < MVL && pm2 < MVL)
        res % mod else {
            val f1: BigInt = pm1
            val f2: BigInt = pm2
            val erg = (f1 * f2) % mod
            erg.longValue 
        }
      })
}

您可能会问自己,是否确实需要长时间声明的MVL,或者是否

if (pm1 < Integer.MAX_VALUE && ...

也会奏效。不,不会。 :)避免另一个陷阱。 :)

最后它非常快速和正确,我学到了两个关于超支和MAX_VALUE的课程 - 比较。