一、计算组合数。编写函数,参数是两个非负整数n和m,返回组合数 ,其 中m≤n≤25。例如,n=25,m=12时答案为5200300。
【分析】 既然题目中的公式多次出现n!,将其作为一个函数编写是比较合理的:
long long factorial(int n){
long long m = 1;
for(int i = 1; i <= n; i++) m *= i;
return m; }
long long C(int n, int m) {
return factorial(n)/(factorial(m)*factorial(n-m)); }
提示:即使最终答案在所选择的数据类型范围之内,计算的中间结果仍然可能溢出。
当n=21,m=1的返回值是-1,溢出。
解决方法:约分。
一个简单的方法是利用n!/m!=(m+1) (m+2)…(n-1)n。虽然不能完全避免中间结果溢出,但是对于题目给出的范围已经可以保证得到正确的结果了。
long long C(int n, int m) {
if(m < n-m) m = n-m;
long long ans = 1;
for(int i = m+1; i <= n; i++) ans *= i;
for(int i = 1; i <= n-m; i++) ans /= i;
return ans; }
小技巧:当m<n-m时把m变成n-m。作用:进一步降低复杂度。
二、素数判定。编写函数,参数是一个正整数n,如果它是素数,返回1,否则返回0。
【分析】 根据定义,被1和它自身整除的、大于1的整数称为素数。
//n=1或者n太大时请勿调用
int is_prime(int n) {
for(int i = 2; i*i <= n; i++)
if(n % i == 0) return 0;
return 1; }
小技巧:注意这里用到了两个小技巧。一是只判断不超过sqrt(x)的整数i。二 是及时退出:一旦发现x有一个大于1的因子,立刻返回0(假),只有最后才返回1(真)。 函数名的选取是有章可循的,“is_prime”取自英文“is it a prime?”(它是素数吗?)。
注意代码上方提示:太小是考虑 1 ,太大是考虑i*i会溢出。
改进版:
int is_prime(int n) {
if(n <= 1) return 0;
int m = floor(sqrt(n) + 0.5);
for(int i = 2; i <= m; i++)
if(n % i == 0) return 0;
return 1; }
除了特判n≤1的情况外,程序中还使用了变量m,一方面避免了每次重复计算sqrt(n),另 一方面也通过四舍五入避免了浮点误差——正如前面所说,如果sqrt将某个本应是整数的值 变成了xxx.99999,也将被修正,但若直接写m = sqrt(n),“.99999”会被直接截掉。
注:为什么只需要计算到开平方就可以?