- //高精度除以低精度;
- //算法:按照从高位到低位的顺序,逐位相除。
- //在除到第j位时,该位在接受了来自第j+1位的余数后与除数相除,如果最高位为零,则商的长度减一。
- #include <stdio.h>
- #include <string.h>
- #define N 500
- int main()
- {
- int a[N] = {0}, c[N] = {0};
- int i, k, d=0, b;
- char a1[N];
- printf("Input 被除数:");
- scanf("%s", a1);
- printf("Input 除数:");
- scanf("%d", &b);
-
- k = strlen(a1);
- for(i = 0; i < k; i++)
- a[i] = a1[k - i - 1] - '0'; //将被除数倒序变为int型存放
-
- for(i = k - 1; i >= 0 ; i--)
- {
- d = d * 10 + a[i];
- c[i] = d / b;
- d = d % b;
- }
- while(c[k - 1] == 0 && k > 1) k--;
- printf("商=");
- for(i = k - 1; i >= 0; i--) printf("%d", c[i]);
- printf("\n余数=%d", d);
- return 0;
- }
《算法竞赛入门经典》
P28 2.4.2浮点数陷阱
- #include <stdio.h>
- int main ()
- {
- double i;
- for (i=0;i!=10;i+=0.1)
- printf("%.1lf\n",i);
- return 0;
- }
下面应用网上大神的分析:
浮点数陷阱:
该程序的运行结果是: 无限循环.
之所以无限循环,那么就可以推断是for循环的条件始终成立,即 i 始终不等于10.
但是 i 是从0开始的,每次都自加0.1.那么应该是100次后就停止.为什么i始终不
等于10呢? 是因为浮点数的原因. 我们把10改成10.0,结果仍然是无限循环.
我们把 i += 0.1 改成 i++,发现执行10次后正常停止.我们可以初步推断,是浮点数
的加法运算引起的.
接下来调用gdb输出中间结果来观察,发现 i 自加0.1后,并不是我们预想的等于0.1
而是等于 0.10000000000000001.
再往下执行几次,i 的值分别是0.20000000000000001.
0.30000000000000004.
0.40000000000000002.
这样,我们就理解了为什么i 始终不等于10. 因为浮点数在进行小数运算的时候由于
精度问题,会有很小的误差,然而用 = 或者 != 这样的运算符来比较,是会检测出这种
误差的.所以导致结果的不正确.
我们还可以多测试一下,将循环条件改为 i != 0.1 或者 i != 0.2时,程序能够正常
运行,得到正常结果.但是当i != 0.3时,就是无限循环.显然,在我们的程序中,这种
不确定的错误是不应该存在的.
因此,在定义循环变量时,尽量采用int型及整数的加减.因为循环的本质意义就是通过
各种条件来控制语句重复运行次数.而这个次数本身就是整数.要实现小数的功能尽量
通过循环中的语句来实现.
P32习题2-8
本题计算过程中需要计算1除以正整数m的平方,而m的取值是小于10的6次方
如果代码写作1/pow(m,2)会出现精度不足的问题
写作1/m/m则每一步计算都在浮点型精度内。
P32习题2-9
题设: 输入正整数a,b,c 输出a/b的小数形式,精确到小数点后c位。a,b小于等于10的6次方,c小于100.
本题先引用网上流传的解法,虽然错的比较离谱,但有些学习的价值
- #include<stdio.h>
- int main()
- {
- int a,b,c;
- scanf("%d%d%d",&a,&b,&c);
- printf("%.*lf",c,(double)a/b);
- return 0;
- }
- /* 分析: 该题间接地给我们提供了printf的一种很强大的使用方法,
- 即通过变量来控制精度.我们一般在使用printf进行格式控制的时候,都是按照
- 给定的精度进行控制,比如精确到小数点后三位.而使用变量来控制精度呢?
- 在printf中可以使用类似于通配符的*来实现.
- 符号*是用后面参数表给出的变量进行替代.因此,%.*lf可通过与*对应的变量
- 来控制小数点后的位数. */
上述解法的弊端在于当c较大,例如c=20,double型变量的精度不足。
下面我自己的提供两个思路
- //《算法竞赛入门经典》P32习题2-9“循环除”法 2012/7/19
- #include <stdio.h>
- #include <stdlib.h>
- int main ()
- {
- int a,b,c,i,temp;
- scanf("%d%d%d",&a,&b,&c);
- temp=a/b;
- printf("%d.",temp); //输出整数部分及小数点
- for (i=0;i<c;i++)
- {
- a=a%b*10;
- temp=a/b;
- printf("%d",temp);
- }
- system("pause");
- return 0;
- }
- //《算法竞赛入门经典》P32习题2-9 利用高精度除以低精度的算法
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- int main()
- {
- int a,b,c,k,i,temp,str1[120],str2[120];
- char ch[10];
- scanf("%d%d%d",&a,&b,&c);
- memset(str1,0,sizeof(int)*120); //将存储数组全部置0
- memset(str2,0,sizeof(int)*120);
- temp=a%b; //用temp存储余数部分
- itoa(temp,ch,10);
- k=strlen(ch); //用k记录余数长度
- for (i=0;ch[i]!=0;i++)
- str1[i]=ch[i]-'0';
- for (i=1,temp=str1[0];i<c+k;i++)
- {
- temp=temp*10+str1[i];
- str2[i-1]=temp/b;
- temp%=b;
- }
- while(str2[k-1]==0 && k>1) //排除多余的0
- k--;
- printf("%d.",a/b); //输出整数部分与小数点
- for (i=k-1;i<c+k-1;i++)
- printf("%d",str2[i]);
- printf("\n");
- system("pause");
- return 0;
- }
习题2-10
题目描述:用1,2,3,...,9组成3个三位数abc,def和ghi,每个数字恰好使用一次,要求abc:def:ghi=1:2:3.输出所有解。提示:不必太动脑筋。
本题在提供的每一个算法中都使用了计时函数,用于比较效率,结果第一种最慢,后两种因时间精度不足无法比较。
先提供网上流传的直接解法,看得我一阵腹痛。。。
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- int main ()
- {
- int a,b,c,d,e,f,g,h,i;
- for (a=1; a<=3; a=a+1)
- for (b=1; b<=9; b=b+1)
- if (b!=a)
- for (c=1; c<=9; c=c+1)
- if (c!=a && c!=b)
- for (d=a*2; d<=9; d=d+1)
- if (d!=a && d!=b && d!=c)
- for (e=1; e<=9; e=e+1)
- if (e!=a && e!=b && e!=c && e!=d)
- for (f=1; f<=9; f=f+1)
- if (f!=a && f!=b && f!=c && f!=d && f!=e)
- for (g=a*3; g<=9; g=g+1)
- if (g!=a && g!=b && g!=c && g!=d && g!=e && g!=f)
- for (h=1; h<=9; h=h+1)
- if (h!=a && h!=b && h!=c && h!=d && h!=e && h!=f && h!=g)
- for (i=1; i<=9; i=i+1)
- if (i!=a && i!=b && i!=c && i!=d && i!=e && i!=f && i!=g && i!=h &&
- (a*100+b*10+c)*2==d*100+e*10+f &&
- (a*100+b*10+c)*3==g*100+h*10+i)
- printf("%d%d%d, %d%d%d, %d%d%d\n",a,b,c,d,e,f,g,h,i);
- printf("Time used = %.3lf\n",(double)clock()/CLOCKS_PER_SEC);
- system("pause");
- return 0;
- }
下面列出根据不同思路写的两种算法
- /*1~9组成的最小三位数是123,最大的是987,由于要满足1:2:3的关系,最小的那个数应该不到于987/3=329。这样的话第一个数的变化范围是123~329,将这里面的数分别乘2、乘3,然后判断这三个数是否符合要求,即这三个数是否由1~9组成,而且各个数字不能相同。
- 即对每个数n(123<=n<=329)用枚举法。
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- int judge (int n);
- int main ()
- {
- int k,m,n,p,q;
- for(k=123;k<=329;k++)
- {
- m=2*k;
- n=3*k;
- p=k*1000+m;
- q=p*1000+n;
- if(judge(q)) //判断k,m,n是否符合要求。如果不符合就跳出本次循环,进入下次循环
- printf("%d,%d,%d\n",k,m,n);
- else continue;
- }
- printf("Time used = %.3lf\n",(double)clock()/CLOCKS_PER_SEC);
- system("pause");
- return 0;
- }
- int judge (int n)
- {
- int a[9],i,j;
- for (i=0;i<9;i++)
- {
- if ((a[i]=n%10)==0)
- return 0;
- n/=10;
- }
- for (i=0;i<9;i++)
- for (j=i+1;j<9;j++)
- if (a[i]==a[j])
- return 0;
- return 1;
- }
- //利用1-9的和为45,乘积为362880判断
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- int judge (int a,int b,int c);
- int main()
- {
- int i;
- for (i=111;i<329;i++)
- if (judge(i,2*i,3*i))
- printf("%d %d %d\n",i,2*i,3*i);
- printf("Time used = %.3lf\n",(double)clock()/CLOCKS_PER_SEC);
- system("pause");
- return 0;
- }
- int judge (int a,int b,int c)
- {
- int i,sum=0,pro=1,str[9];
- str[0] = a / 100;
- str[1] = a / 10 % 10;
- str[2] = a % 10;
- str[3] = b / 100;
- str[4] = b / 10 % 10;
- str[5] = b % 10;
- str[6] = c / 100;
- str[7] = c / 10 % 10;
- str[8] = c % 10;
- for (i=0;i<9;i++)
- {
- sum+=str[i];
- pro*=str[i];
- }
- if (sum==45 && pro==362880)
- return 1;
- else return 0;
- }