4.1 相关程序

即使最终答案在所选择的数据类型范围之内 ,计算的中间结果仍然可能溢出
eg:

点击查看代码
long long fac(int n)
{
  long long m = 1;
  for(int i = 1; i <= n; i++)
    m *= i;
  return m;
}

这段代码在n = 21的时候就会出现数据溢出,这是一个隐藏的bug需要注意
这个题目还说明:即使认为题目在按时你使用某种语言特性,也应该深入分析,不能贸然行事,即使最终答案在所选择的数据类型范围之内,计算的中间结果仍然可能溢出

修改后通过约分来减小中间数据的大小
FlnButFly's Version

点击查看代码
#include<stdio.h>
long long C(int n, int m)
{
	long long ans = 1;
	if(m > n - m)
	{
		for(int i = n; i > m; i--)
			ans *= i;
		for(int i = 1; i <= m-n; i++)
			ans /= i;
	}
	else
	{
		for(int i = n; i > n-m; i--)
			ans *= i;
		for(int i = 1; i <= m; i++)
			ans /= i;
	}
	return ans;
}

int main()
{
	printf("%lld\n", C(21,1));
	return 0; 
}
点击查看源代码
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;实质上是将m和n - m的数值互换了,这样可以简便代码,也就是说这是类似一种封装的思想,因为我们约分的时候一定找的是最大的数去约分,这样才能尽量避免计算式的数据溢出所以我们只要确定最大的数,然后按照顺序填进去就可以了,也就是说这里的m和m-n是可以互换的,并不一定就一定是彼此
对复杂的表达式进行化简有时不仅能减少计算量,还能减少甚至避免中间结果溢出
如果怕太大有时候甚至可以取对数来消去他的增长速度,参考第三章最后一题

编写程序判断一个数是否为素数,因数只有1和自己本身的大于1的数称为素数,这边最先想到的就是模拟,程序如下:

点击查看代码
#include<stdio.h>

int isPrime(int num)
{
	for(int i = 2; i < (num + 1) /2 + 1; i++)
		if(num % i == 0)
			return 1;
	return 0; 
}

int main()
{
	int num;
	scanf("%d", &num);
	if(isPrime(num))
		printf("No\n");
	else
		printf("Yes\n");	
	return 0;	
} 

当然自己编写的程序还不够好,只判断不超过sqrt(num)的函数是更好的,也就是i*i<=num;因为如果这之前的都不行那后面肯定也没有这个数的其他因数了
假设num = a * b;
如果a大则b小,其中最极限的情况就是a == b;
判断一个事物是否具有某一性质的函数有一个学术名称--谓词(predicate)
尽可能的让自己的函数名的选取有规律可循,空格可以用下划线来代替
建议把谓词(用来判断某事物是否具有某种特性的函数)命名成“is_xxx”的形式,返回int值,非0表示真,0表示假
这个函数的缺陷也很明显,正常情况下如果num=1的时候,其返回值是Yes不符合素数的定义,如果要解决需要增加一个特殊判断,同时如果n太大的时候可能会造成数据的溢出,此时数据为负值,仍能够符合i * i < n,但是很明显在逻辑上是错误的
编写函数的时候,应尽量保证该函数能对任何合法参数得到正确的结果。如若不然,应在显著位置标明函数的额缺陷,以避免误用
修改后的程序代码:

点击查看代码
#include<stdio.h>
#include<math.h>

int isPrime(int num)
{
	if(num <= 1)
		return 0;
	int m = floor(sqrt(m) + 0.5);
	for(int i = 2; i < m + 1; i++)
		if(num % i == 0)
			return 0;
	return 1; 
}

int main()
{
	int num;
	scanf("%d", &num);
	if(isPrime(num))
		printf("Yes\n");
	else
		printf("No\n");	
	return 0;	
} 

以上的代码特判了n<=1的情况,还是用了变量m,一方面避免了重复计算sqrt(num),重复计算带来的问题前面的strlen已经体现了,会拖慢程序的运行效率,注意floor(x + 0.5)是四舍五入的方法,尽量避免浮点误差,也就是就算sqrt将某个整数的值编程xxx.9999也将被修正,否则可能会导致程序的错误,丢失精度
当然上述的程序只是简单的模拟,其效率是低下的,在数据很大的时候需要运行很长的时间才能得出结论

posted @   banyanrong  阅读(52)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示