计算化学公社

 找回密码 Forget password
 注册 Register
Views: 3266|回复 Reply: 8
打印 Print 上一主题 Last thread 下一主题 Next thread

[C/C++] 计算组合数的程序问题

[复制链接 Copy URL]

609

帖子

2

威望

4351

eV
积分
5000

Level 6 (一方通行)

跳转到指定楼层 Go to specific reply
楼主

计算组合数C(n,m)。
从 n 个不同元素中每次取出 m 个不同元素
,不管其顺序合成一组,称为从 n 个元素中不重复地选取 m 个元素的一个组合。所有这样的组合的种数称为组合数。
如果你的程序使用int,则你的程序能正确计算的最大n是多少(此时对于所有的m,都是有效的)?


我编写的程序如下所示,编译没问题,但是总是运行不出结果,希望高手指点下。

#include"stdafx.h"
#include <iostream>
using namespace std;
int fac(int m, int k);
int main()
{
        int m, k;
        cout << "输入m,k:\n";
        cin >> m >> k;
        while (1){
                if (m<k){
                        cout << "m,k的取值不正确,请重新输入\n";
                        cin >> m >> k;
                }
                else break;
        }
        cout << "m的k的组合数为:" << fac(m, k) << endl;
        return 0;
}
int fac(int m, int k){
        if (m<k)return -1;
        int a = 1, b = 1, c = 1;
        for (int i = m; i >= 1; i--)
                a *= i;
        for (int j = k; j >= 1; j--)
                b *= j;
        for (int l = m - k; l >= 1; l--)
                c *= l;
        return a / (b*c);
}

1187

帖子

5

威望

2841

eV
积分
4129

Level 6 (一方通行)

2#
发表于 Post on 2019-9-20 21:54:25 | 只看该作者 Only view this author
fac这个名字还以为是阶乘...
公式您可以稍微优化一下啊,这个计算量太大了,而且显然有一部分相同的数被乘了一遍又除了一遍,太浪费了。
最容易想到的,一个是C(m, k) = A(m, k) / k! = m * (m-1) * ... * (m - k + 1) / k!,这样至少少算了两次(m-k)!。
另外C(m, k) = C(m, m-k),所以当2k>m的时候计算C(m, m-k)代替C(m, k)也可以明显降低计算量。

1187

帖子

5

威望

2841

eV
积分
4129

Level 6 (一方通行)

3#
发表于 Post on 2019-9-20 22:00:07 | 只看该作者 Only view this author
snljty 发表于 2019-9-20 21:54
fac这个名字还以为是阶乘...
公式您可以稍微优化一下啊,这个计算量太大了,而且显然有一部分相同的数被乘 ...

大多数编译器在主流电脑上int最大默认是2147483647,首先改成unsigned int就能大一倍,然后你算阶乘的话,13的阶乘就超过了2 ** 31 - 1,所以你这个写法m最大是12,不管k是多少,哪怕k=0,都算不了。

609

帖子

2

威望

4351

eV
积分
5000

Level 6 (一方通行)

4#
 楼主 Author| 发表于 Post on 2019-9-20 22:25:45 | 只看该作者 Only view this author
snljty 发表于 2019-9-20 22:00
大多数编译器在主流电脑上int最大默认是2147483647,首先改成unsigned int就能大一倍,然后你算阶乘的话 ...

明白,那您觉得这个源代码应该怎么修改呢?能否给个结果,多谢了!

120

帖子

0

威望

2562

eV
积分
2682

Level 5 (御坂)

5#
发表于 Post on 2019-9-21 00:45:58 | 只看该作者 Only view this author
本帖最后由 hxd_yi 于 2019-9-21 00:51 编辑

fac可以改为:
int fac(int m, int n){
    if (m<n)return 0;
    int ans=1;    if(n>m/2)n=m-n;
    for(int i=1;i<=n;++i)
        ans=ans*(n+1-i)/i;
    return ans;
}
这个比你原来的能计算更大的c(m,n)
嫌不够大,可以把int改成unsiged long long,到2^64-1呢!
再不够,上所谓的高精度计算,要多少位?




609

帖子

2

威望

4351

eV
积分
5000

Level 6 (一方通行)

6#
 楼主 Author| 发表于 Post on 2019-9-21 09:51:15 | 只看该作者 Only view this author
hxd_yi 发表于 2019-9-21 00:45
fac可以改为:
int fac(int m, int n){
    if (mm/2)n=m-n;

感觉将这个子程序修改为你的这段后,怎么运行结果都是1啊?

120

帖子

0

威望

2562

eV
积分
2682

Level 5 (御坂)

7#
发表于 Post on 2019-9-21 11:36:15 | 只看该作者 Only view this author
zyj19831206 发表于 2019-9-21 09:51
感觉将这个子程序修改为你的这段后,怎么运行结果都是1啊?

ans=ans*(n+1-i)/i;这一句打错了,应该为ans=ans*(m+1-i)/i;

609

帖子

2

威望

4351

eV
积分
5000

Level 6 (一方通行)

8#
 楼主 Author| 发表于 Post on 2019-9-21 16:52:21 | 只看该作者 Only view this author
hxd_yi 发表于 2019-9-21 11:36
ans=ans*(n+1-i)/i;这一句打错了,应该为ans=ans*(m+1-i)/i;

貌似是可以计算了,对于比较大的数有负值?

120

帖子

0

威望

2562

eV
积分
2682

Level 5 (御坂)

9#
发表于 Post on 2019-9-22 16:06:46 | 只看该作者 Only view this author
zyj19831206 发表于 2019-9-21 16:52
貌似是可以计算了,对于比较大的数有负值?

int型的溢出导致的,根据你最初的题意就是这个样子。

本版积分规则 Credits rule

手机版 Mobile version|北京科音自然科学研究中心 Beijing Kein Research Center for Natural Sciences|京公网安备 11010502035419号|计算化学公社 — 北京科音旗下高水平计算化学交流论坛 ( 京ICP备14038949号-1 )|网站地图

GMT+8, 2024-11-27 21:23 , Processed in 0.616315 second(s), 21 queries , Gzip On.

快速回复 返回顶部 返回列表 Return to list