程序設計入門-C語言基礎知識-翁愷-第四周:循環控制-詳細筆記(四)

第四周:循環控制

4-1 for循環

for循環像一個計數循環:設定一個計數器,初始化它,而後在計數器到達峯值以前,重複執行循環體,而每執行一輪循環,計數器以必定步進進行調整,好比加1或者減1oop

for循環語法:
for (循環變量初始化; 循環條件判斷; 循環變量賦值){
循環體
}
整個for循環按如下步驟執行:測試

  1. 初始化循環變量
  2. 判斷循環條件,成立執行循環體不然結束循環。
  3. 循環變量賦值,程序回到第2步
int i;
for (i = 1; i <= 5; i++){
    printf("%d,", i); 
}
printf("\ni=%d",i);
/*執行結果:
1,2,3,4,5,
i=6
*/

Tips for loopscode

  • 若是有固定次數,用for
  • 若是必須執行一次,用do_while
  • 其餘狀況用while

4-2 循環控制

要求程序讀取用戶輸入的一個整數n,並告訴用戶這個數是不是素數。素數:只能被1和本身整除的數,不包括1,好比2,3,5,7,11,17,19...blog

題目分析:
程序如何判斷一個數能被另外一個數整除?
將兩個數作取餘運算符,若是結果爲0就表示前者能被後者整除。好比4%2=0 ,就表示4能被2整除。
如何判斷一個數只能被1和自身整數?
直接一點的方法就是用1到n之間的數去當作取餘數,n做爲被取餘數,二者作取餘運算。1到n之間只要有一個數能被整數,那麼這個數就不是素數,不然這個數就是一個素數。當一個數爲n時超過n的中間值的那個數都不可能對n作整除了,所以咱們能夠省掉一半的計算,用1至n/2之間的數和n作取餘運算便可。ip

  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不是素數。

代碼實現:get

#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);
 }      
}

測試樣例:it

請輸入一個整數:7
數字7是一個素數
--------------------------------

咱們若是想知道100之內的素數改怎麼辦呢?顯然咱們須要判斷2到100的全部整數,若是是素數咱們就輸出。
讓咱們一塊兒來把程序改造下。
程序實現:io

#include <stdio.h>

void main(int argc, char *argv[]) {
 int j = 2;
 int i = 2;
 int middle = 0;
 int isPrimeNumber = 1;
    
 printf("100之內的素數:\n");  
 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);
  }
 }
}

運行樣例:for循環

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
  • 邏輯量是關係運算或邏輯運算的結果

各運算符優先級(圖)

短路

  • 邏輯運算時自左向右進行的,若是左邊的結果已經可以決定結果了,就不會作右邊的計算
  • a==6 && b==1
  • a==6 && b+=1
  • 對於&&,左邊是false就不作右邊了
  • 對於||,左邊是true就不作右邊了

** 例子:湊硬幣 **

  • 如何用1角、2角和5角硬幣湊出2元?
    分析:
  • 用m1,m2,m5分別表明1角、2角和5角
  • 既然是湊出那麼m一、m二、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元\n", 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元\n", 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\n", 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("\n");
}

測試樣例:

請輸入一個正整數: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:\n");
 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\n",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("\n");
}

測試樣例:

請輸入一個整數[-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,那麼這個出口循環變量的判斷也須要作更改。
相關文章
相關標籤/搜索