Statistiques: combinaisons en Python
J'ai besoin de calculer combinatorials (rcn) en Python mais ne trouve pas la fonction pour le faire dans math
, numpy
ou stat
bibliothèques. Quelque chose comme une fonction du type:
comb = calculate_combinations(n, r)
J'ai besoin de connaître le nombre de combinaisons possibles, et pas les combinaisons, ce qui itertools.combinations
ne m'intéresse pas.
Enfin, je veux l'éviter à l'aide de factorielles, comme les chiffres, je vais être de calculer les combinaisons peuvent être trop gros et les factorielles sont va être monstrueux.
Cela semble VRAIMENT facile de répondre à la question, mais je suis d'être noyé dans des questions à propos de générer toutes les combinaisons, ce qui n'est pas ce que je veux.
Vous devez vous connecter pour publier un commentaire.
Voir scipy.spécial.peigne (scipy.misc.peigne dans les anciennes versions de scipy). Lorsque
exact
est Faux, il utilise le gammaln fonction pour obtenir une bonne précision sans prendre beaucoup de temps. Dans le cas précis, il renvoie une précision arbitraire entier, ce qui peut prendre beaucoup de temps à calculer.scipy.misc.comb
est dépréciée en faveur descipy.special.comb
depuis la version0.10.0
.Pourquoi ne pas écrire vous-même? C'est un one-liner ou tel:
Test d'impression le triangle de Pascal:
PS. édité pour remplacer
int(round(reduce(mul, (float(n-i)/(i+1) for i in range(k)), 1)))
avec
int(reduce(mul, (Fraction(n-i, i+1) for i in range(k)), 1))
afin de ne pas tre pour big N/Krange( ..)
avecxrange( ... )
en python 2.x 😉from functools import reduce
.k = min(k, n - k)
, commenCk = nC(n-k)
, et au lieu de faire un générateur d'expression divisant à chaque fois, de faire toutes les choses que vous avez besoin de multiplier les / toutes les choses que vous avez besoin de diviser. Donc:def nCk(n, k):
k = min(k, n - k);
dividend = reduce(mul, xrange(n - k + 1, n + 1), 1);
divisor = reduce(mul, xrange(1, r + 1), 1);
return dividend // divisor
Une recherche rapide sur google code donne (il utilise la formule de @Mark Byers réponse):
choose()
est 10 fois plus rapide (testé sur tous les 0 <= (n,k) < 1e3 paires) quescipy.misc.comb()
si vous avez besoin d'une réponse exacte.Si vous voulez des résultats exacts et vitesse, essayez de gmpy --
gmpy.comb
faire exactement ce que vous demandez, et, il est assez rapide (bien sûr, commegmpy
s'auteur original, je suis biaisé;-).gmpy2.comb()
est 10 fois plus rapide quechoose()
de ma réponse pour le code:for k, n in itertools.combinations(range(1000), 2): f(n,k)
oùf()
est soitgmpy2.comb()
ouchoose()
sur Python 3.Si vous voulez un résultat exact, utilisez
sympy.binomial
. Il semble être la méthode la plus rapide, les mains vers le bas.Une traduction littérale de la définition mathématique est tout à fait adéquat dans beaucoup de cas (en se souvenant que Python utilise automatiquement grand nombre arithmétique):
Pour certains intrants j'ai testé (par exemple n=1000 r=500), c'est plus de 10 fois plus rapide que l'un liner
reduce
suggéré dans un autre (plus haut voté) réponse. D'autre part, il est réalisé par le snippit fournis par @J. F. Sebastian.Voici une autre alternative. Ce fut à l'origine écrit en C++, de sorte qu'il peut être reporté sur C++ pour une durée de précision de type entier (par exemple __int64). L'avantage, c'est (1) elle porte uniquement sur les opérations sur entiers, et (2) il évite les ballonnements la valeur de l'entier en faisant des paires successives de la multiplication et de la division. J'ai testé le résultat avec Nas Banov du triangle de Pascal, il obtient la réponse correcte:
Justification: afin De minimiser le nombre de multiplications et de divisions, nous réécrire l'expression comme
Pour éviter la multiplication de débordement autant que possible, nous allons évaluer en suivant strictement l'ordre, de gauche à droite:
Nous pouvons montrer que l'entier arithmatic utilisé dans ce type de commande est exacte (pas d'erreur d'arrondi).
Utilisant la programmation dynamique, la complexité du temps est en Θ(n*m) et de la complexité de l'espace Θ(m):
Si votre programme a une limite supérieure de
n
(diren <= N
) et les besoins à plusieurs reprises de calcul de la rcn (préférence pour >>N
fois), à l'aide de lru_cache peut vous donner un énorme gain de performance:De la construction de la mémoire cache (ce qui est fait implicitement) prend jusqu'à
O(N^2)
temps. Les appels suivants ànCr
sera de retour dansO(1)
.Vous pouvez écrire 2 fonctions simples qui en fait s'avère être d'environ 5-8X plus rapide que l'utilisation scipy.spécial.peigne. En fait, vous n'avez pas besoin d'importer tous les paquets supplémentaires, et la fonction est tout à fait lisible. L'astuce est d'utiliser memoization pour stocker les valeurs précédemment calculées, et en utilisant la définition de rcn
Si nous comparons les temps de
De départ
Python 3.8
, de la bibliothèque standard comprend maintenant lamath.peigne
fonction pour calculer le coefficient binomial:qui est le nombre de façons de choisir k objets parmi n objets sans répétition
n! /(k! (n - k)!)
:C'est assez facile avec sympy.
En utilisant uniquement de la bibliothèque standard distribué avec Python:
Directe de la formule produit des grands entiers si n est plus grand que 20.
Donc, encore une autre réponse:
court, précis et efficace, car cela évite de python grands entiers en collant avec la nostalgie.
Il est plus précis et plus rapide lors de la comparaison de scipy.spécial.peigne:
range(n-r+1, n+1)
au lieu derange(n-r,n+1)
.C'est @killerT2333 code à l'aide de la builtin memoization décorateur.
C'est probablement aussi vite que vous pouvez le faire en pure python pour raisonnablement grandes entrées:
Cette fonction est très optimazed.
Ici est un algorithme efficace pour vous
Par exemple rcn(30,7)
= fact(30) /( fait(7) * fact(23))
= ( 30 * 29 * 28 * 27 * 26 * 25 * 24 ) /(1 * 2 * 3 * 4 * 5 * 6 * 7)
Donc, il suffit d'exécuter la boucle de 1 à r peut obtenir le résultat.