You may be familiar with the concept of combinations: for example, if you take 5 cards from a 52 cards deck as you would playing poker, you can have a certain number (2,598,960, would you say?) of different combinations.

In mathematics the number of *k* combinations you can have taking from a set of *n* elements is called the [binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient) of n and k, more popularly called **n choose k**.

The formula to compute it is relatively simple: `n choose k`==`n!/(k!*(n-k)!)`, where `!` of course denotes the factorial operator.

You are now to create a choose function that computes the binomial coefficient, like this:

```
choose(1,1)==1
choose(5,4)==5
choose(10,5)==252
choose(10,20)==0
choose(52,5)==2598960
```

Be warned: a certain degree of optimization is expected, both to deal with larger numbers precision (and their rounding errors in languages like JS) and computing time.

Difficulty: introductory

Starter code:

def choose(n,k):
	

Solutions:
from math import factorial

def choose(n, k):
    return factorial(n) / ( factorial(k) * factorial(n-k) ) if k <=n else 0

import sys
import functools
sys.setrecursionlimit(1_000_000)

# lru_cache is a python shortcut for memoization
# memoization is a technique to avoid repeatedly calculating
# something that has already been calculated

@functools.lru_cache(maxsize=1_000_000)
def choose(n,k):
    # pascals triangle uses only addition
    # so no rounding
    #               1
    #              1 1
    #             1 2 1
    #            1 3 3 1
    #           1 4 6 4 1
    # with exception of edge cases
    # this has recursive formula
    # choose(n, k) = choose(n-1, k-1) + choose(n-1, k)
    if k > n:
        return 0
    if k == 0 or k == n:
        return 1
    return choose(n-1, k-1) + choose(n-1, k)

def choose(n,k):
    l = [max(k,n-k),min(k,n-k)]
    nu = 0 if k > n else 1
    de = 1
    for i in range(l[0]+1,n+1):
        nu *= i
    for i in range(1,l[1]+1):
        de *= i
    return int(nu/de)

from functools import reduce
from math import factorial


def choose(n, k):
    return reduce(int.__mul__, range(n - k + 1, n + 1), 1) // factorial(k)

def choose(n, k):
    if k == 0:
        return 1
    else: 
        return (n * choose(n - 1, k - 1)) // k

def choose(n, k):
    return prod(n-k, n)//prod(1, k)

def prod(f, t):
    if f == t:
        return 1
    return t * prod(f, t-1)

from math import factorial as f
choose=lambda n,k:f(n)/(f(k)*f(n-k)) if k <= n else 0

def choose(n,k):
    def factorial(num):
    #a nested function for computing the factorial of the 
    # n, k arguments of the choose function
        ra = reversed(list(range(1,num)))
        for r in ra:
            num = num * r
        return num

    # a little of exception handling and conditional reasoning
    # to make deal with edge cases involving negative values 
    # and ZeroDivisionError
    try:
        choose = factorial(n)/(factorial(k)*factorial((n-k)))
    except ZeroDivisionError:
        choose = n/k
    if choose < 0:
        return 0
    else:
        return choose