61 votes

Combinatoire "N choisit R" en java math ?

Existe-t-il une méthode intégrée dans une bibliothèque java qui permet de calculer 'N choisir R' pour tout N, R ?

7 votes

Que se passe-t-il si le résultat déborde d'un int ? Est-ce important ? Voulez-vous que le résultat soit un BigInteger ?

2 votes

J'essaie simplement de calculer le nombre de combinaisons de 2 cartes avec différentes tailles de jeux (jusqu'à 52), ce qui ne devrait pas dépasser 1 326 (52 choisit 2).

13 votes

Vous avez réalisé que 52 ! est 806581751709438785716606368564037669752895054408832778240000‌​00000000 ? Parce qu'à en juger par la réponse que vous avez acceptée, vous ne semblez pas avoir pensé à la taille des nombres impliqués dans cette formule. Au fait, la réponse pour deux cartes est (n*(n-1))/2. Vous n'avez pas besoin d'une implémentation complète de 'n choose r' pour l'obtenir.

105voto

polygenelubricants Points 136838

La formule

C'est en fait très facile à calculer N choose K sans même calculer les factorielles.

Nous savons que la formule pour (N choose K) est :

    N!
 --------
 (N-K)!K!

Par conséquent, la formule pour (N choose K+1) est :

       N!                N!                   N!               N!      (N-K)
---------------- = --------------- = -------------------- = -------- x -----
(N-(K+1))!(K+1)!   (N-K-1)! (K+1)!   (N-K)!/(N-K) K!(K+1)   (N-K)!K!   (K+1)

C'est-à-dire :

(N choose K+1) = (N choose K) * (N-K)/(K+1)

Nous savons également que (N choose 0) est :

 N!
---- = 1
N!0!

Cela nous donne donc un point de départ facile, et en utilisant la formule ci-dessus, nous pouvons trouver (N choose K) pour tout K > 0 con K les multiplications et K divisions.


Triangle de Pascal facile

En combinant les éléments ci-dessus, nous pouvons facilement générer le triangle de Pascal comme suit :

    for (int n = 0; n < 10; n++) {
        int nCk = 1;
        for (int k = 0; k <= n; k++) {
            System.out.print(nCk + " ");
            nCk = nCk * (n-k) / (k+1);
        }
        System.out.println();
    }

Cette empreinte :

1 
1 1 
1 2 1 
1 3 3 1 
1 4 6 4 1 
1 5 10 10 5 1 
1 6 15 20 15 6 1 
1 7 21 35 35 21 7 1 
1 8 28 56 70 56 28 8 1 
1 9 36 84 126 126 84 36 9 1 

BigInteger version

En appliquant la formule pour BigInteger est simple :

static BigInteger binomial(final int N, final int K) {
    BigInteger ret = BigInteger.ONE;
    for (int k = 0; k < K; k++) {
        ret = ret.multiply(BigInteger.valueOf(N-k))
                 .divide(BigInteger.valueOf(k+1));
    }
    return ret;
}

//...
System.out.println(binomial(133, 71));
// prints "555687036928510235891585199545206017600"

Selon Google, 133 choisir 71 = 5,55687037 × 10 38 .


Références

1 votes

J'ai remarqué un grand gain de performance en utilisant cette approche (par rapport au calcul récursif).

0 votes

Votre solution pour imprimer le triangle de Pascal ne fonctionne pas pour les grands nombres. Prenons l'exemple de 30.

3 votes

Une petite note d'implémentation sur votre fonction binomiale : Au lieu d'itérer jusqu'à K, vous pouvez itérer jusqu'à min(N-K, K).

49voto

theomega Points 8874

Le module "Math" d'apache-commons prend en charge cette fonction en org.apache.commons.math4.util.CombinatoricsUtils

6 votes

Aucun des liens précédents ne fonctionne. Lol ! Les responsables d'Apache devraient s'occuper de rediriger leurs anciens liens vers des informations actualisées.

1 votes

24voto

dimo414 Points 7128

El définition récursive vous donne une fonction de choix assez simple qui fonctionnera bien pour les petites valeurs. Si vous prévoyez d'utiliser cette méthode souvent, ou sur de grandes valeurs, il serait utile de la mémoriser, mais sinon, elle fonctionne très bien.

public static long choose(long total, long choose){
    if(total < choose)
        return 0;
    if(choose == 0 || choose == total)
        return 1;
    return choose(total-1,choose-1)+choose(total-1,choose);
}

L'amélioration de la durée d'exécution de cette fonction est laissée à l'appréciation de la Commission. exercice pour le lecteur :)

0 votes

Je déteste pinailler, mais je suis légitimement curieux de savoir pourquoi je viens d'être rétrogradé pour cette réponse. Certes, la réponse de polygenelubricants est beaucoup plus détaillée, mais j'ai utilisé cette fonction de nombreuses fois sans problème, y a-t-il une erreur quelconque ?

3 votes

Je n'ai pas voté contre vous, mais votre algorithme a choisi 2*C(n,k)-1 fois. C'est-à-dire qu'en calculant choose(10,5) il fera 503 appels récursifs (2*C(10,5)-1 = 2*252-1 = 504-1). Il n'a donc aucun espoir de calculer C(40,20), qui représente environ 138 milliards.

0 votes

Je suis d'accord, il en va de même pour une implémentation de base de Fibonacci, ou pour tout autre algorithme qui bénéficie de la programmation dynamique. Pensez-vous que ma réponse (en particulier la suggestion de mémoriser la méthode) ne couvre pas suffisamment cela ?

15voto

J'essaie simplement de calculer le nombre de combinaisons de 2 cartes avec différentes tailles de jeux...

Pas besoin d'importer une bibliothèque externe - dès la définition de la combinaison, avec n cartes qui seraient n*(n-1)/2

Question bonus : Cette même formule permet de calculer la somme du premier n-1 les entiers - vous voyez pourquoi ils sont identiques ? :)

2 votes

(Réponse à la question bonus, un an plus tard : Il y a n-1 façons d'associer la première carte à une autre carte, plus n-2 des façons de jumeler la deuxième carte avec les cartes restantes, etc.)

2voto

Aistina Points 6720

La formule mathématique pour cela est la suivante :

N!/((R!)(N-R)!)

Cela ne devrait pas être difficile de comprendre à partir de là :)

26 votes

Oui, il devrait. Vous ne voulez pas calculer d'énormes factorielles si vous êtes limité par la taille d'un fichier de type int par exemple.

0 votes

@jleedev : Comme si la factorisation contre cela était si difficile en premier lieu :P

0 votes

Vous pouvez effectuer une factorisation pour nettoyer un peu le résultat, mais cette définition donne toujours des nombres beaucoup plus grands que ceux que vous voulez utiliser. Pour obtenir les meilleurs résultats, un algorithme de choix ne devrait jamais calculer un nombre plus grand que le résultat - ces nombres deviennent assez vite grands comme ça.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X