power and modulo on the fly for big numbers

后端 未结 3 513
春和景丽
春和景丽 2021-01-13 07:26

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 li

相关标签:
3条回答
  • 2021-01-13 08:00

    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.

    0 讨论(0)
  • 2021-01-13 08:02

    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.

    0 讨论(0)
  • 2021-01-13 08:03

    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.

    0 讨论(0)
提交回复
热议问题