大数的动态幂和模运算

发布于 2024-09-01 18:28:38 字数 1494 浏览 2 评论 0原文

我将某个基数 b 进行 p 次幂,并对其取 m 模。

假设 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 中的错误吗?

I raise some basis b to the power p and take the modulo m of that.

Let's assume b=55170 or 55172 and m=3043839241 (which happens to be the square of 55171). The linux-calculator bc gives the results (we need this for control):

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

Now calculating 55170^5606 gives a somewhat large number, but since I have to do a modulooperation, I can circumvent the usage of BigInt, I thought, because of:

(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

... and a^d = a^(b+c) = a^b * a^c, therefore I can divide b+c by 2, which gives, for even or odd ds d/2 and d-(d/2), so for 8^5 I can calculate 8^2 * 8^3.

So my (defective) method, which always cut's off the divisor on the fly looks like that:

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 
      } 
}

and feeded with some values,

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

As we can see, the second result is exactly the same as the one above, but the first one looks quiet different. I'm doing a lot of such calculations, and they seem to be accurate as long as they stay in the range of Int, but I can't see any error. Using a BigInt works as well, but is way too slow:

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

(same result as with bc) Can somebody see the error in powMod?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

笑,眼淚并存 2024-09-08 18:28:38

我认为答案就在这里:

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

你就得到了它。

I think the answer is here:

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

That means you can have a long overflow even for numbers which are less than that particular module value. Let's try to catch it:

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

There you have it.

迷雾森÷林ヴ 2024-09-08 18:28:38

不熟悉 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 在做什么。

Not familiar with Scala, but...

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  
      }  
} 

Did you mean

          val pm1 = powMod (b, pot2, mod) 

Notice the pot2 instead of pot.

Strangely, it seems that this should loop forever/overflow the stack, but who knows what Scala is doing.

街角卖回忆 2024-09-08 18:28:38

好吧,伙计们,我花了一些时间,最终摧毁了一个很长但从未被证实的假设,即如果将两个 64 位正整数值(又名:长整型,毕竟实际上只有 63 位)相乘,您可能会超出范围并获得负值,但不会再次达到正值(但错误)。

所以我尝试在我的代码中添加一个守卫,用 BigInt 计算我的值,它太大了,但是守卫不够,因为我测试了 res res < 0 。 <代码>res < pm1 &&分辨率< 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 是否真的需要,或者 a 是否

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

也可以工作。不,不会的。 :) 另一个需要避免的陷阱。 :)

最后,它非常快且正确,我学到了关于溢出和 MAX_VALUE 的两个教训 - 比较。

Ok fellows, it took me some time, and finally destroyed a long but never proven assumption, which was, that if you multiply two 64-bit-positive integral values (aka: Longs, and practically only 63-bit, after all), you could overrun, and get negative values, but not get an overrun to reach positive (but wrong) values again.

So I had tried to put a guard into my code, to calculate my value with BigInt, it too big, but the guard was insufficient, because I tested for res < 0. res < pm1 && res < pm2 isn't sufficient too.

To increase the speed I used a mutable.HashMap, and now the code looks like this:

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

You might ask yourself, whether the Long-declared MVL is really needed, or whether a

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

would have worked too. No. It wouldn't. :) Another trap to avoid. :)

Finally it is pretty fast and correct and I learned two lessons about overruns and MAX_VALUE - comparision.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文