Binominal coefficient in python - python

so I was wondering how i could get around this problem where i have this code
#defining a function that computes the factorial of an integer
def fac(b):
if b==1:
return 1
else:
return b * fac(b-1)
#takes two integers and does the binomial coefficient operand
def combinations(n,k):
result = (fac(n)) / (fac(k) * fac(n-k))
return result
n=10
k=2
print(combinations(n,k))
and using it to (nCk) large numbers such as (1000,700) without using the
sys.setreccursionlimit(x)
for example. can i somehow remove the lowest values in (6C3) so it just calculates (6x5x4)/(3x2x1) since 6! is 6x5x4x3x2x1 and 3! is 3x2x1. and the formula is n!/(k!x(n-k)!)
and therefore lowering the amounts of recursions happening

I feel you're kind of trying to reinvent the wheel here. This functionality already exists in the built-in math module, and it's quite efficient:
>>> import math
>>> math.comb(1000, 700)
542825004640614064815358503892902599588060075560435179852301016412253602009800031872232761420804306539976220810204913677796961128392686442868524741815732892024613137013599170443939815681313827516308854820419235457578544489551749630302863689773725905288736148678480

Factorial
Recursion is great in some languages, but absolutely sucks in python. Do not use recursion in python unless you have a very good reason to.
For your factorial function, I suggest using a simple loop:
def factorial(n):
result = 1
for k in range(2,n+1):
result *= k
return result
Or you can use the prod function to compute the product of the range directly:
from math import prod
def factorial(n):
return prod(range(2, n+1))
Or you can use the existing factorial function directly:
from math import factorial
Binomial coefficient
Mathematicians like to "compress" the formula of the binomial coefficient as (n choose k) = factorial(n) / (factorial(k) * factorial(n-k)), but this formula is inefficient for no good reason if used directly. Remember that all the factors in factorial(n-k) cancel out with the lower factors from factorial(n). So, there is no need to compute the product of these factors at all.
Instead you can at least do this small optimisation:
def binomial(n, k):
a, b = (k, n-k) if k < n-k else (n-k, k)
numerator = 1
for i in range(b+1, n+1):
numerator *= i
return numerator / factorial(a)
Of course you can use the existing function comb directly:
from math import comb

You can use the cache decorator (docs, nice video tutorial)
from functools import cache
#cache
def fac(b):
return b * fac(b-1) if b else 1
def combinations(n,k):
M = max(n,k)
for i in range(M):
fac(i)
return (fac(n)) // (fac(k) * fac(n-k))
n=1000
k=700
print(combinations(n,k))
Output
542825004640614064815358503892902599588060075560435179852301016412253602009800031872232761420804306539976220810204913677796961128392686442868524741815732892024613137013599170443939815681313827516308854820419235457578544489551749630302863689773725905288736148678480

Related

Is there a function to multiply the result of all iterations of a loop in python? [duplicate]

