算法题中实现计算排列数与组合数

知识背景

排列数公式: A n m = n × ( n − 1 ) × . . . × ( n − m + 1 ) = n ! ( n − m ) ! A_n^m = n×(n-1)×...×(n-m+1) = \frac{n!}{(n-m)!} Anm=n×(n1)×...×(nm+1)=(nm)!n!

组合数公式: C n m = n × ( n − 1 ) × . . . × ( n − m + 1 ) m × ( m − 1 ) × . . . × 1 = n ! m !   ( n − m ) ! C_n^m = \frac{n×(n-1)×...×(n-m+1)}{m×(m-1)×...×1} = \frac{n!}{m!\space(n-m)!} Cnm=m×(m1)×...×1n×(n1)×...×(nm+1)=m! (nm)!n!

代码实现

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

ll A (int n, int m) { 
	ll res = 1;
	for (int i = 0;i < m;i ++) 
		res *= n - i;
	return res;
}

ll C (int n, int m) {
	ll res = 1;
	for (int i = 0;i < m;i ++) {
		res *= n - i;
		res /= i + 1;
	}
	return res;
}

int main()
{
	int n, m;
	
	cin >> n >> m;
	
	cout << A (n, m) << endl << C (n, m) << endl;
		
	return 0;
}

如果题目要求取模,则在乘法进行时进行取模操作即可。

思考 C n m C_n^m Cnm的实现

显然,如果我们要是将 C n m C_n^m Cnm的核心代码改写成:

for (int i = 0;i < m;i ++) {
	res *= n - i;
	res /= m - i;
}

改写后,我们计算的方式为:n/m × (n-1)/(m-1) × ... × (n-m+1)/1,由于这其中的除法为整除,但是我们并不能保证(n-i)/(m-i)一定能够整除,所以这大概率会影响结果,导致结果错误。

而正确的计算方式就是“代码实现”中给出的:n/1 × (n-1)/2 × (n-3)/3 × ... × (n-m+1)/m

这样计算之所以正确是因为连续的i个数中必然存在能整除i的数,比如,三个数1、2、3中存在3可以被3整除,三个数5、6、7中存在6可以被3整除……

也就是说,第一项n必然可以被1整除,前两项n × (n-1)也可以被2整除,前三项n × (n-1) × (n-2)可以被3整除……

按照这种计算方式完全可以保证没有精度的损失,每次都是整除。

这就是为什么我们计算组合数时,分子和分母一个正序乘一个逆序乘的原因。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不牌不改

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值