C语言基础 5. For
文章目录
5.1. For
-
阶乘:
-
n! : 表示n的阶乘
-
即: n! == 1 * 2 * 3 * … * n
-
写一个程序, 让用户输入n, 然后计算输出n!.
- 分析:
- 1.产生2到n之间的数,因为1*1相当于没有乘,而记录累乘结果的变量正好是1,所以1!不影响结果
- 2.让这些数累乘
- 分析:
-
-
for:
for (int i = 1; i <= n; i++) {
factorial *= i;
}
for (初始条件; 判断条件; 调整) {
执行语句;
}
-
执行顺序:
- for (1.初始条件; 2.判断条件; 4.调整) {
3.执行语句;
}
- for (1.初始条件; 2.判断条件; 4.调整) {
-
for( 对于 ):
- 可以这样翻译: 对于一开始i=1, 当i<=n时, 重复循环体, 每一轮循环在做完循环体内的语句后, 使得i++.
-
小套路:
- 做求和程序时, 记录结果的变量应该初始化为0, 而做求积的变量时, 变量应该初始化为1.
-
使用while求n的阶乘:
#include <stdio.h>
int main()
{
int n;
printf("请输入一个数来求它的阶乘: ");
scanf("%d", &n);
//产生2到n之间的数
int i = 2; // i表示这些数
int factorial = 1; // factorial接收累乘的结果
while (i <= n) {
//printf("%d ", i);
//让这些数累乘
factorial *= i;
i++;
}
//printf("factorial = %d ", factorial);
printf("%d! = %d\n", n, factorial);
return 0;
}
- 使用for求n的阶乘:
#include <stdio.h>
int main()
{
int n;
printf("请输入一个数来求它的阶乘: ");
scanf("%d", &n);
//产生2到n之间的数
int factorial = 1; // factorial接收累乘的结果
for (int i = 2; i <= n; i++) {//循环控制变量i只在循环里被使用了,外面没任何用处,所以可以定义到for语句里面
//让这些数累乘
factorial *= i;
}
或者可以产生n到2之间的数
//for (i = n; i <= 2; i--) {
// //让这些数累乘
// factorial *= i;
//}
//printf("factorial = %d ", factorial);
printf("%d! = %d\n", n, factorial);
return 0;
}
5.2. 循环的计算和选择
- 循环次数:
- 两种循环的循环次数都是相同的,不同的是,上面
i
的初始值是0,最后为n,而下面的i初始值是1, 最后为n+1
- 两种循环的循环次数都是相同的,不同的是,上面
for (i = 0; i < n; i++) {
printf("i = %d ", i);
count++;
}
for (i = 1; i <= n; i++) {
printf("i = %d ", i);
count++;
}
-
for == while:
- for和while是等价的
-
for中的每一个表达式都是可以省略的
-
如何选择不同的循环:
- 如果有固定次数, 选择for
- 如果必须执行一次, 用do while
- 其他情况用while
-
计算循环次数
#include <stdio.h>
int main()
{
int i;
int n = 5;
int count = 0;
/*for (i = 0; i < n; i++) {
printf("i = %d ", i);
count++;
}*/
for (i = 1; i <= n; i++) {
printf("i = %d ", i);
count++;
}
printf("\ni最后 = %d\n", i);
printf("循环次数为%d\n", count);
return 0;
}
5.3. Break&Continue
-
素数:
- 素数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数.
- 比如:
2, 3, 5, 7, 11, 13, 17, 19…
- 比如:
- 素数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数.
-
判断一个数是否为素数:
- 分析:
- 1.输入一个数num
- 2.产生2到num-1之间的数
- 3.试除,让num一个一个除这些数
- 4.如果能除尽则立即停止
- 5.如果这些数都除不尽,说明num是素数
- 分析:
-
break VS continue:
- break: 跳出循环
- continue: 跳过循环这一轮剩下的语句进入下一轮循环
-
自己写的:
#include <stdio.h>
int main()
{
//输入一个数
int num;
printf("请输入一个数,用来判断它是否为素数: ");
scanf("%d", &num);
int i;
//产生2到num-1之间的数
for ( i = 2; i <= num - 1; i++) {
//printf("%d ", i);
//试除,能除尽则停止
if (num % i == 0) {
printf("%d不是素数", num);
break; // 直接跳出循环, 循环中的break作用是跳出循环
}
}
//都除不尽则是素数
if (i == num) { // i++, i==num了, 进入不了循环, 说明num已经将i变量试除完毕, 此时判断为素数
printf("%d是素数", num); //虽然这种方法的结果也对, 但是它是有害的, 可读性差, 不建议使用
}
return 0;
}
- 跟着视频教程写的:
#include <stdio.h>
int main()
{
//输入一个数
int num;
printf("请输入一个数,用来判断它是否为素数: ");
scanf("%d", &num);
int i;
//定义一个判断素数的变量
int isPrime = 1;
//产生2到num-1之间的数
for (i = 2; i <= num - 1; i++) {
//printf("%d ", i);
//试除,能除尽则停止
if (num % i == 0) {
isPrime = 0;
break; // 直接跳出循环, 循环中的break作用是跳出循环
}
}
//都除不尽则是素数
if (isPrime == 1) {
printf("%d是素数", num);
}
else {
printf("%d不是素数", num);
}
return 0;
}
5.4. 嵌套的循环
-
输出100以内的素数:
- 分析:
- 1.产生1-100之间的数
- 2.产生2到num-1之间的数
- 3.试除,让num一个一个除这些数
- 4.如果能除尽则立即停止
- 5.如果这些数都除不尽,说明num是素数
- 分析:
-
嵌套:
-
循环里面还是循环
-
注意: 内层和外层的循环控制变量(也就是i和j和k)不能是一样的.
-
-
循环控制变量:
- 用来控制循环的次数, index简写成
i
, 因为ijk挨在一起, 所以常用的循环控制变量就是这三个
- 用来控制循环的次数, index简写成
-
输出前50个素数:
- 分析:
- 1.产生num++,count++
- 2.产生2到num-1之间的数
- 3.试除,让num一个一个除这些数
- 4.如果能除尽则立即停止
- 5.如果这些数都除不尽,说明num是素数
- 分析:
-
输出100以内的素数:
#include <stdio.h>
int main() {
//产生1-100之间的数
int num;
for (num = 2; num <= 100; num++) {
//定义一个素数变量
int isPrime = 1;
//产生2到num-1之间的数
int i;
for (i = 2; i <= num - 1; i++) {
//试除
if (num % i == 0) {
isPrime = 0; //能除尽则让素数变量为0
break;
}
}
//都除不尽则是素数
if (isPrime == 1) {
printf("%d ", num);
}
}
return 0;
}
- 输出前50个素数:
#include <stdio.h>
int main() {
int count = 0;
int num = 2;
while (count < 50) {
//定义一个素数变量
int isPrime = 1;
//产生2到num-1之间的数
int i;
for (i = 2; i <= num - 1; i++) {
//试除
if (num % i == 0) {
isPrime = 0; //能除尽则让素数变量为0
break;
}
}
//都除不尽则是素数
if (isPrime == 1) {
printf("%d ", num);
//计数
count++;
}
num++;
}
return 0;
}
5.5. 从嵌套中跳出
-
疑问:
排列组合现在还不是太懂 -
凑硬币:
- 如何用一角两角和五角的硬币凑出10元以下的金额?
- 提示: 排列组合
-
break和continue只能对它所在的那层(离它最近的)循环做
-
接力break:
- 为了跳出全部的三层循环, 满足条件后, 在三个循环中都加上break, 成为接力break.
#include <stdio.h> int main() { int money; int one, two, five; scanf("%d", &money); int exit = 0; for (one = 1; one < money * 10; one++) { for (two = 1; two < money * 10 / 2; two++) { for (five = 1; five < money * 10 / 5; five++) { if (one + two * 2 + five * 5 == money * 10) { printf("可以用%d个1角加%d个2角加%d个5角得到%d元\n", one, two, five, money); exit = 1; break; } } if (exit == 1) break; } if (exit == 1) break; } return 0; }
-
goto:
-
使用goto来代替接力break
-
goto out; //out是一个标号, 从这里
-
out: //直接跳转到这里
-
虽然goto语句有危险, 但是它可以使用在它的使用场景中, 也就是想要跳出多层循环时使用
-
-
凑硬币, 列出所有的可能:
#include <stdio.h>
int main()
{
int money;
int one, two, five;
scanf("%d", &money);
for (one = 1; one < money * 10; one++) {
for (two = 1; two < money * 10 / 2; two++) {
for (five = 1; five < money * 10 / 5; five++) {
if (one + two * 2 + five * 5 == money * 10) {
printf("可以用%d个1角加%d个2角加%d个5角得到%d元\n", one, two, five, money);
}
}
}
}
return 0;
}
- 出现一个可能后直接结束程序:
#include <stdio.h>
int main()
{
int money;
int one, two, five;
scanf("%d", &money);
int exit = 0;
for (one = 1; one < money * 10; one++) {
for (two = 1; two < money * 10 / 2; two++) {
for (five = 1; five < money * 10 / 5; five++) {
if (one + two * 2 + five * 5 == money * 10) {
printf("可以用%d个1角加%d个2角加%d个5角得到%d元\n", one, two, five, money);
exit = 1;
break;
}
}
if (exit == 1) break;
}
if (exit == 1) break;
}
return 0;
}
- 使用goto来代替接力break:
#include <stdio.h>
int main()
{
int money;
int one, two, five;
scanf("%d", &money);
for (one = 1; one < money * 10; one++) {
for (two = 1; two < money * 10 / 2; two++) {
for (five = 1; five < money * 10 / 5; five++) {
if (one + two * 2 + five * 5 == money * 10) {
printf("可以用%d个1角加%d个2角加%d个5角得到%d元\n", one, two, five, money);
goto out;
}
}
}
}
out:
return 0;
}
5.6. Test
5.6.1. 前n项和
-
前n项和:
-
f(n) = 1/1 + 1/2 + 1/3 + 1/4 + … + 1/n
-
分析:
- 1.输入一个数n
- 2.产生1到n之间的数
- 3.让1/这些数 4.相加
-
-
f(n) = 1/1 - 1/2 + 1/3 - 1/4 + … + 1/n
- 分析:
- 1.输入一个数n
- 2.产生1到n之间的数
- 3.让1/这些数
- 4.使用一个控制正负号的变量sign
- 分析:
-
fn
#include <stdio.h>
int main()
{
//输入n
int n;
scanf("%d", &n);
//求和变量
double sum = 0.0;
//产生1到n之间的数
for (int i = 1; i <= n; i++) {
//让1/这些数
double fn = 1.0 / i;
//相加
sum += fn;
}
printf("sum = %f", sum);
return 0;
}
- 一加一减
#include <stdio.h>
int main()
{
//输入n
int n;
scanf("%d", &n);
//求和变量
double sum = 0.0;
//正负变量
int sign = 1;
//产生1到n之间的数
for (int i = 1; i <= n; i++) {
//让1/这些数
double fn = sign * 1.0 / i;
//相加
sum += fn;
//循环一次变一次正负号
sign = -sign;
}
printf("sum = %f", sum);
return 0;
}
5.6.2. 整数分解
-
疑问:
-
逆序后,个个位数的第一位是0,则合成整数后,0就会消失
-
已解决, 因为此方法确实在遇到末位是0的数时行不通, 要换一种方法
-
-
正序整数分解:
-
输入一个自然数, 正序输出它的每一位数字
-
输入: 13425
-
输出: 1 3 4 2 5
-
分析1:
-
1.输入n
-
2.先将这个数逆序输出
-
3.再整数求逆
-
1.取出个位数digit=n%10
-
2.取出剩余位n/=10
-
3.直到n/10为0
-
4.让个个位数变成一个整数
-
5.再让这个整数求逆
-
发现行不通, 另谋他法
-
-
-
分析2:
n=13425 13425 / 10000 = 1; 13425 % 10000 = 3425 10000 / 10 = 1000 3425 / 1000 = 3; 3425 % 1000 = 425 1000 / 10 = 100 425 / 100 = 4; 425 % 100 = 25 100 / 10 = 10 25 / 10 = 2; 25 % 10 = 5 10 / 10 = 1 5 / 1 = 5; 5 % 1 = 0 1 / 10 = 0
- 1.输入n
- 2.n/[10^n的(位数-1)]=第一位
- 3.n%[10^n的(位数-1)]=剩余位
- 4.[10^n的(位数-1)]/10
-
-
pow()函数:
- 乘方函数, int ciMi = pow(10, 4); 计算10的4次方赋值给变量ciMi
- 使用条件:
- 引用头文件: #include <math.h>
-
分析1行不通:
#include <stdio.h>
int main() {
//输入
int n;
printf("请输入一个数, 将它正序分解: ");
scanf("%d", &n);
int ret = 0;
//循环取出位数
while (n != 0) {
//取出个位数
int digit = n % 10;
//取出剩余位
n /= 10;
//让个个位数变成一个整数
ret += digit; // 5 54 543 5432 54321
ret *= 10; // 50 540 5430 54320 543210
}
//将这个整数/10
int r = ret / 10;
while (r != 0) {
int d = r % 10;
r /= 10;
//输出个个位数
int result = d;
printf("%d", result);
//让结果格式化
if (r != 0) {
printf(" ");
}
}
return 0;
}
- 分析2:
#include <stdio.h>
#include <math.h>
int main() {
//输入
int n;
scanf("%d", &n);
//计算n的位数
int num = n; //保留n的值
//10的次方变量
int ciMi = 1;
int weiShu = 0;
while (num >= 10) {
num /= 10;
ciMi *= 10; //10的(n的位数-1)次方
/*weiShu++;*/
}
//int ciMi = pow(10, weiShu - 1); //可以这样写,但是还可以将ciMi放到上面的循环,更简便
//表每一位的变量
int digit = 0;
//循环得到n的每一位
while (ciMi > 0) { //假如判断条件再写成n>0,设n为600,则ciMi算下来为100
//600/100=6,600%100=0, n为0直接就跳出循环了,所以判断条件应该为ciMi>0
digit = n / ciMi;
printf("%d", digit);
//结果格式化
if (ciMi >= 10) { //同理, 这里的判断条件也应该为ciMi>=10
printf(" ");
}
n %= ciMi;
ciMi /= 10;
}
//可以在结果的最后输出一句话, 测试结果格式化是否正确
/*printf("hello");*/
return 0;
}
5.6.3. 最大公约数
-
最大公约数(gcd):
-
输入两个数a和b,输出它们的最大公约数(是指两个或多个整数共有约数中最大的一个),提示:枚举法和辗转相除法
-
输入: 12 18
-
输出: 6
-
分析:
-
枚举法(此方法容易理解, 但是效率不高):
- 1.输入两个数a,b
- 2.找出a,b中较小的
- 3.产生1到较小的数之间的数
- 4.将a和b都试除这些数
- 5.公约数的最总结果就是最大公约数
-
辗转相除法(不容易理解, 但是效率高):
- 1.如果b等于0, 计算结束, a就是最大公约数
- 2.否则, 计算a除以b的余数, 让a等于b, 而b等于那个余数
- 3.回到第一步
-
-
-
枚举法:
#include <stdio.h>
int main() {
//输入
int a, b;
scanf("%d %d", &a, &b);
//找出较小值
int min;
if (a < b) {
min = a;
}
else {
min = b;
}
//产生1到a之间的数
int i;
int ret = 0;
for (i = 1; i <= min; i++) {
//printf("%d ", i);
//试除,得到所有公约数
if (a % i == 0 && b % i == 0) {
//将公约数赋值给结果变量, 最后赋值的结果也就是最大公约数, 因为公约数是递增的
ret = i;
}
}
printf("%d和%d的最大公约数为: %d", a, b, ret);
return 0;
}
- 辗转相除法:
#include <stdio.h>
int main() {
//输入
int a, b;
scanf("%d %d", &a, &b);
//余数变量
int yushu = 0;
while (b != 0) {
yushu = a % b;
a = b;
b = yushu;
}
printf("a和b的gcd是%d", a);
return 0;
}
5.7. PAT
-
05-0. 求序列前N项和(15)
-
疑问: 精确到两位小数?
-
已解决
-
本题要求编写程序,计算序列 2/1+3/2+5/3+8/5+… 的前N项之和。注意该序列从第2项起,每一项的分子是前一项分子与分母的和,分母是前一项的分子。
-
输入格式:
输入在一行中给出一个正整数N。 -
输出格式:
在一行中输出部分和的值,精确到小数点后2位。题目保证计算结果不超过双精度范围。 -
输入样例:
20 -
输出样例:
32.66 -
分析:
-
分子变量:fenZi 分母变量:fenMu
-
通项公式: fenZi+fenMu / fenZi
-
初始值: fenZi=2, fenMu=1
-
1.输入n
-
2.初始化变量
-
3.通项公式
-
4.求和,精确到两位小数(%.2f 表示只取小数点后两位的数)
-
-
- 代码:
#include <stdio.h>
int main() {
//输入n
int n;
scanf("%d", &n);
//分子,分母变量
double fenZi = 2.0;
double fenMu = 1.0;
double sum = 0.0;
int i = 1;
while (i <= n) {
//求和
sum += fenZi / fenMu;
//通项公式
double tmp = fenMu; //1
fenMu = fenZi; //2
fenZi = fenZi + tmp; //2+1
i++;
}
printf("%.2f\n", sum); //%.2f 表示只取小数点后两位的数
return 0;
}
-
05-1. 约分最简分式(15)
-
分数可以表示为“分子/分母”的形式。编写一个程序,要求用户输入一个分数,然后将其约分为最简分式。最简分式是指分子和分母不具有可以约分的成分了。如6/12可以被约分为1/2。当分子大于分母时,不需要表达为整数又分数的形式,即11/8还是11/8;而当分子分母相等时,仍然表达为1/1的分数形式。
-
输入格式:
输入在一行中给出一个分数,分子和分母中间以斜杠“/”分隔,如:12/34表示34分之12。分子和分母都是正整数(不包含0,如果不清楚正整数的定义的话)。 -
提示:在scanf的格式字符串中加入“/”,让scanf来处理这个斜杠。
-
输出格式:
在一行中输出这个分数对应的最简分式,格式与输入的相同,即采用“分子/分母”的形式表示分数。如5/6表示6分之5。 -
输入样例:
60/120 -
输出样例:
1/2 -
分析:
- 1.输入fenZi/fenMu
- 2.找到它们的最大公约数gcd
- 3.让它们分别除以gcd
- 4.输出结果
-
-
代码:
#include <stdio.h>
int main() {
//输入
int fenZi, fenMu;
scanf("%d/%d", &fenZi, &fenMu);
//定义求最大公约数的变量,来保留fenZi和fenMu变量的值
int a = fenZi;
int b = fenMu;
while (b != 0) {
//辗转相除法,求最大公约数
int yuShu = a % b;//余数变量
a = b;
b = yuShu;
}
int gcd = a;
//printf("gcd = %d", gcd);
//化简
fenZi /= gcd;
fenMu /= gcd;
//输出结果
printf("%d/%d", fenZi, fenMu);
return 0;
}
-
05-2. 念数字(15)
-
输入一个整数,输出每个数字对应的拼音。当整数为负数时,先输出“fu”字。十个数字对应的拼音如下:
0: ling
1: yi
2: er
3: san
4: si
5: wu
6: liu
7: qi
8: ba
9: jiu -
输入格式:
输入在一行中给出一个整数,如:1234。 -
提示:整数包括负数、零和正数。
-
输出格式:
在一行中输出这个整数对应的拼音,每个数字的拼音之间用空格分开,行末没有最后的空格。如yi er san si。 -
输入样例:
-600 -
输出样例:
fu liu ling ling -
分析:
- 1.输入num
- 2.判断num的正负及零
- 3.正序整数分解
- 4.switch-case语句判断( 此处只能使用这种笨方法, 后面学到数组后会有更简洁的方法)
- 5.输出
-
-
代码:
#include <stdio.h>
int main() {
//输入
int num;
scanf("%d", &num);
//判断正负及0
if (num != 0) {
//去负号*-1
if (num < 0) {
num *= -1;
printf("fu ");
}
//10的次幂的变量
int ciMi = 1;
int tmp = num; //保留num的值
//计算10的num的位数-1次幂
while (tmp >= 10) {
tmp /= 10;
ciMi *= 10;
}
//表每一位的变量
int digit = 0;
while (ciMi > 0) {
digit = num / ciMi;
num %= ciMi;
ciMi /= 10;
//switch-case判断数字
switch (digit) {
case 0:
printf("ling");
break;
case 1:
printf("yi");
break;
case 2:
printf("er");
break;
case 3:
printf("san");
break;
case 4:
printf("si");
break;
case 5:
printf("wu");
break;
case 6:
printf("liu");
break;
case 7:
printf("qi");
break;
case 8:
printf("ba");
break;
case 9:
printf("jiu");
break;
}
//格式化
if (ciMi > 0) {
printf(" ");
}
}
printf("hello");
}
//判断零
else {
printf("ling\n");
}
return 0;
}
-
05-3. 求a的连续和(15)
-
输入两个整数a和n,a的范围是[0,9],n的范围是[1,8],求数列之和S = a+aa+aaa+…+aaa…a(n个a)。如a为2、n为8时输出的是2+22+222+…+22222222的和。
-
输入格式:
输入在一行中给出两个整数,先后表示a和n。 -
输出格式:
在一行中输出要求的数列之和。 -
输入样例:
2 4 -
输出样例:
2468 -
分析:
-
2+22+222+2222+…+22222222
-
210=20 20+2=22 2210=220 220+2=222
-
a*=10 a+=2
-
1.输入
-
2.循环n次
-
3.通项公式a*=10,a+=2
-
4.每一项相加
-
-
-
代码:
#include <stdio.h>
int main() {
//输入
int a, n;
scanf("%d %d", &a, &n);
//求和变量
int sum = 0;
//循环n次
for (int i = 1; i <= n; i++) {
//每一项相加
sum += a;
//通项公式
a *= 10;
a += 2;
}
printf("%d", sum);
return 0;
}