web-dev-qa-db-fra.com

Nombre de combinaisons (N choisissez R) en C++

Ici, j'essaie d'écrire un programme en C++ pour trouver NCR. Mais j'ai un problème dans le résultat. Ce n'est pas correct. Pouvez-vous m'aider à trouver quelle est l'erreur dans le programme?

#include <iostream>
using namespace std;
int fact(int n){
    if(n==0) return 1;
    if (n>0) return n*fact(n-1);
};

int NCR(int n,int r){
    if(n==r) return 1;
    if (r==0&&n!=0) return 1;
    else return (n*fact(n-1))/fact(n-1)*fact(n-r);
};

int main(){
    int n;  //cout<<"Enter A Digit for n";
    cin>>n;
    int r;
         //cout<<"Enter A Digit for r";
    cin>>r;
    int result=NCR(n,r);
    cout<<result;
    return 0;
}
8
Hams

Votre formule est totalement fausse, elle est supposée être fact(n)/fact(r)/fact(n-r), mais c'est à son tour une manière très inefficace de la calculer.

Voir Calcul rapide du nombre de combinaisons de plusieurs catégories et plus particulièrement de mes commentaires sur cette question. (Oh, et s'il vous plaît rouvrez cette question afin que je puisse y répondre correctement)

Le cas single-split est en réalité très facile à manipuler:

unsigned nChoosek( unsigned n, unsigned k )
{
    if (k > n) return 0;
    if (k * 2 > n) k = n-k;
    if (k == 0) return 1;

    int result = n;
    for( int i = 2; i <= k; ++i ) {
        result *= (n-i+1);
        result /= i;
    }
    return result;
}

Démo: http://ideone.com/aDJXNO

Si le résultat ne correspond pas, vous pouvez calculer la somme des logarithmes et obtenir le nombre de combinaisons inexactement comme un double. Ou utilisez une bibliothèque de nombres entiers de précision arbitraire.


Je pose ma solution à une autre question étroitement liée ici, car ideone.com perd des extraits de code récemment, et l’autre question reste fermée aux nouvelles réponses.

#include <utility>
#include <vector>

std::vector< std::pair<int, int> > factor_table;
void fill_sieve( int n )
{
    factor_table.resize(n+1);
    for( int i = 1; i <= n; ++i )
        factor_table[i] = std::pair<int, int>(i, 1);
    for( int j = 2, j2 = 4; j2 <= n; (j2 += j), (j2 += ++j) ) {
        if (factor_table[j].second == 1) {
            int i = j;
            int ij = j2;
            while (ij <= n) {
                factor_table[ij] = std::pair<int, int>(j, i);
                ++i;
                ij += j;
            }
        }
    }
}

std::vector<unsigned> powers;

template<int dir>
void factor( int num )
{
    while (num != 1) {
        powers[factor_table[num].first] += dir;
        num = factor_table[num].second;
    }
}

template<unsigned N>
void calc_combinations(unsigned (&bin_sizes)[N])
{
    using std::swap;

    powers.resize(0);
    if (N < 2) return;

    unsigned& largest = bin_sizes[0];
    size_t sum = largest;
    for( int bin = 1; bin < N; ++bin ) {
        unsigned& this_bin = bin_sizes[bin];
        sum += this_bin;
        if (this_bin > largest) swap(this_bin, largest);
    }
    fill_sieve(sum);

    powers.resize(sum+1);
    for( unsigned i = largest + 1; i <= sum; ++i ) factor<+1>(i);
    for( unsigned bin = 1; bin < N; ++bin )
        for( unsigned j = 2; j <= bin_sizes[bin]; ++j ) factor<-1>(j);
}

#include <iostream>
#include <cmath>
int main(void)
{
    unsigned bin_sizes[] = { 8, 1, 18, 19, 10, 10, 7, 18, 7, 2, 16, 8, 5, 8, 2, 3, 19, 19, 12, 1, 5, 7, 16, 0, 1, 3, 13, 15, 13, 9, 11, 6, 15, 4, 14, 4, 7, 13, 16, 2, 19, 16, 10, 9, 9, 6, 10, 10, 16, 16 };
    calc_combinations(bin_sizes);
    char* sep = "";
    for( unsigned i = 0; i < powers.size(); ++i ) {
        if (powers[i]) {
            std::cout << sep << i;
            sep = " * ";
            if (powers[i] > 1)
                std::cout << "**" << powers[i];
        }
    }
    std::cout << "\n\n";
}
20
Ben Voigt

La définition de N choisit R consiste à calculer les deux produits et à les diviser l’un avec l’autre,

(N * N-1 * N-2 * ... * N-R + 1)/(1 * 2 * 3 * ... * R)

Cependant, les multiplications peuvent devenir vraiment trop grandes et déborder du type de données existant. L'astuce de mise en œuvre consiste à réorganiser la multiplication et les divisions de la manière suivante:

(N)/1 * (N-1)/2 * (N-2)/3 * ... * (N-R + 1)/R

Il est garanti qu'à chaque étape, les résultats sont divisibles (pour n nombres continus, l'un d'entre eux doit être divisible par n, le produit de ces nombres l'est également).