How do I go about computing a factorial of an integer in Python?
The easiest way is to use math.factorial (available in Python 2.6 and above):
import math
math.factorial(1000)
If you want/have to write it yourself, you can use an iterative approach:
def factorial(n):
fact = 1
for num in range(2, n + 1):
fact *= num
return fact
or a recursive approach:
def factorial(n):
if n < 2:
return 1
else:
return n * factorial(n-1)
Note that the factorial function is only defined for positive integers, so you should also check that n >= 0 and that isinstance(n, int). If it's not, raise a ValueError or a TypeError respectively. math.factorial will take care of this for you.
On Python 2.6 and up, try:
import math
math.factorial(n)
Existing solution
The shortest and probably the fastest solution is:
from math import factorial
print factorial(1000)
Building your own
You can also build your own solution. Generally you have two approaches. The one that suits me best is:
from itertools import imap
def factorial(x):
return reduce(long.__mul__, imap(long, xrange(1, x + 1)))
print factorial(1000)
(it works also for bigger numbers, when the result becomes long)
The second way of achieving the same is:
def factorial(x):
result = 1
for i in xrange(2, x + 1):
result *= i
return result
print factorial(1000)
def factorial(n):
if n < 2:
return 1
return n * factorial(n - 1)
For performance reasons, please do not use recursion. It would be disastrous.
def fact(n, total=1):
while True:
if n == 1:
return total
n, total = n - 1, total * n
Check running results
cProfile.run('fact(126000)')
4 function calls in 5.164 seconds
Using the stack is convenient (like recursive call), but it comes at a cost: storing detailed information can take up a lot of memory.
If the stack is high, it means that the computer stores a lot of information about function calls.
The method only takes up constant memory (like iteration).
Or using a 'for' loop
def fact(n):
result = 1
for i in range(2, n + 1):
result *= i
return result
Check running results
cProfile.run('fact(126000)')
4 function calls in 4.708 seconds
Or using the built-in function math
def fact(n):
return math.factorial(n)
Check running results
cProfile.run('fact(126000)')
5 function calls in 0.272 seconds
If you are using Python 2.5 or older, try
from operator import mul
def factorial(n):
return reduce(mul, range(1, n+1))
For newer versions of Python, there is factorial in the math module as given in other answers here.
def fact(n):
f = 1
for i in range(1, n + 1):
f *= i
return f
Another way to do it is to use np.prod shown below:
def factorial(n):
if n == 0:
return 1
else:
return np.prod(np.arange(1,n+1))
Non-recursive solution, no imports:
def factorial(x):
return eval(' * '.join(map(str, range(1, x + 1))))
You can also make it in one line recursively if you like it. It is just a matter of personal choice. Here we are using inline if else in Python, which is similar to the ternary operator in Java:
Expression1 ? Expression2 : Expression3
One line function call approach:
def factorial(n): return 1 if n == 0 else n * factorial(n-1)
One line lambda function approach:
(although it is not recommended to assign lambda functions directly to a name, as it is considered a bad practice and may bring inconsistency to your code. It's always good to know. See PEP8.)
factorial = lambda n: 1 if n == 0 else n * factorial(n-1)

My program can't run that fast even with memoization

I tried a problem on project euler where I needed to find the sum of all the fibonacci terms under 4 million. It took me a long time but then I found out that I can use memoization to do it but it seems to take still a long time. After a lot of research, I found out that I can use a built-in module called lru_cache. My question is : why isn't it as fast as memoization ?
Here's my code:
from functools import lru_cache
#lru_cache(maxsize=1000000)
def fibonacci_memo(input_value):
global value
fibonacci_cache = {}
if input_value in fibonacci_cache:
return fibonacci_cache[input_value]
if input_value == 0:
value = 1
elif input_value == 1:
value = 1
elif input_value > 1:
value = fibonacci_memo(input_value - 1) + fibonacci_memo(input_value - 2)
fibonacci_cache[input_value] = value
return value
def sumOfFib():
SUM = 0
for n in range(500):
if fibonacci_memo(n) < 4000000:
if fibonacci_memo(n) % 2 == 0:
SUM += fibonacci_memo(n)
return SUM
print(sumOfFib())
The code works by the way. It takes less than a second to run it when I use the lru_cache module.
The other answer is the correct way to calculate the fibonacci sequence, indeed, but you should also know why your memoization wasn't working. To be specific:
fibonacci_cache = {}
This line being inside the function means you were emptying your cache every time fibonacci_memo was called.
You shouldn't be computing the Fibonacci sequence, not even by dynamic programming. Since the Fibonacci sequence satisfies a linear recurrence relation with constant coefficients and constant order, then so will be the sequence of their sums.
Definitely don't cache all the values. That will give you an unnecessary consumption of memory. When the recurrences have constant order, you only need to remember as many previous terms as the order of the recurrence.
Further more, there is a way to turn recurrences of constant order into systems recurrences of order one. The solution of the latter is given by a power of a matrix. This gives a faster algorithm, for large values of n. Each step will be more expensive, though. So, the best method would use a combination of the two, choosing the first method for small values of n and the latter for large inputs.
O(n) using the recurrence for the sum
Denote S_n=F_0+F_1+...+F_n the sum of the first Fibonacci numbers F_0,F_1,...,F_n.
Observe that
S_{n+1}-S_n=F_{n+1}
S_{n+2}-S_{n+1}=F_{n+2}
S_{n+3}-S_{n+2}=F_{n+3}
Since F_{n+3}=F_{n+2}+F_{n+1} we get that S_{n+3}-S_{n+2}=S_{n+2}-S_n. So
S_{n+3}=2S_{n+2}-S_n
with the initial conditions S_0=F_0=1, S_1=F_0+F_1=1+1=2, and S_2=S_1+F_2=2+2=4.
One thing that you can do is compute S_n bottom up, remembering the values of only the previous three terms at each step. You don't need to remember all of the values of S_k, from k=0 to k=n. This gives you an O(n) algorithm with O(1) amount of memory.
O(ln(n)) by matrix exponentiation
You can also get an O(ln(n)) algorithm in the following way:
Call X_n to be the column vector with components S_{n+2},S_{n+1},S_{n}
So, the recurrence above gives the recurrence
X_{n+1}=AX_n
where A is the matrix
[
[2,0,-1],
[1,0,0],
[0,1,0],
]
Therefore, X_n=A^nX_0. We have X_0. To multiply by A^n we can do exponentiation by squaring.
For the sake of completeness here are implementations of the general ideas described in #NotDijkstra's answer plus my humble optimizations including the "closed form" solution implemented in integer arithmetic.
We can see that the "smart" methods are not only an order of magnitude faster but also seem to scale better compatible with the fact (thanks #NotDijkstra) that Python big ints use better than naive multiplication.
import numpy as np
import operator as op
from simple_benchmark import BenchmarkBuilder, MultiArgument
B = BenchmarkBuilder()
def pow(b,e,mul=op.mul,unit=1):
if e == 0:
return unit
res = b
for bit in bin(e)[3:]:
res = mul(res,res)
if bit=="1":
res = mul(res,b)
return res
def mul_fib(a,b):
return (a[0]*b[0]+5*a[1]*b[1])>>1 , (a[0]*b[1]+a[1]*b[0])>>1
def fib_closed(n):
return pow((1,1),n+1,mul_fib)[1]
def fib_mat(n):
return pow(np.array([[1,1],[1,0]],'O'),n,op.matmul)[0,0]
def fib_sequential(n):
t1,t2 = 1,1
for i in range(n-1):
t1,t2 = t2,t1+t2
return t2
def sum_fib_direct(n):
t1,t2,res = 1,1,1
for i in range(n):
t1,t2,res = t2,t1+t2,res+t2
return res
def sum_fib(n,method="closed"):
if method == "direct":
return sum_fib_direct(n)
return globals()[f"fib_{method}"](n+2)-1
methods = "closed mat sequential direct".split()
def f(method):
def f(n):
return sum_fib(n,method)
f.__name__ = method
return f
for method in methods:
B.add_function(method)(f(method))
B.add_arguments('N')(lambda:(2*(1<<k,) for k in range(23)))
r = B.run()
r.plot()
import matplotlib.pylab as P
P.savefig(fib.png)
I am not sure how you are taking anything near a second. Here is the memoized version without fanciness:
class fibs(object):
def __init__(self):
self.thefibs = {0:0, 1:1}
def __call__(self, n):
if n not in self.thefibs:
self.thefibs[n] = self(n-1)+self(n-2)
return self.thefibs[n]
dog = fibs()
sum([dog(i) for i in range(40) if dog(i) < 4000000])

Functional Programming Python: smallest number divisible by each of the numbers 1 to 20

I want to build a while loop in python using its functional programming capabilities, but until now I'm failing.
What I have accomplished is this peace of code, which should calculate the smallest number divisible by each of the numbers 1 to 20. But it doens't seem it's using functional programming capabilities a lot. And also gives me the error as below:
"RuntimeError: maximum recursion depth exceeded" at the line of the incrementation of "i";
even if this should be limited to 20.
def byYmult(x, y): return x % y == 0
def first20div():
i=0
for y in range(1,20):
i += byYmult(x, y)
return i >= 20
def while_block():
global x
if first20div():
print(x)
return 1
else:
x += 1
return 0
x = 0
while_FP = lambda: ((not first20div()) and while_block() ) or while_FP()
while_FP()
This is non-functial for a lot of reasons:
you do not pass, nor return functions;
you only construct a named lambda expression at the bottom, but this is usually considered un-Pythonic;
usually functional programming means you do not alter data, but here you define a global x, that you update;
by some globals are also seen as non-functional: all data should be passed to the function.
So there is a lot to work with. Furthermore the algorithm you describe is not very optimal. Instead of performing a brute force approach where we keep guessing the number until finally we got lucky, a better approach is to calculate the least common multiple (LCM) of the numbers 1..20.
We can first define - in a functional way - we can calculate the LCM by calculating the greatest common divider (GCD) first, and this can be done by the Euclidean Algorithm. Lucky for us, it is already in the math package:
from math import gcd
Now the LCM is:
def lcm(a,b):
return (a*b)//gcd(a,b)
The LCM of three or more numbers can be calculated by calculating the LCM of the first two numbers and then pass it as first argument to the LCM with the third number, or more formally:
lcm(x,y,z) == lcm(lcm(x,y),z)
and this can be done by using reduce from functools:
from functools import reduce
def lcm_multiple(xs):
return reduce(lcm, xs)
and now we can calculate the answer, by passing it a range(2,20) object:
answer = lcm_multiple(range(2, 20))
Or in full:
from math import gcd
from functools import reduce
def lcm(a,b):
return (a*b)//gcd(a,b)
def lcm_multiple(xs):
return reduce(lcm, xs)
answer = lcm_multiple(range(2, 20))

How can I make this python function run O(log n) time instead of O(n) time?

def findMax(f,c):
n=1
while f(n) <= c:
n += 1
return n
This is a higher-order python function that given function f and a maximal count,
c returns the largest n such that f(n) ≤ c. This works but not when n gets too large for e.g f(10**6). How can I make this algorithm run O(log n) time so it can facilitate f(10**6) using the function below?
def f(n):
return math.log(n, 2)
Change n += 1 to n *= 2 for logarithmic outcome.
Logarithmic sequences increment in multiples of 2, and are non-linear, thus logarithmic sequences don't increment by 1.
Use a search algorithm to find the solution faster. This is an implementation using jpe.math.framework.algorythems.brent which is an implementation of the brent search algorithm.
import math
import jpe
import jpe.math.framework.algorythems
def f(x):
return math.log(x, 2)
value = 9E2
startVal = 1E300
val = int(jpe.math.framework.algorythems.brent(f, a=0, b=startVal, val=value, mode=jpe.math.framework.algorythems.modes.equalVal)[0])#takes 37 iters
print(val)
Alternatively in this scenario with this f:
the result is within 1 of 2**c (c as passedto findMax)

How do you a double factorial in python?

I've been stucked on this question for a really long time.
I've managed to do a single recursive factorial.
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
Double factorial
For an even integer n, the double factorial is the product of all even positive integers less than or equal to n. For an odd integer p, the double factorial is the product of all odd positive integers less than or equal to p.
If n is even, then n!! = n*(n - 2)*(n - 4)*(n - 6)* ... *4*2
If p is odd, then p!! = p*(p - 2)*(p - 4)*(p - 6)* ... *3*1
But I have no idea to do a double factorial. Any help?
from functools import reduce # only in Python 3
reduce(int.__mul__, range(n, 0, -2))
Isn't that just the same as the factorial with a different ending condition and a different parameter to the recursion call?
def doublefactorial(n):
if n <= 0:
return 1
else:
return n * doublefactorial(n-2)
If n is even, then it will halt when n == 0. If n is odd, then it will halt when n == -1.
The problem here is that the double factorial is defined for negative real numbers (-1)!! = 1, (-3)!! = -1 (even negative integers (such -2, -4, ...) should have solution as +/- inf) so... something is smelling bad in all solutions for negative numbers. If one want to define the double factorial for al reals those solutions don't work. The solution is to define the double factorial using gamma function.
import scipy.special as sp
from numpy import pi
def dfact(x):
n = (x + 1.)/2.
return 2.**n * sp.gamma(n + 0.5)/(pi**(0.5))
It works! :D
Starting Python 3.8, we can use the prod function from the math module which calculates the product of all elements in an iterable, which in our case is range(n, 0, -2):
import math
math.prod(range(n, 0, -2))
Note that this also handles the case n = 0 in which case the result is 1.
My version of the recursive solution, in one line:
dfact = lambda n: (n <= 0) or n * dfact(n-2)
However, it is also interesting to note that the double factorial can be expressed in terms of the "normal" factorial. For odd numbers,
n!! = (2*k)! / (2**k * k!)
where k = (n+1)/2. For even arguments n=2k, although this is not consistent with a generalization to complex arguments, the expression is simpler,
n!! = (2k)!! = 2*k * k!.
All this means that you can write code using the factorial function from the standard math library, which is always nice:
import math
fact = math.factorial
def dfact(n):
if n % 2 == 1:
k = (n+1)/2
return fact(2*k) / (2**k * fact(k))
else:
return 2**k * fact(k)
Now, this code is obviously not very efficient for large n, but it is quite instructive. More importantly, since we are dealing with standard factorials now, it is a very good starting point for optimizations when dealing with really large numbers. You try to use logarithms or gamma functions to get approximate double factorials for large numbers.
def doublefactorial(n):
if n in (0, 1):
return 1
else:
return n * doublefactorial(n-2)
should do it.
def double_fact(number):
if number==0 or number==1:
return 1
else:
return number*double_fact(number-2)
I think this should work for you.
I hope I understand it correctly, but will this work
def factorial(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial(n-2)
reduce(lambda x,y: y*x, range(n,1,-2))
Which is basically the same as the simple iterative version:
x = n
for y in range(n-2, 1, -2):
x*=y
Obviously you can also do it recursively, but what's the point ? This kind of example implemented using recursivity are fine when using all recursive languages, but with imperative language it's always making simple tools like recursivity looking more complex than necessary, while recursivity can be a real simplifier when dealing with fundamentally recursive structures like trees.
def doublefactorial(n):
if n <= 0:
return 1
else:
return n * doublefactorial(n-2)
That should do it. Unless I'm misunderstanding

Categories