循环 —— C语言程序设计(三)
2021/8/5 20:38:45
本文主要是介绍循环 —— C语言程序设计(三),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
C语言程序设计(三)
文章目录
- C语言程序设计(三)
- 循环
- while循环
- do-while循环
- 循环应用1
- 循环计算
- 计数循环
- 猜数游戏
- 拓展: 随机数
- 算平均数
- 整数分解
- 应用:数的逆序
- 第三种循环
- for循环
- for和while对比
- 循环次数
- 循环控制
- 素数判断
- break 和 continue
- 循环的嵌套
- 凑硬币
- 循环应用2
- 求和
- 正序分解整数
- 最大公约数
- 枚举
- 辗转相除法
循环
while循环
- 如果我们把while翻译作"当",那么一个while循环的意思就是:当条件满足时,不断地重复循环体内的语句。(即先判断后循环)
- 循环执行之前判断是否继续执行循环,所以有可能循环一次也没有执行(即条件不成立不进入循环);
- 条件成立是循环继续的条件。
while(<循环条件>){ <循环体语句> }
tips:循环就意味着它要考虑到更多的情况是否在循环内进行,所以要进行验证;但人工验证的同时不可能全部都试一遍,因此要从一般到特殊,尽可能的包含到。测试程序常使用边界数据,如有效范围两端的数据、特殊的倍数等。
do-while循环
do { <循环体语句> }while(<循环条件>);
- 在进入循环的时候不做检查,而是在执行完一轮循环体的代码之后,再来检查循环的条件是否满足,如果满足则继续下一轮循环,不满足则循环结束。(即先循环后判断)
- 注意: do-while 循环的 while 循环后面要有分号
;
来结束语句。
区别两种循环:do-while 循环和 while 循环很像,区别在于 do-while 循环的循环体执行结束的时候才来判断条件。也就是说,无论如何,循环都会执行至少一遍,然后再来判断条件。与while循环相同的是,条件满足时执行循环,条件不满足时循环结束。
循环应用1
循环计算
计算 l o g 2 x log_2x log2x:
#include <stdio.h> int main() { int x; int ret = 0; scanf("%d",&x); int t = x; //将x的初始值记录下来 while ( x > 1 ){ x /= 2; ret++; } printf("log2 of %d is %d.", t, ret); return 0; }
- tips:计算之前先保存原始的值,后面可能有用.
计数循环
#include <stdio.h> int main() { int count = 100; while ( count >= 0){ count--; printf("%d ", count); } printf("发射\n"); return 0; }
- 这个循环需要执行多少次? (101)
- 循环停下来的时候,有没有输出过0? (有)
- 循环结束后,count的值是多少? (-1)
tips: 如果要模拟运行一个很大次数的循环,可以模拟较少的循环次数,然后做出推断。
比如这个题,count = 100;我们可以把count先设为3,然后列举出输出结果: 2 1 0 -1;即输出为4次,即循环执行4次;有输出0;最后输出为-1。进而可以推断执行count + 1次。
猜数游戏
让计算机来想一个数,然后让用户来猜,用户每输入一个数,就告诉它是大了还是小了,直到用户猜中为止,最后还要告诉用户它猜了多少次。
(因为需要不断重复让用户猜,所以需要用到循环在实际写出程序之前,我们可以先用文字描述程序的思路核心重点是循环的条件,人们往往会考虑循环终止的条件)
思路:
-
计算机随机出一个数,记在变量number里;
-
一个负责记次数的变量count初始化为0;
-
让用户输入一个数字a;
-
count递增加一;
-
判断a和number的大小关系,如果是大,就输出大,如果是小,就输出小;
-
如果a和number是不相等的;程序回到第三步(3.);
-
否则,程序输出“猜中”和次数,然后结束。
循环条件是a和number不相等
srand(time(0)); int number = rand()%100+1; // x % n 的结果是[0, n-1]的一个整数(即1到100之间的整数) int count = 0; int a = 0; printf("我已经想好了一个1到100之间的数。"); do { printf("请猜这个1到100之间数:"); scanf("%d", &a); count ++; if ( a > number ) { printf("你猜的数大了。"); } else if ( a < number ) { printf("你猜的数小了。"); } } while (a != number); printf("太好了,你用了%d次就猜到了答案。\n", count);
拓展: 随机数
- 每次调用rand()函数就可以得到一个随机的整数
- x % n (取余) 的结果是[0, n-1]的一个整数
算平均数
-
sum = ∑ i = 1 n x i n \frac{\sum_{i = 1}^n{x_i}}{n} n∑i=1nxi
-
让用户输入一系列的正整数,最后输入 − 1 -1 −1 表示用户输入结束,然后程序算出这些数字的平均数,输出输入的数字的个数和平均数
-
变量->算法->流程图->程序
变量
- 一个记录读到的整数变量
- 平均数要怎么计算?
- 只需要每读到一个数,就把它加到一个累加的变量里,到全部数据读完,再拿它去除读到的数的个数就可以了
- 一个变量记录累加的结果,一个变量记录读到的数的个数(两个变量)
算法
- 初始化变量sum和count为0;
- 读入number;
- 如果 number 不是 -1,则将 number 加入 sum,并将 count+1,回到2;
- 如果 number 是-1,则计算和打印出 sum/count (注意换成浮点数来计算)
流程图
#include <stdio.h> int main() { int number ; int sum = 0; int count = 0; scanf("%d",&number); while (number != -1){ sum += number; count++; scanf("%d",&number); } printf("平均数为%f\n",1.0*sum/count);//1.0*sum转化为浮点数 return 0; }
整数分解
- 一个整数是由1至多位数字组成的,如何分解出整数的各个位上的数字,然后加以计算
-
对一个整数做%10的操作,就得到它的个位数;
-
对一个整数做/10的操作,就去掉了它的个位数;
-
然后再对2的结果做%10,就得到原来数的十位数;
-
以此类推。
举个栗子:
一个5位数的整数12345,如何将它的各个位上的数字进行分解?
- 先将12345对10取余,即12345%10 = 5,得到个位上的数;
- 再把12345用10来整除,即12345/10 = 1234,就去掉个位;得到四位数;
- 再对2的结果用10取余,即1234%10=4,得到十位上的数;
- 再把1234用10来整除,即1234/10 = 123,得到三位数;
- 以此类推,分别求出1,2,3,4,5;
- 这样就得到整数的分解
- 对于计算机来说,很多都是逻辑处理,它并不能像人类一样正常一眼就能看出几位数中的各个位上的数是多少,它只知道这个数的大小,并能对其进行运算。
应用:数的逆序
- 输入一个正整数,输出逆序的数
- 结尾的0的处理
#include <stdio.h> int main() { int x; scanf("%d",&x); int digit; int ret = 0; while(x>0){ digit = x%10; ret = ret*10 + digit; printf("x = %d, digit = %d, ret = %d\n", x, digit, ret); x /= 10; } printf("%d",ret); return 0; }
第三种循环
for循环
for( <初始动作(条件)>; <循环继续的条件>; <每一轮循环结束后要做的动作> ){ <循环体语句>; }
- for循环像一个计数循环:设定一个计数器,初始化它,然后在计数器到达某值之前,重复执行循环体,而每执行一轮循环,计数器值以一定步进行调整,比如加1或者减1。
for
=对于
(可以将for
当成对于
)- for ( count=10; count>0; count-- )
- 就读成:“
对于
一开始的count=10,当count>0时,重复做循环体,每一轮循环在做完循环体内语句后,使得count–。“
- tips: 做求和的程序时,记录结果的变量应该初始化为0,而做求积的变量时,记录结果的变量应该初始化为1。循环控制变量i 只在循环里被使用了,在循环外面它没有任何用处。因此,我们可以把变量i 的定义写到for语句里面去。
for和while对比
- for == while
例如:
- for循环
int i; for (int i=1; i<=n; i++){ fact *= i; }
- while循环
int i=1; while (i<=n){ fact *=i; i++; }
-
任何一个for循环都可以改成while循环
-
for(初始动作; 条件; 每轮的动作){
}
- for中的每一个表达式都是可以省略的
- for(; 条件; ) == while (条件)
- 但是两个分号
;
是不可省略, 是为了分隔开条件的
循环次数
- for (i = 0; i<n; i++)
- 则循环的次数是n,而循环结束以后,i的值还是n。循环的控制变量i,是选择从0开始还是从1开始,是判断i<n还是判断i<=n,对循环的次数,循环结束后变量的值都有影响。
- 如果是for(i=1; i<n; i++), 循环次数为n-1;循环结束后i的值为n
- 如果for(i=1; i<=n; i++), 循环次数为n; 循环结束后i的值为n+1
举个栗子:
#include <stdio.h> int main() { int i; for( i=1; i<=5; i++){ printf("i=%d\n",i); } printf("\n最后i=%d\n",i); return 0; }
Tips for loops:
- 如果有固定次数,用for循环
- 如果必须执行一次,用do-while循环
- 其他情况用while循环
另外注意 ++i 和 i++在for语句中是不做区别的, 都是用i加1后的值, 本质上都是取运算后 i 的值
i++和++i的区别详情请见: https://blog.csdn.net/honorzoey/article/details/112167850?spm=1001.2014.3001.5501
循环控制
素数判断
- 判断一个数是不是素数(只能被1和自己整除的数,不包括1)
- 2,3,5,7,11,13,17,19
#include <stdio.h> int main() { int x; scanf("%d",&x); int isPrime = 1; //开始先假设是素数,即为1 int i = 2; while (i<x){ if (x%i == 0){ isPrime = 0; //不是素数,为0 } i++; } if (isPrime == 1){ printf("%d是素数\n",x); }else{ printf("%d不是素数\n",x); } return 0; }
可以用for循环
#include <stdio.h> int main() { int x; scanf("%d",&x); int isPrime = 1; //开始先假设是素数,即为1 int i; for (i=2;i<x;i++){ if (x%i == 0){ isPrime = 0; //不是素数,为0 } } if (isPrime == 1){ printf("%d是素数\n",x); }else{ printf("%d不是素数\n",x); } return 0; }
break 和 continue
-
break
: 跳出循环 (和在switch -case中的用法一样,跳出语句) -
回顾: switch语句可以看作是⼀种基于计算的跳转,计算控制表达式的值后,程序会跳转到相匹配的case(分支标号)处。分支标号只是说明switch内部位置的路标,在执行完分支中的最后一条语句后,如果后面没有break,就会顺序执行到下面的case里去,直到遇到一个break,或者switch结束为止
-
continue
: 跳过循环这一轮剩下的语句进入下一轮循环(如果没下一轮循环则继续下个语句执行) -
break和continue只能对它们所在的那层循环起作用。
//break #include <stdio.h> int main() { int x; scanf("%d",&x); int isPrime = 1; //开始先假设是素数,即为1 int i; for (i=2;i<x;i++){ if (x%i==0){ isPrime = 0; //不是素数,为0 break; //跳出循环 } } if (isPrime == 1){ printf("%d是素数\n",x); }else{ printf("%d不是素数\n",x); } return 0; }
//continue #include <stdio.h> int main() { int x; scanf("%d",&x); int isPrime = 1; //开始先假设是素数,即为1 int i; for (i=2;i<x;i++){ if (x%i==0){ isPrime = 0; //不是素数,为0 continue; } printf("%d\n",x); } if (isPrime == 1){ printf("%d是素数\n",x); }else{ printf("%d不是素数\n",x); } return 0; }
断点调试一下可以看到break和continue的执行情况
循环的嵌套
- 100以内的素数
#include <stdio.h> int main() { int x; int i; for (x=1; x<=100; x++){ int isPrime = 1; //开始先假设是素数,即为1 for (i=2;i<x;i++){ if (x%i==0){ isPrime = 0; //不是素数,为0 break; //跳出循环 } } if (isPrime == 1){ printf("%d ", x); } } return 0; }
-
嵌套注意事项:
-
上面的变量要区分好局部变量和全局变量, isPrime不能做全局变量,只能做第一层for循环里面的局部变量;因为如果做全局变量,一旦进入for循环就会出现一旦isPrime变量被改为0;进入下一轮循环时它就不能默认为1了, 无论循环怎么进行,它始终不能变成1.因此isPrime变量要在for循环内始终以1为初始变量值。
-
int isPrime = 1; //开始先假设是素数,即为1 int i; for (x=1; x<=100; x++){ for (i=2;i<x;i++){ if (x%i==0){ isPrime = 0; //不是素数,为0 break; //跳出循环 } }
-
还有一般每一层的变量与下一层的变量不一样(要注意其命名方式,避免混淆变量名),但是也有一样的,就像上面的例子第一层循环的x变量和第二层循环的x是一样。
-
-
求50个连续的素数
-
#include <stdio.h> int main() { int x; int cnt = 0; x = 1; while(cnt<50){ //50个素数 int i; int isPrime = 1; //开始先假设是素数,即为1 for (i=2; i<x; i++){ if (x%i==0){ isPrime = 0; //不是素数,为0 break; //跳出循环 } } if (isPrime == 1){ cnt++; printf("%d\t", x); //排列整齐\t if(cnt%5==0){ printf("\n"); //每隔五个数换行 } } x++; } return 0; }
凑硬币
- 如何用1角、2角和5角的硬币凑出10元以下的金额呢?
#include <stdio.h> int main() { int x; int one, two, five; scanf("%d",&x); for( one = 1; one < x*10; one++){ for(two = 1; two < x*10/2; two++){ for(five = 1; five < x*10/5; five++){ if(one+2*two+5*five == x*10){ printf("%d个1角加%d个2角加%d个5角等于%d元\n",one,two,five,x); } } } } return 0; }
- 如果1到10块钱中的每个金额只需一种排列方式
#include <stdio.h> int main() { int x; int one, two, five; // scanf("%d",&x); for( x = 1; x <= 10; x++){ int exit = 0; //使用break退出 for( one = 1; one < x*10; one++){ for(two = 1; two < x*10/2; two++){ for(five = 1; five < x*10/5; five++){ if(one+2*two+5*five == x*10){ printf("%d个1角加%d个2角加%d个5角等于%d元\n",one,two,five,x); exit = 1; break; } } if (exit == 1) break; //加不加大括号没影响,只是不加大括号会更加易懂,为了退出当层的循环而设置的break,满足条件就退出. } if(exit == 1) break; //加不加大括号没影响,只是不加大括号会更加易懂,为了退出当层的循环而设置的break,满足条件就退出. } } return 0; }
注意: 和上次的局部变量和全局变量的问题 int exit = 0;要设置在第一层for循环里面
break和continue只能对它们所在的那层循环起作用。
- 另一种代替break的做法:
goto
#include <stdio.h> int main() { int x; int one, two, five; for( x = 1; x <= 10; x++){ for( one = 1; one < x*10; one++){ for(two = 1; two < x*10/2; two++){ for(five = 1; five < x*10/5; five++){ if(one+2*two+5*five == x*10){ printf("%d个1角加%d个2角加%d个5角等于%d元\n",one,two,five,x); goto out; } } } out: break; //跳出循环 } } }
也能达到只用break的做法
goto
{ { { { ...; goto <命名1>; } } } <命名1>: <所要做的动作>; }
- 跟函数跳转类似,就好比如重新定义一个"跳转"函数,当语句执行到goto语句时,跳转到你想要跳转的地方
循环应用2
求和
f ( n ) = 1 + 1 2 + 1 3 + 1 4 + . . . + 1 n f(n) = 1+\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+...+\frac{1}{n} f(n)=1+21+31+41+...+n1
#include <stdio.h> int main() { int n; int i; double ret = 0.0; //浮点数 scanf("%d", &n); for (i=1; i<=n; i++){ ret += 1.0/i; } printf("%f\n",ret); }
tips: 除了double、float可以转换成浮点数之外,也可以用一个int类型的数乘于1.0就可以转化为float型(浮点数)。
f ( n ) = 1 − 1 2 + 1 3 − 1 4 + . . . + 1 n f(n)=1-\frac{1}{2}+\frac{1}{3}-\frac{1}{4}+...+\frac{1}{n} f(n)=1−21+31−41+...+n1
#include <stdio.h> int main() { int n; int i; double ret = 0.0; int sign = 1; //或者 double scanf("%d",&n); for (i=1; i<=n; i++){ ret += 1.0*sign/i; sign = -sign; } printf("%f\n",ret); return 0; }
其中可以直接把int sign = 1;换成 double sign = 1.0; 这样下面就不用1.0*sign
正序分解整数
- 输入一个非负整数,正序输出它的每一位数字
- 输入:13425
- 输出:1 3 4 2 5
注: 取整数的最高位的做法: n位数整除 1 0 n 10^n 10n, 得到最高位数,即 x ÷ 1 0 n x \div 10^n x÷10n; 取除了最高位之外的数: 对 n 位数与 1 0 n 10^n 10n 取余, 即 x % 1 0 n x\%10^n x%10n 得到n-1位数。
逆序输出:先取余,后整除;
d = x % 10
x / =10
正序输出: 先整除后取余.
d = x / mask
x %= mask
而mask就是 1 0 n 10^n 10n, n随着位数的变化而变化
#include <stdio.h> int main() { int x; scanf("%d",&x); int mask = 1; int t = x; //将x值赋给t,是为了之后的操作不改变x的值 while ( t>9 ){ //保证t在两位数以上 t /= 10; mask *= 10; }//该循环得用while,是为了得到输入的x是几位数 // printf("x=%d, mask=%d\n", x, mask); //测试代码的效果 do{ int d = x / mask;//取得最高位 printf("%d", d); if (mask>9){ //在最后输出的数字中不带空格隔开 printf(" "); } x %= mask;//除最高位后剩下的几位 mask /= 10; //不断取后几位数的最高位 }while(mask > 0);//分解整数输出 printf("\n"); return 0; }
可以用浮点运算函数
#include<math.h>
mask = pow(10,n)
但是在计算机中比较慢
最大公约数
- 输入两个数 a 和 b,输出它们的最大公约数
- 输入:12 18
- 输出:6
两种算法:枚举和辗转相除法
枚举
在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠。 是一个被命名的整型常数的集合,枚举在日常生活中很常见,例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一个枚举。
- 设t为2;
- 如果u和v都能被t整除,则记下这个t
- t加1后重复第2步,直到t等于u或v;
- 那么,曾经记下的最大的可以同时整除u和v的t就是gcd
#include <stdio.h> int main() { int a,b; int min; scanf("%d %d", &a, &b); if (a<b){ min = a; }else{ min = b; }//选出a和b中最小的那个,最大公约数要比他们最小的还要小 int ret = 0; int i; for(i=1; i<min; i++){ if (a%i == 0){ if(b%i == 0){ ret = i; } } } printf("%d和%d的最大公约数是%d。\n", a, b, ret); return 0; }
辗转相除法
欧几里得算法又称辗转相除法,是指用于计算两个非负整数a,b的最大公约数。计算公式 gcd(a,b) = gcd(b,a mod b)。
-
如果b等于0,计算结束,a就是最大公约数;
-
否则,计算a除以b的余数,让a等于b,而b等于那个余数;
-
回到第一步。
#include <stdio.h> int main() { int a, b; int t; scanf("%d %d",&a,&b); while(b!=0){ t = a%b; a = b; b = t; printf("a=%d,b=%d,t=%d\n",a, b, t);//测试变量的变化过程 } printf("gcd = %d\n",a); return 0; }
这篇关于循环 —— C语言程序设计(三)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-26怎么使用nsenter命令进入容器?-icode9专业技术文章分享
- 2024-12-26导入文件提示存在乱码,请确定使用的是UTF-8编码怎么解决?-icode9专业技术文章分享
- 2024-12-26csv文件怎么设置编码?-icode9专业技术文章分享
- 2024-12-25TypeScript基础知识详解
- 2024-12-25安卓NDK 是什么?-icode9专业技术文章分享
- 2024-12-25caddy 可以定义日志到 文件吗?-icode9专业技术文章分享
- 2024-12-25wordfence如何设置密码规则?-icode9专业技术文章分享
- 2024-12-25有哪些方法可以实现 DLL 文件路径的管理?-icode9专业技术文章分享
- 2024-12-25错误信息 "At least one element in the source array could not be cast down to the destination array-icode9专业技术文章分享
- 2024-12-25'flutter' 不是内部或外部命令,也不是可运行的程序 或批处理文件。错误信息提示什么意思?-icode9专业技术文章分享