C++中的组合数(n选择r) 这里我试着用C++编写一个程序来查找NCR。但是我在结果中遇到了一个问题。这是不对的。你能帮我找出程序中的错误吗 #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; }

C++中的组合数(n选择r) 这里我试着用C++编写一个程序来查找NCR。但是我在结果中遇到了一个问题。这是不对的。你能帮我找出程序中的错误吗 #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; },c++,combinatorics,binomial-coefficients,C++,Combinatorics,Binomial Coefficients,使用double代替int 更新: 你的公式也错了。您应该使用factn/factr/factn-r您的公式是完全错误的,它应该是factn/factr/factn-r,但这反过来又是一种非常低效的计算方法 请看,特别是我对这个问题的评论。哦,请重新打开这个问题,这样我才能正确地回答它 单个拆分案例实际上非常容易处理: unsigned nChoosek( unsigned n, unsigned k ) { if (k > n) return 0; if (k * 2 &

使用double代替int

更新:


你的公式也错了。您应该使用factn/factr/factn-r

您的公式是完全错误的,它应该是factn/factr/factn-r,但这反过来又是一种非常低效的计算方法

请看,特别是我对这个问题的评论。哦,请重新打开这个问题,这样我才能正确地回答它

单个拆分案例实际上非常容易处理:

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;
}
演示:

如果结果不合适,您可以计算对数之和,并以双精度不精确地获得组合数。或者使用任意精度的整数库

我在这里提出另一个密切相关的问题的解决方案,因为ideone.com最近一直在丢失代码片段,而另一个问题仍然没有新的答案

#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";
}
线路

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

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

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

实现n-choose-k的一个好方法是不基于阶乘,而是基于与阶乘密切相关的升积函数


上升的乘积m,n乘以m*m+1*m+2*..*n、 关于处理各种角落案例的规则,如n>=m或n这是为了在竞争性编程中解决nCr时不超过时间限制,我发布这篇文章,因为它将对你有所帮助,因为你已经得到了你问题的答案, 获得二项式系数的素因子分解可能是计算它的最有效的方法,尤其是在乘法很昂贵的情况下。这当然适用于计算阶乘的相关问题,例如参见

这里有一个简单的算法,它基于计算素数因子分解的埃拉托什尼筛。这个想法基本上是在你使用筛子找到素数的时候对它们进行检查,然后还计算它们的倍数中有多少落在[1,k]和[n-k+1,n]范围内。筛子本质上是一个On\log\log n算法,但没有进行乘法运算。一旦找到素因式分解,所需的实际乘法次数最多为O\left\frac{n\log\log n}{\log n}\right,可能还有比这更快的方法

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

N选择R的定义是计算两个乘积并将其中一个除以另一个

N*N-1*N-2*…*N-R+1/1*2*3*…*R

但是,乘法可能会变得太大,并且很快就会溢出现有的数据类型。实现技巧是将乘法和除法重新排序为

N/1*N-1/2*N-2/3*…*N-R+1/R

它保证在每一步的结果都能被n个连续数整除,其中一个必须能被n整除,这些数的乘积也是如此

例如,对于N选择3,N,N-1,N-2中的至少一个将是3的倍数,而对于N选择4,N,N-1,N-2,N-3中的至少一个将是4的倍数

下面给出了C++代码

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;
}

这里使用的递归函数不正确。事实功能应改为:

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.
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));
    }
};
})

递归调用应该在else部分进行

NCR功能应改为:

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.
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));
    }
};

你的公式是错误的,它在分子和分母中都有factn-1,它们被抵消了。你犯了一些与运算相同的错误。。。factn-r需要是一个除数,而不是一个乘法因子。是的!我不想检查公式,因为使用int也会产生错误的结果。即使使用double也不是一个很好的方法。试着计算6000,选择3。它很容易适合32位整数,但使用该公式和double将失败得很惨。。。我的意思是选择3个容易合身的。6000 choose 3实际上没有。请注意,我3年前在回答中提供的代码不仅没有使用动态列表增长,而且还使用因子分解来解决组合数问题。在语言中,我想这里的问题是python?res应该很长吗?只是在除法发生之前res*=n-k+1步溢出的情况下。读者请注意,nChoosek代码缺少n=k始终等于1的情况,请记住添加该条件。@IAmRoot看起来n=k的情况将由if k*2>nk=n-k处理;现在k为0,如果k==0,则返回1@正是这样。进入循环也不会造成任何伤害,只是没有理由,正如您所指出的,早期退出优化确实涵盖了这种情况。@IAmRoot从不使事情过于复杂。