教娃编程实录1:质数判断与分解质因数
求出100到200之间的所有质数
子任务1:如何判断一个整数是否为质数
为摸索出判断一个自然数是否为质数的算法,首先让娃用纸和笔去判断151是否为质数。她是这样做的:
151 ÷ 2 = 75......1
151 ÷ 3 = 50......1
151 ÷ 5 = 30......1
151 ÷ 7 = 21......4
151 ÷ 11 = 13......8
151 ÷ 13 = 11......8
至此,她做出判断:151是一个质数。问其故,答:试到除数大于商还不整除,就可以确定是质数了。
然其说,开始编码。
子任务2:编码与调试
经若干次提点、编译和调试,得到如下可运行代码:
1 #include <stdio.h> 2 3 int main() 4 { 5 int num = 99; 6 while (num < 200) 7 { 8 num = num + 1; 9 int d = 1; // divisor 10 int q = 0; // quotient 11 int r = 0; // remainder 12 while (true) 13 { 14 d = d + 1; 15 r = num % d; 16 q = num / d; 17 if (r == 0) 18 { 19 break; 20 } 21 if (d >= q) 22 { 23 printf("%d is a prime.\n", num); 24 break; 25 } 26 } // end of inner loop 27 } // end of outer loop 28 29 return 0; 30 }
运行得到符合预期的结果:
子任务3:把质数判断写成一个函数bool is_prime(int num)
稍加提点,经编译、调试完成is_prime函数以及改写的main函数:
1 bool is_prime(int num) 2 { 3 int d = 1; // divisor 4 int q = 0; // quotient 5 int r = 0; // remainder 6 while (true) 7 { 8 d = d + 1; 9 r = num % d; 10 q = num / d; 11 if (r == 0) 12 { 13 return false; 14 } 15 if (d >= q) 16 { 17 return true; 18 } 19 } 20 } 21 22 int main() 23 { 24 int x = 99; 25 while (x < 200) 26 { 27 x = x + 1; 28 if (is_prime(x)) 29 { 30 printf("%d is a prime.\n", x); 31 } 32 } 33 return 0; 34 }
运行结果和之前完全一致。
求出0到100之间的所有质数
娃这次反应很快,改了main函数开头两行代码里的两个数值:
int x = -1;
while (x < 100)
并表示肯定没问题。
运行结果的头几行如下:
1 is a prime.
3 is a prime.
5 is a prime.
...
问:可有什么问题?
答:哦,1搞错了。
还有什么搞错的吗?
没有了。
2呢?
2,哦,2也搞错了。
看看怎么出错的吧。
……
又经提点和尝试,得到如下没有错误的is_prime函数:
1 bool is_prime(int num) 2 { 3 if(num == 1) 4 { 5 return false; 6 } 7 if (num == 2) 8 { 9 return true; 10 } 11 int d = 1; // divisor 12 int q = 0; // quotient 13 int r = 0; // remainder 14 while (true) 15 { 16 d = d + 1; 17 r = num % d; 18 q = num / d; 19 if (r == 0) 20 { 21 return false; 22 } 23 if (d >= q) 24 { 25 return true; 26 } 27 } 28 }
对100到200之间的所有整数分解质因数
要求输出格式如下:
100 = 2 * 2 * 5 * 5
101 = 101
102 = 2 * 3 * 17
...
子任务1:分解质因数的算法
娃胸有成竹地说:还用判断质数的方法,碰到整除就得到num = d * q,再分别对d和q继续用同样的办法分解质因数。
我:这么说是没错的,而且前面除数逐次加一的尝试方法碰到整除时d一定是质数,下一步只需对q继续分解质因数。
子任务2:编码与调试
理解d一定会是质数后,娃开始尝试直接在main函数添加代码。尝试了一阵发现太难了。求提点。
提点1:写一个函数void factorize(int num),对传入的num分解质因数
提点2:对每行输出结果做拆解,开头的“num = ”还有末尾的换行在main里写代码来做,中间的质因数分解结果输出由factorize函数来做
很快main函数出来了:
1 int main() 2 { 3 int x = 99; 4 while (x < 200) 5 { 6 x = x + 1; 7 printf("%d = ", x); 8 factorize(x); 9 printf("\n"); 10 } 11 return 0; 12 }
factorize函数在提点和帮写中也出来了:
1 void factorize(int x) 2 { 3 int d = 1; 4 int q = 0; 5 int r = 0; 6 while (true) 7 { 8 d = d + 1; 9 q = x / d; 10 r = x % d; 11 if (r == 0) 12 { 13 printf("%d * ", d); 14 factorize(q); 15 return; 16 } 17 if (q < d) 18 { 19 printf("%d.", x); 20 return; 21 } 22 } 23 }
当然,作为初学者,娃对factorize内部有对factorize自身的调用难免会有一些困惑。在几番调试运行之下,慢慢也便懂得了这种称作递归的用法。
运行,节选输出结果如下:
100 = 2 * 2 * 5 * 5.
101 = 101.
102 = 2 * 3 * 17.
103 = 103.
104 = 2 * 2 * 2 * 13.
......
124 = 2 * 2 * 31.
125 = 5 * 5 * 5.
126 = 2 * 3 * 3 * 7.
127 = 127.
128 = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 1.
......
查看问题,发现128的分解结果里末尾多了“ * 1”。娃思考后判断问题出在factorize(2)。
提议把质因数分解的范围调整到1到9,修改mian函数里头两行代码,如下:
int x = 0;
while (x < 9)
运行,输出结果如下:
1 = 1.
2 = 2 * 1.
3 = 3.
4 = 2 * 2 * 1.
5 = 5.
6 = 2 * 3.
7 = 7.
8 = 2 * 2 * 2 * 1.
9 = 3 * 3.
验证了娃的判断,提点参看之前写is_prime函数碰到的缺陷和修改办法,很快找到了改正办法,即在factorize函数开头增加如下代码即可:
if (x == 2) { printf("%d.",x); return; }
子任务3:factorize(0)会怎么样
在main函数把第一行代码改为:
int x = -1;
重新运行,先是出现如下的结果输出:
过一阵,又会看到如下的窗口提示:
点击“中断”按钮,进到下图所示的调用堆栈:
简单跟娃解释一下代码在指定的地方(第84行)出现factorize(0)无限嵌套调用factorize(0)的问题,导致factorize(0)永远执行不完(无法走到第85行的代码),不必过多地解释堆栈溢出是怎么回事。
随后就是要如何解决factorize(0)走不到出口的问题,办法很简单,把前面对付factorize(2)的缺陷的代码稍作修改即可:
if (x == 2 || x == 0) { printf("%d.",x); return; }
课后作业
1、factorize函数里的if (q < d)是否可以换成if (q <= d),为什么?
2、factorize函数对350分解质因数的过程中,在找出2和5两个质因数后,对最后的35分解质因数时,还是会从除数2开始尝试能否整除,有没有改进的办法,如何改进?
提点:增加一个参数
3、如何实现一个求阶乘值的函数