Challenge: nCr
Subdomeniu: Number Theory (number-theory)
Scor cont: 120.0 / 120
Submission status: Accepted
Submission score: 1.0
Submission ID: 464776690
Limbaj: python3
Link challenge: https://www.hackerrank.com/challenges/ncr/problem
Cerinta
Given two integers $n$ and $r$. In how many ways can $r$ items be chosen from $n$ items?
Input Format
The first line contains the number of test cases $T$. Each of the next $T$ lines contains two integers $n$ and $r$.
Output Format
Output $T$ lines, containing the required answer for the corresponding test case. Output all answers modulo $142857$.
Constraints
$1 \le T \le 10^5$
$1 \le n \le 10^9$
$0 \le r \le n$
Cod sursa
import functools
import sys
class MathEngine():
"""A collection of algorithms for Modular Arithmetics.
References:
[1] Granville, A. "Binomial coefficients modulo prime powers".
[2] Knuth, D. "The Art of Computer Programming", vol 1.
[3] https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Polynomial_extended_Euclidean_algorithm
"""
def __init__(self):
self.pe = None
self._cache_factorial = {}
def binomial(self, n, m):
"""Find the binomial (n m) = n!/m!/(n-m)!."""
if m == 0:
if n == 0:
return 0
else:
return 1
b = self.factor_binomial(n, m)
result = self.factors_to_integer(b)
return result
def binomial_prime_power(self, n, m, p):
"""Find the max power of `p` which divides a binomial (n m)."""
result = self._number_of_carries(n-m,m,p)
return result
def factor_binomial(self, n, m):
"""Factor the binomial (n m) into primes.
The algorithm is based on Kummer's theorem [1, page 7].
Result:
{d1:p1, ..., dn:pn}, so that (n m) = d1^p1 * ... * dn^pn.
"""
result = {}
for p in self.primes(n+1):
power = self.binomial_prime_power(n,m,p)
if power > 0:
result[p] = power
return result
def factor_integer(self, n):
"""Factor n into primes.
Result:
{d1:p1, ..., dn:pn}, so that n = d1^p1 * ... * dn^pn.
"""
result = {}
for p in self.primes(n+1):
power = 0
while n % p == 0 and n != 1:
n = n // p
power += 1
if power > 0:
result[p] = power
if n == 1:
break
return result
def factor_factorial(self, n):
"""Factor the factorial of n into primes.
The algorithm is based on the Kummer's theorem formulated for binomial coefficients;
for the case of factorials see [1, eq.(17)] or [2, eq.(1.2.5.8)].
Result:
{d1:p1, ..., dn:pn} such that n! = d1^p1 * ... * dn^pn.
"""
result = {}
for prime in self.primes(n+1):
k = n
result[prime] = 0
while k > 0:
k = k // prime
result[prime] += k
return result
def factorial(self, n, p=0, mod=0):
"""Find the product of all integers <= n, i.e. the factorial of n;
if p > 0 exclude integers divisible by p; if mod > 0 find result
modulo mod."""
_cache_key = (n,p,mod)
if _cache_key in self._cache_factorial:
return self._cache_factorial[_cache_key]
result = 1
if p == 0:
if mod == 0:
for k in range(1,n+1):
result *= k
else:
for k in range(1,n+1):
result = (result*k) % mod
else:
if mod == 0:
for k in range(1,n+1):
if k % p != 0:
result *= k
else:
for k in range(1,n+1):
if k % p != 0:
result = (result*k) % mod
self._cache_factorial[_cache_key] = result
return result
def factors_to_integer(self, n):
result = 1
for prime,power in n.items():
result *= prime**power
return result
def gcd_extended(self, a, b):
"""Find gcd(a,b) and coefficients s,t of the Bezout's identity, such that
`a*s + b*t = gcd(a,b)`. See [3] for more details.
Result:
(gcd(a,b), s, t)
"""
s, s_old = 0, 1
t, t_old = 1, 0
r, r_old = b, a
while r != 0:
k = r_old // r
r_old, r = r, r_old - k * r
s_old, s = s, s_old - k * s
t_old, t = t, t_old - k * t
return r_old, s_old, t_old
def integer_digits(self, n, p):
"""Find digits of n in the p representation.
Result:
[a0,...,ak], such that n = a0 + a1*p + ... + ak*p^k, where ai < p.
"""
result = []
while n > 0:
result.append(n % p)
n //= p
return result
def integer_digits_len(self, n, p):
result = 1
while n > 0:
result += 1
n //= p
return result
def mod_binomial(self, n, m, mod):
"""Find the reminder from the division of the binomial (n m) by mod.
The algorithm is simple and slow, but less prone to errors comparing
to the faster and more complex generalized Lucas algorithm, for more
details see `mod_binomial_lucas` method.
"""
binomial_factors = self.factor_binomial(n, m)
result = 1
for prime, exp in binomial_factors.items():
result = (result * pow(prime, exp, mod)) % mod
return result
def mod_binomial_lucas(self, n, m, p, q=1):
"""Find the reminder form the division of the binomial (n m) by p^q,
where mod is a prime number. This routine employs Lucas' Theorem (for q=1)
and its generalization for (q>1), see [1, Theorem 1].
"""
e0 = self.binomial_prime_power(n,m,p)
qq = q-e0
if qq < 0:
return 0
p_e0, p_q, p_qq = p**e0, p**q, p**qq
r = n - m
d = self.integer_digits_len(n, p)
_ni, _mi, _ri = n, m, r
_least_positive_residue_n = []
_least_positive_residue_m = []
_least_positive_residue_r = []
for i in range(0, d):
_least_positive_residue_n.append(_ni % p_qq)
_least_positive_residue_m.append(_mi % p_qq)
_least_positive_residue_r.append(_ri % p_qq)
_ni //= p
_mi //= p
_ri //= p
_factorial = [1]
curr = 1
for x in range(1,p_qq):
if x % p != 0:
curr = (curr*x) % p_qq
_factorial.append(curr)
result, i = 1, 0
aaa, bbb, ccc = 1, 1, 1
for i in range(0, d):
aa = _least_positive_residue_n[i]
bb = _least_positive_residue_m[i]
cc = _least_positive_residue_r[i]
a = _factorial[aa]
b = _factorial[bb]
c = _factorial[cc]
aaa = (aaa*a) % p_qq
bbb = (bbb*b) % p_qq
ccc = (ccc*c) % p_qq
i += 1
result = aaa * self.mod_inverse(bbb*ccc % p_qq, p_qq) % p_qq
if p != 2 or qq < 3:
result *= (-1)**self._number_of_carries(m,r,p,qq-1)
result = (result * p_e0) % p_q
return result
def _least_positive_residue(self, n, j, p, q):
"""Helper for mod_binomial_lucas method"""
result = (n//p**j) % p**q
return result
def _number_of_carries(self, m, r, p, j=0):
"""Helper for mod_binomial_lucas method"""
result, carry, jj = 0, 0, 0
while (m > 0 or r > 0):
if (m % p) + (r % p) + carry >= p:
carry = 1
else:
carry = 0
if jj >= j:
result += carry
m, r = m // p, r // p
jj += 1
return result
def mod_chinese(self, rs):
n = functools.reduce(lambda a,b: a*b, rs.keys(), 1)
result = 0
for ni,ai in rs.items():
nn = n // ni
_, ri, si = self.gcd_extended(ni, nn)
ei = si*nn
result += ai*ei
result = result % n
return result
def mod_inverse(self, n, mod):
r, s, t = self.gcd_extended(n, mod)
if r != 1:
return None
return s
def primes(self, n):
return self.pe.primes(n)
class Solver(object):
def __init__(self):
self.me = MathEngine()
self.mod = 142857
def solution(self, n, r):
"""Find solution to the "nCr" problem."""
mod_factors = {3: 3, 11: 1, 13: 1, 37: 1}
mods = {p**q:self.me.mod_binomial_lucas(n, r, p, q) for p,q in mod_factors.items()}
result = self.me.mod_chinese(mods)
return result
if __name__ == '__main__':
T = int(sys.stdin.readline())
solver = Solver()
for i in range(T):
n, r = [int(num) for num in sys.stdin.readline().split()]
print(solver.solution(n, r))
HackerRank Number Theory – nCr
