# # # # # Birthday Problem Filename: birthday.py Author: David Radcliffe Created: 2013 July 15 License: CC BY 3.0 # Helper functions def binom(n,k,p=1): # Binomial coefficient n choose k (multiplied by p**k) k = min(k, n-k) ans = 1 for j in xrange(k): ans = (p*ans*(n-j))/(j+1) return ans def multinom(n, v, p=1): # Multinomial coefficient ans = 1 for k in v: ans *= binom(n, k, p) n -= k return ans # Q(n, 1, d) = probability that no two people out of a group of n # will have matching birthdays, given that there are d days in a year. # # More generally, # Q(n, k, d) = probability that a birthday is shared by some k people out # of n, but no birthday is shared by more than k people. # # A recursive formula for Q(n, k, d) is given at # http://mathworld.wolfram.com/BirthdayProblem.html # (and implemented here). # # Note that the time required to compute this formula # grows exponentially in k. # # P(n, k, d) = probability that k or more people _do_ share the same birthday # = 1 - sum(Q(n,i,d), i=1 to k-1) # Memo = { } def Q(n, k, d): if (n, k, d) in Memo: return Memo[(n, k, d)] if n >= d*k: return 0 p = 1.0 / d if k == 1: ans = 1.0 for i in range(n): ans *= p * (d-i) else: ans = 0.0 for i in xrange(1, int(n/k)+1): s = 0.0 for j in xrange(1, k): s += Q(n-i*k, j, d-i) * (1 - p*i)**(n-i*k) ans += s * binom(d, i) * multinom(n, (k,)*i, p) Memo[(n, k, d)] = ans return ans def P(n, k, d): ans = 1.0 for i in xrange(1, k): ans -= Q(n, i, d) return ans if __name__ == '__main__': print P(40, 2, 365) print P(500, 6, 365) # Probability that in a group of 500 people # at least five have a common birthday