Par exemple, pour N choisissez 3, au moins un des N, N-1, N-2 sera un multiple de 3, et pour N, choisissez 4, au moins un de N, N-1, N-2, N -3 sera un multiple de 4.

Code C++ donné ci-dessous. 

int NCR(int n, int r)
{
    if (r == 0) return 1;

    /*
     Extra computation saving for large R,
     using property:
     N choose R = N choose (N-R)
    */
    if (r > n / 2) return NCR(n, n - r); 

    long res = 1; 

    for (int k = 1; k <= r; ++k)
    {
        res *= n - k + 1;
        res /= k;
    }

    return res;
}
4
Steven

Une bonne façon de mettre en œuvre n-Choose-k est de ne pas le baser sur la factorielle, mais sur une fonction de "produit en hausse" étroitement liée à la factorielle.

Le produit_augmentation (m, n) se multiplie m * (m + 1) * (m + 2) * ... * n, avec des règles de traitement de divers cas de coin, comme n> = m ou n <= 1:

Voir ici pour une implémentation nCk ainsi que nPk en tant que fonctions intrinsèques dans un langage de programmation interprété écrit en C:

static val rising_product(val m, val n)
{
  val acc;

  if (lt(n, one))
    return one;

  if (ge(m, n))
    return one;

  if (lt(m, one))
    m = one;

  acc = m;

  m = plus(m, one);

  while (le(m, n)) {
    acc = mul(acc, m);
    m = plus(m, one);
  }

  return acc;
}

val n_choose_k(val n, val k)
{
  val top = rising_product(plus(minus(n, k), one), n);
  val bottom = rising_product(one, k);
  return trunc(top, bottom);
}

val n_perm_k(val n, val k)
{
  return rising_product(plus(minus(n, k), one), n);
}

Ce code n'utilise pas d'opérateurs tels que + et < car il est de type générique (le type val représente une valeur de tout type, telle que divers types de nombres comprenant des entiers "bignum") et parce qu'il est écrit en C (pas de surcharge) , et parce que c'est la base d'un langage semblable à LISP qui n'a pas de syntaxe infixe.

Malgré cela, cette implémentation n-chois-k a une structure simple, facile à suivre.

Légende: le: inférieur ou égal; ge: supérieur ou égal; trunc: division tronquante; plus: addition, mul: multiplication, one: une constante typée val pour le premier chiffre.

3
Kaz

Utilisez double au lieu de int

METTRE À JOUR:  

Votre formule est également fausse. Vous devriez utiliser fact(n)/fact(r)/fact(n-r)

1
Pulkit Goyal

la ligne

else return (n*fact(n-1))/fact(n-1)*fact(n-r);

devrait être

else return (n*fact(n-1))/(fact(r)*fact(n-r));

ou même

else return fact(n)/(fact(r)*fact(n-r));
1
Korchkidu

La fonction récursive est utilisée incorrectement ici. La fonction fact() devrait être remplacée par ceci:

int fact(int n){
if(n==0||n==1) //factorial of both 0 and 1 is 1. Base case.
{
    return 1;
}else

    return (n*fact(n-1));//recursive call.

};

L'appel récursif doit être fait dans la partie else.

La fonction NCR() devrait être remplacée par ceci:

int NCR(int n,int r){
    if(n==r) {
        return 1;
    } else if (r==0&&n!=0) {
        return 1;
    } else if(r==1)
    {
        return n;
    }
    else
    {
        return fact(n)/(fact(r)*fact(n-r));
    }
};
0
Elyor

ceci est pour référence afin de ne pas dépasser la limite de temps lors de la résolution de nCr dans une programmation compétitive, je le publie car il vous sera utile car vous avez déjà obtenu la réponse à votre question, Obtenir la factorisation première du binôme Le coefficient est probablement le moyen le plus efficace de le calculer, surtout si la multiplication est coûteuse. Ceci est certainement vrai du problème connexe du calcul factoriel (voir Cliquez ici par exemple).

Voici un algorithme simple basé sur le Sieve of Eratosthenes qui calcule la factorisation principale. L'idée est essentiellement de passer par les nombres premiers que vous les trouvez en utilisant le tamis, mais aussi de calculer combien de leurs multiples tombent dans les intervalles [1, k] et [n-k + 1, n]. Le tamis est essentiellement un algorithme O (n\log\log n), mais aucune multiplication n’est effectuée. Le nombre réel de multiplications nécessaires une fois la factorisation première trouvée est au pire O\left (\ frac {n\log\log n} {\ log n}\right) et il existe probablement des méthodes plus rapides que cela.

prime_factors = []

n = 20
k = 10

composite = [True] * 2 + [False] * n

for p in xrange(n + 1):
if composite[p]:
    continue

q = p
m = 1
total_prime_power = 0
prime_power = [0] * (n + 1)

while True:

    prime_power[q] = prime_power[m] + 1
    r = q

    if q <= k:
        total_prime_power -= prime_power[q]

    if q > n - k:
        total_prime_power += prime_power[q]

    m += 1
    q += p

    if q > n:
        break

    composite[q] = True

prime_factors.append([p, total_prime_power])

 print prime_factors
0
user4027538