while(<循环条件>){ <循环体语句> }
tips:循环就意味着它要考虑到更多的情况是否在循环内进行,所以要进行验证;但人工验证的同时不可能全部都试一遍,因此要从一般到特殊,尽可能的包含到。测试程序常使用边界数据,如有效范围两端的数据、特殊的倍数等。
do { <循环体语句> }while(<循环条件>);
;
来结束语句。区别两种循环:do-while 循环和 while 循环很像,区别在于 do-while 循环的循环体执行结束的时候才来判断条件。也就是说,无论如何,循环都会执行至少一遍,然后再来判断条件。与while循环相同的是,条件满足时执行循环,条件不满足时循环结束。
计算 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; }
#include <stdio.h> int main() { int count = 100; while ( count >= 0){ count--; printf("%d ", count); } printf("发射\n"); return 0; }
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);
sum = ∑ i = 1 n x i n \frac{\sum_{i = 1}^n{x_i}}{n} n∑i=1nxi
让用户输入一系列的正整数,最后输入 − 1 -1 −1 表示用户输入结束,然后程序算出这些数字的平均数,输出输入的数字的个数和平均数
变量->算法->流程图->程序
变量
算法
流程图
#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; }
对一个整数做%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;
- 这样就得到整数的分解
- 对于计算机来说,很多都是逻辑处理,它并不能像人类一样正常一眼就能看出几位数中的各个位上的数是多少,它只知道这个数的大小,并能对其进行运算。
#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
当成对于
)
对于
一开始的count=10,当count>0时,重复做循环体,每一轮循环在做完循环体内语句后,使得count–。“例如:
int i; for (int i=1; i<=n; i++){ fact *= i; }
int i=1; while (i<=n){ fact *=i; i++; }
任何一个for循环都可以改成while循环
for(初始动作; 条件; 每轮的动作){
}
;
是不可省略, 是为了分隔开条件的举个栗子:
#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
#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; }
#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
: 跳出循环 (和在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的执行情况
#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; }
#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; }
#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只能对它们所在的那层循环起作用。
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>: <所要做的动作>; }
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
注: 取整数的最高位的做法: 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)
但是在计算机中比较慢
两种算法:枚举和辗转相除法
在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠。 是一个被命名的整型常数的集合,枚举在日常生活中很常见,例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一个枚举。
#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; }