Hexbugman213

python nCr

Dec 13th, 2021 (edited)
1,048
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.79 KB | None | 0 0
  1. import math
  2. import timeit
  3. import typing
  4. from fractions import Fraction
  5. from functools import reduce
  6. from math import factorial
  7. from operator import mul  # or mul=lambda x,y:x*y
  8.  
  9. import gmpy2
  10. import scipy
  11. import sympy
  12. from scipy.special import binom
  13.  
  14.  
  15. def nasbanov(n, k):
  16.     # https://stackoverflow.com/a/3027128/9044183
  17.     return int(reduce(mul, (Fraction(n - i, i + 1) for i in range(k)), 1))
  18.  
  19.  
  20. def andrewdalke(n, k):
  21.     # https://stackoverflow.com/a/3025547/9044183
  22.     if 0 <= k <= n:
  23.         ntok = 1
  24.         ktok = 1
  25.         for t in range(1, min(k, n - k) + 1):
  26.             ntok *= n
  27.             ktok *= t
  28.             n -= 1
  29.         return ntok // ktok
  30.     else:
  31.         return 0
  32.  
  33.  
  34. def jfsmodifiedscipy(N, k):
  35.     # https://stackoverflow.com/a/3025547/9044183
  36.     if (k > N) or (N < 0) or (k < 0):
  37.         return 0
  38.     top = N
  39.     val = 1
  40.     while top > (N - k):
  41.         val *= top
  42.         top -= 1
  43.     n = 1
  44.     while n < k + 1:
  45.         val /= n
  46.         n += 1
  47.     return val
  48.  
  49.  
  50. def stdfactorial(n, r):
  51.     # https://stackoverflow.com/a/19109792/9044183
  52.     return factorial(n) // factorial(r) // factorial(n - r)
  53.  
  54.  
  55. def stdfactorial2(n, r):
  56.     # https://stackoverflow.com/a/52987317/9044183
  57.     return factorial(n) // (factorial(r) * factorial(n - r))
  58.  
  59.  
  60. def recursive(n, k):
  61.     # https://stackoverflow.com/a/43107785/9044183
  62.     return 1 if k == 0 or k == n else recursive(n - 1, k - 1) + recursive(n - 1, k)
  63.  
  64.  
  65. def pantelis300(n, k):
  66.     # https://stackoverflow.com/a/25813723/9044183
  67.     c = [0] * (n + 1)
  68.     c[0] = 1
  69.     for i in range(1, n + 1):
  70.         c[i] = 1
  71.         j = i - 1
  72.         while j > 0:
  73.             c[j] += c[j - 1]
  74.             j -= 1
  75.  
  76.     return c[k]
  77.  
  78.  
  79. def rojas(n, k):
  80.     # https://stackoverflow.com/a/56120255/9044183
  81.     m = 0
  82.     if k == 0:
  83.         m = 1
  84.     if k == 1:
  85.         m = n
  86.     if k >= 2:
  87.         num, dem, op1, op2 = 1, 1, k, n
  88.         while op1 >= 1:
  89.             num *= op2
  90.             dem *= op1
  91.             op1 -= 1
  92.             op2 -= 1
  93.         m = num // dem
  94.     return m
  95.  
  96.  
  97. def wirawan(n, r):
  98.     # https://stackoverflow.com/a/12115521/9044183
  99.     assert n >= 0
  100.     assert 0 <= r <= n
  101.  
  102.     c = 1
  103.     for (num, denom) in zip(range(n, n - r, -1), range(1, r + 1, 1)):
  104.         c = (c * num) // denom
  105.     return c
  106.  
  107.  
  108. def kta(n, r):
  109.     # https://stackoverflow.com/a/58247588/9044183
  110.     if r == 0:
  111.         return 1
  112.     p = n
  113.     for i in range(1, r):
  114.         p = p * (n - i) / i
  115.     else:
  116.         p = p / r
  117.     return p
  118.  
  119.  
  120. def rabih(n, k):
  121.     # https://stackoverflow.com/a/30428134/9044183
  122.     if k == n:
  123.         return 1
  124.     if k > n:
  125.         return 0
  126.     d, q = max(k, n - k), min(k, n - k)
  127.     num = 1
  128.     for n in range(d + 1, n + 1):
  129.         num *= n
  130.     denom = 1
  131.     for d in range(1, q + 1):
  132.         denom *= d
  133.     return num / denom
  134.  
  135.  
  136. def scipybinom(n, r):
  137.     return scipy.special.binom(n, r)
  138.  
  139.  
  140. def scipycombexact(n, r):
  141.     return scipy.special.comb(n, r, True)
  142.  
  143.  
  144. def hybrid(n: typing.Union[int, float], k: typing.Union[int, float]) -> typing.Union[int, float]:
  145.     # my own custom hybrid solution
  146.     def is_integer(n):
  147.         return isinstance(n, int) or n.is_integer()
  148.     if k < 0:
  149.         raise ValueError("k cannot be negative.")
  150.     elif n == 0:
  151.         return 0
  152.     elif k == 0 or k == n:
  153.         return 1
  154.     elif is_integer(n) and is_integer(k):
  155.         return int(gmpy2.comb(int(n), int(k)))
  156.     elif n > 0:
  157.         return scipy.special.binom(n, k)
  158.     else:
  159.         return float(sympy.binomial(n, k))
  160.  
  161.  
  162. def test_basic_func(func):
  163.     [[func(i, j) for j in range(i + 1)] for i in range(1, 100)]
  164.  
  165.  
  166. def oobf(func):
  167.     [func(100, j) for j in range(100, 200)]
  168.  
  169.  
  170. def ffrac_n(func):
  171.     [[func(i + 0.5, j) for j in range(i + 1)] for i in range(1, 100)]
  172.  
  173.  
  174. def ffrac_r(func):
  175.     [[func(i, j + 0.5) for j in range(i + 1)] for i in range(1, 100)]
  176.  
  177.  
  178. def ffrac_nr(func):
  179.     [[func(i + 0.5, j + 0.5) for j in range(i + 1)] for i in range(1, 100)]
  180.  
  181.  
  182. def negative_test(func):
  183.     [[func(-i, j) for j in range(i + 1)] for i in range(1, 100)]
  184.  
  185.  
  186. def negative_fracnr_test(func):
  187.     [[func(-i - 0.5, j + 0.5) for j in range(i + 1)] for i in range(1, 100)]
  188.  
  189.  
  190. def test():
  191.     # pos int n/r and negative n and out of bounds and frac n and frac r
  192.     fully_featured = [sympy.binomial, hybrid]
  193.     negative_n = [nasbanov, rojas, kta, gmpy2.comb] + fully_featured
  194.     frac_n_and_r = [scipybinom] + fully_featured
  195.     frac_n = [kta] + frac_n_and_r
  196.     frac_r = [scipy.special.comb] + frac_n_and_r
  197.     # large indices return 0
  198.     out_of_bounds = [nasbanov, andrewdalke, jfsmodifiedscipy, rojas, kta, rabih, math.comb, gmpy2.comb, scipybinom,
  199.                      scipy.special.comb, sympy.binomial, scipycombexact, hybrid]
  200.     basicfuncs = [stdfactorial, stdfactorial2, pantelis300, wirawan] + out_of_bounds
  201.     tests = {
  202.         "basic": (basicfuncs, test_basic_func,),
  203.         "oob": (out_of_bounds, oobf,),
  204.         "fn": (frac_n, ffrac_n,),
  205.         "fr": (frac_r, ffrac_r,),
  206.         "fnr": (frac_n_and_r, ffrac_nr,),
  207.         "nn": (negative_n, negative_test,),
  208.         "f": (fully_featured, negative_fracnr_test),
  209.     }
  210.     for name, (funcs, testfunc) in tests.items():
  211.         print(name.upper())
  212.         functimes = {}
  213.         for func in funcs:
  214.             funcname = f"{getattr(func, '__module__', '?')}.{func.__name__}"
  215.             print(f"timing {funcname}...")
  216.             functime = timeit.timeit(lambda: testfunc(func), number=10) / 10
  217.             functimes[funcname] = functime
  218.         print("-----")
  219.         functimes = {k: v for k, v in sorted(functimes.items(), key=lambda item: item[1])}
  220.         for k, v in functimes.items():
  221.             print(k, v)
  222.  
  223.  
  224. def get_features():
  225.     funcs = [nasbanov, andrewdalke, jfsmodifiedscipy, stdfactorial, stdfactorial2, recursive,
  226.              pantelis300, rojas, wirawan, kta, rabih, math.comb, gmpy2.comb, scipybinom,
  227.              scipy.special.comb, sympy.binomial, scipycombexact]
  228.     for func in funcs:
  229.         funcname = f"{getattr(func, '__module__', '?')}.{func.__name__}"
  230.         features = ["pos int n/r"]
  231.         try:
  232.             assert func(-4, 2) == 10
  233.             features.append("negative n")
  234.         except Exception as e:
  235.             pass
  236.         try:
  237.             assert func(5, 10) == 0
  238.             features.append("out of bounds")
  239.         except Exception as e:
  240.             pass
  241.         try:
  242.             assert func(0.5, 3) == 1 / 16
  243.             features.append("frac n")
  244.         except Exception as e:
  245.             pass
  246.         try:
  247.             assert not float(func(3, 0.5)).is_integer()
  248.             features.append("frac r")
  249.         except Exception as e:
  250.             pass
  251.  
  252.         print(f"{funcname} supports {' and '.join(features)}")
  253.  
  254.  
  255. if __name__ == "__main__":
  256.     test()
  257.  
Add Comment
Please, Sign In to add comment