• 程序设计入门-C语言基础知识-翁恺-第四周:循环控制-详细笔记(四)


    第四周:循环控制

    4-1 for循环

    for循环像一个计数循环:设定一个计数器,初始化它,然后在计数器到达峰值之前,重复执行循环体,而每执行一轮循环,计数器以一定步进进行调整,比如加1或者减1

    for循环语法:
    for (循环变量初始化; 循环条件判断; 循环变量赋值){
    循环体
    }
    整个for循环按以下步骤执行:

    1. 初始化循环变量
    2. 判断循环条件,成立执行循环体否则结束循环。
    3. 循环变量赋值,程序回到第2步
    int i;
    for (i = 1; i <= 5; i++){
        printf("%d,", i); 
    }
    printf("
    i=%d",i);
    /*执行结果:
    1,2,3,4,5,
    i=6
    */
    

    Tips for loops

    • 如果有固定次数,用for
    • 如果必须执行一次,用do_while
    • 其他情况用while

    4-2 循环控制

    要求程序读取用户输入的一个整数n,并告诉用户这个数是否是素数。素数:只能被1和自己整除的数,不包括1,比如2,3,5,7,11,17,19...

    题目分析:
    程序如何判断一个数能被另一个数整除?
    将两个数做取余运算符,如果结果为0就表示前者能被后者整除。比如4%2=0 ,就表示4能被2整除。
    如何判断一个数只能被1和自身整数?
    直接一点的方法就是用1到n之间的数去当做取余数,n作为被取余数,两者做取余运算。1到n之间只要有一个数能被整数,那么这个数就不是素数,否则这个数就是一个素数。当一个数为n时超过n的中间值的那个数都不可能对n做整除了,因此我们可以省掉一半的计算,用1至n/2之间的数和n做取余运算即可。

    1. 声明变量isPrimeNumber并赋初始值 1 1=素数,0=非素数
    2. 程序接受用户输入整数放入number中
    3. 如果number等于1,则将isPrimeNumber赋值为0
    4. 初始化循环变量i的值为2
    5. 判断i是否小于等于number/2
      5.1 小于等于用number和i做取余运算,若取余结果为0则isPrimeNumber赋值为0并且程序跳转到第6步,
      否则程序跳转至第5.3步
      5.2 否则程序跳转到第6步
      5.3 i自增1,程序回到第4步
    6. 判断isPrimeNumber如果是1输出number为素数否则输出number不是素数。

    代码实现:

    #include <stdio.h>
    
    void main(int argc, char *argv[]) {
     int number = 0;
     int isPrimeNumber = 1;
     int middle = 0;
     printf("请输入一个整数:"); 
     scanf("%d", &number);
    	
     if(number == 1){
      isPrimeNumber = 0;  
     } else {
      int i = 2;
      middle = number/2;
      for(; i <= middle; i++){
       if(number % i == 0){
        isPrimeNumber = 0;
        break;
       }
      }  
     }
    	
     if(isPrimeNumber == 1){
      printf("数字%d是一个素数", number); 
     } else {
      printf("数字%d不是一个素数", number);
     }		
    }
    

    测试样例:

    请输入一个整数:7
    数字7是一个素数
    --------------------------------
    

    我们如果想知道100以内的素数改怎么办呢?显然我们需要判断2到100的所有整数,如果是素数我们就输出。
    让我们一起来把程序改造下。
    程序实现:

    #include <stdio.h>
    
    void main(int argc, char *argv[]) {
     int j = 2;
     int i = 2;
     int middle = 0;
     int isPrimeNumber = 1;
    	
     printf("100以内的素数:
    ");  
     for(; j < 100; j++)	{
      middle = j/2;
      isPrimeNumber = 1;
      i = 2;
      for(; i <= middle; i++){
       if(j % i == 0){
        isPrimeNumber = 0;
        break;
       }
      }
      if(isPrimeNumber == 1){
       printf("%d ", j);
      }
     }
    }
    

    运行样例:

    100以内的素数:
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
    --------------------------------
    

    break vs continue
    tips:(vs versus 对比)

    • break:跳出循环
    • continue : 跳过这一轮循环剩下的语句并进入下一轮循环

    逻辑运算

    • 逻辑运算是对逻辑量进行的运算,结果只有0或1
    • 逻辑量是关系运算或逻辑运算的结果

    各运算符优先级(图)

    短路

    • 逻辑运算时自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算
    • a6 && b1
    • a==6 && b+=1
    • 对于&&,左边是false就不做右边了
    • 对于||,左边是true就不做右边了

    ** 例子:凑硬币 **

    • 如何用1角、2角和5角硬币凑出2元?
      分析:
    • 用m1,m2,m5分别代表1角、2角和5角
    • 既然是凑出那么m1、m2、m5都至少要出现一次
    • xm1 + ym2 + z*m5 = 20 本质上就是一个一元三次方程。
    • 我们知道x的取值范围[1,13] y的取值范围[1,7],z的取值范围[1,3]
    • 因此我们用一个x,y,z的嵌套循环就能求解了,并且和循环的嵌套的顺序无关。
    • x、y、z的下限哦们知道是1,上限目前是根据具体的金额2元算出来的,那么如果我们要算的是3元、5元或者其他金额我们的程序还需要修改。显然我们不希望这样,那有没有办法不修改程序还能适应各种金额呢?
    • 其实不难,假如总数是zs x上限 xs = zs - m2 - m5,就是总数减去其他两个数各取1的值。对于ys,zs同理。
    • 当x,y,z解的交集恰好等于20就是方程的有效解了。

    代码实现:

    #include <stdio.h>
    
    void main(int argc, char *argv[]) {
    void combination()
    {
     int zs = 20;
     int one = 1;
     int two = 2;
     int five = 5;
    
     for (int x = 1; x <= (zs - two - five) / one; x++)
     {
      for (int y = 1; y <= (zs - one - five) / two; y++)
      {
       for (int z = 1; z <= (zs - one - two) / five; z++)
       {
        if (x * one + y * two + z * five == zs)
        {
         printf("可以用%d个一角加%d个两角加%d个五角得到2元
    ", x, y, z);
        }
       }
      }
     }
    }
    
    

    测试样例:

    可以用1个一角加2个两角加3个五角得到2元
    可以用1个一角加7个两角加1个五角得到2元
    可以用2个一角加4个两角加2个五角得到2元
    可以用3个一角加1个两角加3个五角得到2元
    可以用3个一角加6个两角加1个五角得到2元
    可以用4个一角加3个两角加2个五角得到2元
    可以用5个一角加5个两角加1个五角得到2元
    可以用6个一角加2个两角加2个五角得到2元
    可以用7个一角加4个两角加1个五角得到2元
    可以用8个一角加1个两角加2个五角得到2元
    可以用9个一角加3个两角加1个五角得到2元
    可以用11个一角加2个两角加1个五角得到2元
    可以用13个一角加1个两角加1个五角得到2元
    
    --------------------------------
    

    如果我们只想得到一个结果怎么做呢?有读者可能会想到在最里面的for循环也就是z所在的for循环加一个break,实际上我们会发现,即使加上了break,运行的结果还是一样,原因就是break只会跳出离他最近的那一个循环,因此我们需要一点小的技巧来处理。
    跳出多重循环:

    #include <stdio.h>
    
    void main(int argc, char *argv[]) {
    {
     int zs = 20;
     int one = 1;
     int two = 2;
     int five = 5;
     int exist = 0;
    
     for (int x = 1; x <= (zs - two - five) / one; x++)
     {
      for (int y = 1; y <= (zs - one - five) / two; y++)
      {
       for (int z = 1; z <= (zs - one - two) / five; z++)
       {
        if (x * one + y * two + z * five == zs)
        {
         printf("可以用%d个一角加%d个两角加%d个五角得到2元
    ", x, y, z);
         exist=1;
         break;
        }
       }
       
       if (exist) {
        break;
       }   
      }
      
      if (exist) {
       break;
      }
      
     }
    }
    
    
    

    测试样例:

    可以用1个一角加2个两角加3个五角得到2元
    
    --------------------------------
    

    tips:我们还可以使用c语言的goto关键词来实现循环的退出,但是goto容易破坏掉程序的顺序结构,因此不推荐使用。

    求两个数的最大公约数
    辗转相除法

    1. 如果b等于0,计算结束,a就是最大公约数
    2. 否则,计算a除以b的余数,让a等于b,而b等于那个余数;
    3. 回到第一步

    代码实现:

    #include <stdio.h>
    
    void main(int argc, char *argv[]) {
    {
        int a, b;
        int t;
        a = 12;
        b = 18;
    
        while (b != 0)
        {
            t = a % b;
            a = b;
            b = t;
        }
        printf("gcd=%d
    ", a);
    }
    
    

    测试样例:

    gcd=6
    
    --------------------------------
    

    正序分解整数

    • 输入一个非负整数,正序输出它的每一位数字
    • 输入:13425
    • 输出:1 3 4 2 5

    题目分析:

    • 要正序输出就要得到数字的最高位,然后把这个数字除以10再得到最高位,一直到循环最低位就是正序分解的结果了。
    • 我们知道一个数字13425/10000 就能得到最高位,再用13425%10000就能得到3425取掉最高位的数字。因此num/10000就是最高位, num%10000 就是余下的数字。
    • 但是10000要根据数字的大小来定,所以第一步应该先计算数字有多少位来决定是除10还是除10000
    • 计算整数的位数我们整除10直到结果等于0,除了多少次加1就是几位数,10的位数减一的次方就是我们第一次分解需要的除数
      代码实现:
    #include <stdio.h>
    
    void main(int argc, char *argv[])
    {
        int x;
        printf("请输入一个正整数:");
        scanf("%d",&x);    
        int mask = 1;
        int t = x;
        while (t >= 10)
        {
            t /= 10;
            mask *= 10;
        }
    
        do
        {
            int d = x / mask;
            printf("%d", d);
    
            if (mask >= 10)
            {
                printf(" ");
            }
            x %= mask;
            mask /= 10;
    
        } while (mask > 0);
        printf("
    ");
    }
    
    

    测试样例:

    请输入一个正整数:13425
    1 3 4 2 5
    --------------------------------
    
    请输入一个正整数:500000
    5 0 0 0 0 0
    
    --------------------------------
    
    
    

    4-3 课后习题

    1 .题目内容: 题目内容:
    我们认为 2 是第一个素数,3 是第二个素数,5 是第三个素数,依次类推。
    现在,给定两个整数 n 和 m,0<n<=m<=200,你的程序要计算第 n 个素数到第 m 个素
    数之间所有的素数的和,包括第 n 个素数和第 m 个素数。
    输入格式:
    两个整数,第一个表示 n,第二个表示 m。
    输出格式:
    一个整数,表示第 n 个素数到第 m 个素数之间所有的素数的和,包括第 n 个素数和第 m
    个素数。
    输入样例:
    2 4
    输出样例:
    15

    题目分析:

    • 要计算第n个素数, 第 m个素数,以及他们之间素数的和
    • 怎么计算第n个素数呢?用index来记录是第几个素数,然后统计从2开始的素数,每统计一个index自增1,当我们统计到第n个素数时,将和记录在sum中,继续统计下一个直到第m个素数,累加值sum就是我们要求的和。
    • 我们需要一个数素数的循环f1,循环遍历i从2开始,在循环内部判断这个数是否是素数,执行完i步进1
    • 判断是素数的判断中需要添加index是否到n和是否到m的判断对sum做累加操作
    • 我们最多要数到第200个素数,但是我们目前还不知道第200个素数的值是多少,因此我们不妨把第一个循环的值设置大一点。比如10000,先假设这些数中足够200个素数了。

    程序实现:

    #include "sumPrime.h"
    
    void sumPrime()
    {
     int j = 2;
     int i = 2;
     int middle = 0;
     int isPrimeNumber = 1;
     int index = 0;
     int sum = 0;
     int n, m;
     int isCalcRange = 0;//是否计算一个范围内素数的和,如果m==n 只需计算第n 个素数的值即可,因此当且仅当m>n 时此变量值为1
    
     printf("请输入n和m以空格隔开 0<n<=m<200:
    ");
     scanf("%d %d", &n, &m);
     isCalcRange = m > n;
     for (; j < 10000; j++)
     {
      middle = j / 2;
      isPrimeNumber = 1;
      i = 2;
      for (; i <= middle; i++)
      {
       if (j % i == 0)
       {
        isPrimeNumber = 0;
        break;
       }
      }
    
      if (isPrimeNumber == 1)
      {
       index++;
    
       if (index >= n)
       {
        if (index == m)
        {
         sum += j;
         break;
        } 
        else
        {
         sum += j;
        }            
       }
    
       
      }
     }
     printf("%d
    ",sum);
    }
    

    测试样例:

    请输入n和m以空格隔开 0<n<=m<200:
    2 4
    15
    
    --------------------------------
    
    请输入n和m以空格隔开 0<n<=m<200:
    200 200
    1223
    
    --------------------------------
    
    请输入n和m以空格隔开 0<n<=m<200:
    1 200
    111587
    
    --------------------------------
    
    
    请输入n和m以空格隔开 0<n<=m<200:
    0 0
    5736396
    
    --------------------------------
    
    
    

    tips:思考下当我们输入两个0时计算的结果是什么?我们改如何改进我们的程序才不会出现这样的情况?

    2.题目内容:
    你的程序要读入一个整数,范围是[-100000,100000]。然后,用汉语拼音将这个整数
    的每一位输出出来。
    如输入 1234,则输出:
    yi er san si
    注意,每个字的拼音之间有一个空格,但是最后的字后面没有空格。当遇到负数时,在输出
    的开头加上“fu”,如-2341 输出为:
    fu er san si yi
    输入格式:
    一个整数,范围是[-100000,100000]。
    输出格式:
    表示这个整数的每一位数字的汉语拼音,每一位数字的拼音之间以空格分隔,末尾没有空格。
    输入样例:
    -30
    输出样例:
    fu san ling

    题目分析:

    • 前面我们做过整数的正序拆分,只需要把拆分后的数字对应成拼音即可。
    • 这里出现了负数,我们用isNegative记录这个数是否是负数,然后对负数做取负的运算,之后的计算和正数已知,最后再通过判断isNegative进行符号的输出即可。
      程序实现:
    #include <stdio.h>
    #include "getDigitPinYin.h"
    
    void main(int argc, char *argv[])
    {
        int x;
        int isNegative = 0;
        printf("请输入一个整数[-100000,100000]:");
        scanf("%d", &x);
    
        if (x < 0)
        {
            isNegative = 1;
            x = -x;
            printf("fu ");
        }
    
        int mask = 1;
        int t = x;
        while (t >= 10)
        {
            t /= 10;
            mask *= 10;
        }
    
        do
        {
            int d = x / mask;
    
            switch (d)
            {
                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;
                default:
                    printf("unown");
                    break;
            }        
    
            if (mask >= 10)
            {
                printf(" ");
            }
            x %= mask;
            mask /= 10;
    
        } while (mask > 0);
        printf("
    ");
    }
    
    

    测试样例:

    请输入一个整数[-100000,100000]:-30
    fu san ling
    
    --------------------------------
    
    请输入一个整数[-100000,100000]:12345
    yi er san si wu
    
    --------------------------------
    

    4-4 讨论题

    标题:利用循环变量来判断素数不好吗?
    内容:
    课程中提到有种“聪明”的做法,可以不设 isPrime,直接利用循环出口处循环变量和终点值
    的关系来判断循环是否 break 了。你觉得这种做法好吗?

    我认为这种做法不可取,有至少以下两点原因:

    1. 程序不易于阅读,如果通过变量isPrime一眼能看出程序要表达的意思,如果用出口循环变量判断需要思维转个弯才能理解。
    2. 不易扩展维护,如果我们的程序不判断到number/2,而是number-1,那么这个出口循环变量的判断也需要做更改。
  • 相关阅读:
    SQLServer提取日期中的年月日及其他格式
    大白话解说,半分钟就懂 --- 分布式与集群是什么 ? 区别是什么?
    VS2015 Git 源码管理工具简单入门
    Web.Config配置文件中customErrors元素的使用方法
    C#发起Http请求,调用接口
    如何停止和禁用Linux系统中的不需要的服务
    QtCreator调试传入运行参数
    gSOAP 在windows下的安装与使用(mingw32)
    MinGW 使用 mintty 终端替代默认终端以解决界面上复制与粘贴的问题
    在windows下执行./configure,make,makeinstall源码安装程序spice-gtk
  • 原文地址:https://www.cnblogs.com/simple-blog/p/9500959.html
Copyright © 2020-2023  润新知