3

I solved the following leetCode problem with some code :


You have d dice, and each die has f faces numbered 1, 2, ..., f.

Return the number of possible ways modulo 10^9 + 7 to roll the dice so the sum of the face up numbers equals t.


I made two versions of the solution code, one in node.js using mathjs, and one in python using the math module .

In node.js

const { combinations: comb, bignumber: Big } = require("mathjs");

function dice(d, f, t) {
    if (t > d * f || t < d) return 0;

    var result = Big(0);
    var i = 0;
    var sign = 1;
    var n = t - 1;
    var k = t - d;

    while (k >= 0 && i <= d) {
        result = result.add(
            comb(Big(d), Big(i))
                .times(comb(Big(n), Big(k)))
                .times(sign)
        );

        i++;
        n -= f;
        k -= f;
        sign *= -1;
    }

    return result;
}

console.log(
    dice(30, 30, 500).mod(
        Big(10)
            .pow(9)
            .add(7)
    )
);

In python :

import math


def dice(d, f, t):
    if t > d * f or t < d:
        return 0

    result = 0
    i = 0
    sign = 1
    n = t - 1
    k = t - d

    while k >= 0 and i <= d:
        result += math.comb(d, i) * math.comb(n, k) * sign
        i += 1
        n -= f
        k -= f
        sign *= -1

    return result


print(dice(30, 30, 500) % (math.pow(10, 9) + 7))


Now when i run the code with these parameters : d=30 f=30 t=500 (the last line of each version of the code), i expect the result to be 222616187 .

In the node.js version , that's exactly what i get .

But in the python version , i'm getting 811448245.0 i can't figure out why is that happening .

So why is there a difference in the results ?

soufiane yakoubi
  • 861
  • 11
  • 31
  • 2
    Note that the result of ``dice(30, 30, 500)`` cannot be accurately represented by a 64 bit float. It is off by ``-14999044413600247749080617``. – MisterMiyagi Feb 05 '20 at 14:00

2 Answers2

5

The math module usesfloat, not arbitrary precision int.

math - Mathematical functions

[...]

The following functions are provided by this module. Except when explicitly noted otherwise, all return values are floats.

Since math.pow returns a float, the leading argument to % is converted to a float as well. The result of dice(30, 30, 500) is too large to be accurately represented as a float. Its float representation is off by -14999044413600247749080617.

The ** operator and its function version operator.pow do not force float conversion and provide an integer if all parameters are integers.

>>> print(dice(30, 30, 500) % (10 ** 9 + 7))
222616187
MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
2

Solved, in a weird way. It turns out, math.pow returns a float instead of int and somehow bugged. I think int % float has a different cast operation in it and treated differently by the compiler. It can be investigated further. If you cast it to int, that would be your answer.

import math


def dice(d, f, t):
    if t > d * f or t < d:
        return 0

    result = 0
    i = 0
    sign = 1
    n = t - 1
    k = t - d

    while k >= 0 and i <= d:
        result += math.comb(d, i) * math.comb(n, k) * sign
        i += 1
        n -= f
        k -= f
        sign *= -1

    return result


print(dice(30, 30, 500) % int((math.pow(10, 9) + 7)))
MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
Alper
  • 487
  • 4
  • 17
  • after i saw your answer i got curious and i tried to replace `math.pow(10 , 9)+7` with `10**9 +7` and without casting to an int i got the correct result , how is `**` different than `math.pow` ? – soufiane yakoubi Feb 05 '20 at 13:53
  • 1
    Math.pow (which is in c) [function](https://hg.python.org/cpython/file/c7163a7f7cd2/Modules/mathmodule.c#l1781) takes the parameters and convert them into a double(double in c, float in python). Default pow does not do that. This cast operation makes everything different in the background maybe even in the compiler. [Differences](https://stackoverflow.com/questions/10282674/difference-between-the-built-in-pow-and-math-pow-for-floats-in-python) of default and math.pow here. You can check this out also. – Alper Feb 05 '20 at 14:03