当前位置:文档之家› C语言程序设计 数组(8.2.4)--使用循环和数组代替递归

C语言程序设计 数组(8.2.4)--使用循环和数组代替递归

代码 4: #include <stdio.h>
int main() {
int f[10001] = {0, 1, 2, 3}; int n, m, i; while(scanf("%d%d", &n, &m) == 2) {
if(n == 0 && m == 0) {
break; } else if(n >= 0) {
printf("%d\n", f[n] % m); } return 0; }
再输入 n=10000,m=999 时,就能够获得 433 这一正确的结果。大功告成! 但是很不幸,程序仍然有错!这是什么原因呢?进一步仔细思考,发现前面的 程序都没有考虑 n 为负数的情况,依题意当 n 为负数时,f(n)的值为 n,此时负 数 f(n)对 m 求余时,按 C 语言求余运算得到的结果为使商尽可能大的负余数, 例如因为-7=(-2)*3+(-1),商是-2,余数是-1,所以(-7)%3=-1。依题意,我们需要 求的是使商尽可能小的正余数,例如将-7 表示为-7=(-3)*3+2,商是-3,余数是 2,所以(-7)%3=2。此时需要使用公式(m - (-n) % m) % m 获得题目要求的负数的 正余数。因此,最终代码修改为:
再回过头来仔细理解题目,我们最终需要获得的并非 f[n]的具体值,而是 f[n] % m 的结果,我们会发现, f[n] % m 与(f[n-1] % m + f[n-3] % m) % m 的值 是一样的,而后者就不会出现溢出的情况。因此,代码进一步修改为:
代码 3: #include <stdint f[10001] = {0, 1, 2, 3}; int n, m, i; while(scanf("%d%d", &n, &m) == 2) {
if(n == 0 && m == 0) {
break; } for(i = 4; i <= n; i++) {
f[i] = f[i - 1]%m + f[i - 3]%m; }
代码 2: #include <stdio.h>
int main()
{ int f[10001] = {0, 1, 2, 3}; int n, m, i; while(scanf("%d%d", &n, &m) == 2) { if(n == 0 && m == 0) { break; } for(i = 4; i <= n; i++) { f[i] = f[i - 1] + f[i - 3]; } printf("%d\n", f[n] % m); } return 0;
教 学 案 例 — 递 归 (Recursive)
【任务描述】
已知 f(n)的值由下面的递归公式定义: f(n) = f(n-1) + f(n-3), n > 3 f(n) = n, n <= 3 写出一个程序,计算 f(n) % m 的值(使商尽可能小的正余数)。其中: n <= 10000, 2 <= m <= 10000. 输入:
此教学案例包括的知识点: 1、 递归函数在处理层级较多的递归任务时,并不合适; 2、 可以使用循环和数组代替递归函数; 3、 数据过大时,会出现溢出的情况; 4、 如果掌握余数的性质,能够避免溢出; 5、 负数求正余数的方法;
至此,绝大部分学生都会认为已经圆满的解决了该问题,但是还会有部分 爱动脑筋、善于思考的学生会追问,这就是最优解了么?还有没有更快的办法呢 答案是确定的,以上的代码还能够进一步优化,从而加快运行速度。如对于 f 数 组,仅需要计算一次,以后再需要计算 f(n)时,直接从中取第 n 个元素即可。这 就省去了重新计算的时间,这种思想也是今后要学习的动态规划算法的本质。具 体代码,就留给感兴趣的学生自己编写了。
}
该代码执行速度极快,但是通过对执行结果的观察,发现当输入的 n 较小 时,例如 1,程序能够获得正确的输出结果。但是当输入较大的 n,例如当 n=10000,m=999 时,输出结果却为-657,显然出现了错误。这是什么原因呢? 这是由于当 n 较大时,f[n]的结果会变得很大,直至出现溢出的情况,即使将 f[n]的类型定义为 long,问题仍然不能够得到有效解决。
for(i = 4; i <= n; i++) {
f[i] = f[i - 1]%m + f[i - 3]%m; } printf("%d\n", f[n] % m); } else { printf("%d\n", (m - (-n) % m) % m); } } return 0; }
【教学指导】
代码 1: #include <stdio.h>
int f(int n) {
if(n <= 3) {
return n; }
return f(n-1) + f(n-3); }
int main() {
int n, m; while(scanf("%d%d", &n, &m) == 2) {
if(n == 0 && m == 0) {
有多组测试样例,每个样例包括两个整数 n 和 m。n = 0 和 m = 0 表示输入 结束,无需处理。(注:本题为 ACM 竞赛试题) 输出:
对于每个样例,打印 f(n) % m 的值。 输入样例: 12 10000 999 -7 3 00 输出样例: 1 433 2
【任务分析】
该题目初看就是一个非常典型的递归问题,使用课堂上介绍的递归函数, 能够非常容易的写出如下代码:
break; } printf("%d\n", f(n) % m); } return 0; }
该代码在输入较小的 n 时,能够获得正确的结果,同时运行时间也非常短。 然而,当输入的 n 较大时,会出现程序运行时间过长的问题。仔细思考造成这一 问题的原因,可见随着 n 的增大,递归调用的级数会成指数增长,必然会造成 运算速度慢,同时内存消耗大的问题。这也是递归函数在实际应用中的缺点。知 道了问题所在,将程序进行修改,使用循环和数组代替递归,并写出如下代码:
相关主题