-1

I was under the impression that python Integers are arbitrary precision. But today while solving a leetCode problem, and multiple submissions with Wrong Answer, I finally added mod to my solution and it worked. I am confused as to why it happened, following is my code:

import math

class Solution:
    def numRollsToTarget(self, d: int, f: int, target: int) -> int:
        if target>d*f or target<d:
            return 0
        memo = {}
        return int(self.recurse(d,f,target,memo)%(math.pow(10,9) + 7))

    # def recurse(self,d,f,target,memo):

    def recurse(self,d,f,target,memo):
        key = "%d %d"%(d,target)
        if key in memo:
            return memo[key]
        if d*f<target or target<d:
            return 0
        elif d==1:
            return 1
        else:
            ways = 0
            for i in range(1,f+1):
                # following line uncommented, and the next one commented, gives correct answer
                #ways = (ways+self.recurse(d-1,f,target-i,memo))%(math.pow(10,9) + 7)
                ways += self.recurse(d-1,f,target-i,memo)

            memo[key] = ways
            return memo[key]

My guess is that overflow is causing the wrong result, modding the partial result everytime while summing solves the problem. Or is there something else I am missing?

Wajahat
  • 1,593
  • 3
  • 20
  • 47
  • Do you mind elaborating the question a bit more ? – Kris Sep 24 '19 at 04:56
  • What makes you think there is an integer overflow here? – Stop harming Monica Sep 24 '19 at 05:21
  • @Goyo, that is my guess, what else could be causing this? Just adding the mod solves the issue. – Wajahat Sep 24 '19 at 20:33
  • No, you were right that python integers are arbitrary precision. But you are calculating with floats, which are not and cannot accurately represent large integers. That may be what causes the wrong result and modding it works by [coincidence](https://pragprog.com/the-pragmatic-programmer/extracts/coincidence). Or maybe you just got it wrong and modding is actually the right way of solving the problem. Either way if you want to work with integers use the `**` operator instead of `math.pow()`. – Stop harming Monica Sep 24 '19 at 21:16
  • But I haven't used float for calculation. The version of code I have posted here, only does math.pow() in the end, and this is the version which gives wrong answer. The version with math.pow() (the commented line) actually gives the correct answer (but not because of float, but because of mod). – Wajahat Sep 25 '19 at 01:06

1 Answers1

1

You are calculating with floats:

return int(self.recurse(d,f,target,memo)%(math.pow(10,9) + 7))

This forces the modulo to be computed using floats, it is not integer overflow but float inaccuracy what led you to a wrong result.

Let's make d, f, target = 30, 30, 500. Then int(self.recurse(d, f, target, memo) returns 1319186227454333254490768998141738589030871, an int while math.pow(10, 9) + 7 is 1000000007.0, a float.

So this is what you are doing:

print(1319186227454333254490768998141738589030871 % 1000000007.0)  # wrong
811448245.0

If you instead make sure that the dividend is also an integer (10 ** 9 + 7 or 1000000007):

print(1319186227454333254490768998141738589030871 % 1000000007)  # right   
222616187
Stop harming Monica
  • 12,141
  • 1
  • 36
  • 56
  • Interesting, yeah that was the issue. I had started with `10**9+7` but had replaced it with `math.pow()` while fixing another bug in the code, bad mistake. – Wajahat Sep 26 '19 at 18:46
  • Indeed. I personally regard modulo and floor division as undefined behavior when floats are involved. – Stop harming Monica Sep 26 '19 at 18:53