My notes about combinatorics (binomial coefficent, permutations and cominations).
Kaggle: https://www.kaggle.com/code/michelesalvucci/combinatorics-with-python.
Championship example: 20 teams challange each other 2 by 2.
import scipy.special
import numpy as np
import itertools
from pprint import pprint
binomial_coeff = int(scipy.special.binom(20, 2))
# Multiply by 2 because of the round trip
test_championship_matches = 19*10*2
print(binomial_coeff * 2, test_championship_matches)
assert binomial_coeff * 2 == test_championship_matches
# Direct calculation of binomial coefficient
def custom_binom(n:int, k:int) -> int:
return int(np.math.factorial(n) / (np.math.factorial(k) * np.math.factorial(n-k)))
print(custom_binom(20, 2))
print(int(np.math.factorial(20) / (np.math.factorial(2) * np.math.factorial(20-2))))
# All the matches through itertools.permutations
teams = [
'Juve', 'Milan', 'Inter', 'Roma', 'Lazio',
'Fiorentina', 'Sampdoria', 'Genoa', 'Torino', 'Atalanta',
'Sassuolo', 'Udinese', 'Cagliari', 'Bologna', 'Parma',
'Salernitana', 'Chievo', 'Lecce', 'Palermo', 'Napoli'
]
matches = list(itertools.permutations(teams, 2))
print(matches[:10], "...")
print(len(matches))
380 380
190
190
[('Juve', 'Milan'), ('Juve', 'Inter'), ('Juve', 'Roma'), ('Juve', 'Lazio'), ('Juve', 'Fiorentina'), ('Juve', 'Sampdoria'), ('Juve', 'Genoa'), ('Juve', 'Torino'), ('Juve', 'Atalanta'), ('Juve', 'Sassuolo')] ...
380
arr = np.array(['h', 'e', 'l', 'o'], dtype='S')
# How many simple permutations?
print(np.math.factorial(arr.shape[0]))
# How many circular permutations?
print(np.math.factorial(arr.shape[0] - 1))
# Permutations through itertools
print(len(list(itertools.permutations(['h', 'e', 'l', 'o']))))
24
6
24
example_word = 'halloween'
example_word_lst = list(example_word)
arr = np.array(example_word_lst, dtype='S')
num = np.math.factorial(arr.shape[0])
unique, counts = np.unique(arr, return_counts=True)
print("Counts", counts)
# Calculate factorial for each count to find out denominator
ufunc = np.vectorize(lambda count: np.math.factorial(count))
counts = ufunc(counts)
print("Counts factorials", counts)
denom = np.multiply.reduce(counts)
result = int(num/denom)
print("Result:", result)
Counts [1 2 1 2 1 1 1]
Counts factorials [1 2 1 2 1 1 1]
Result: 90720
# Combination of class 3 of a 4-len array
example_word = 'aiuo'
example_word_lst = list(example_word)
combs = list(itertools.combinations(example_word_lst, 3))
print(combs)
assert len(combs) == 4
# Combination of class 3 of a 2-len array
example_word = 'st'
example_word_lst = list(example_word)
combs = list(itertools.combinations_with_replacement(example_word_lst, 3))
print(combs)
assert len(combs) == 4
[('a', 'i', 'u'), ('a', 'i', 'o'), ('a', 'u', 'o'), ('i', 'u', 'o')]
[('s', 's', 's'), ('s', 's', 't'), ('s', 't', 't'), ('t', 't', 't')]
8 friends meet weekly for a dinner, changing every time their seat at the table. Supposing that seats are numbered, after how many years they will run out of every possible disposition?
The starting set is formed by \(n=8\) elements and each seat at the table is a group of \(k=8\) elements.
Order is fundamental, indeed each seat at the table differs from the others right for the order the friends take places.
\(k=n\) and the elements are distinct (no repetitions), therefore each table seat is a simple permutation of 8.
np.math.factorial(8)
40320
There are 40.320 possible ways of disposing seats at the table.
To calculate how many years they will take to run out of every possible disposition it's enough to divide the number of possible dispositions by the number of weeks in a year, that are about \(52\).
round(np.math.factorial(8) / 52, 2)
775.38
They will take about 775 years.