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

第三週:循環

3.1 循環

while循環
語法:
while(條件表達式){
//循環體語句
}算法

  • 若是咱們把while翻譯做「當」,那麼一個while循環的意思就是:當條件知足時,不斷地重複循環體內的語句。
  • 循環體執行以前判斷是否繼續循環,因此有可能循環一次也沒有被執行、
  • 條件成立時循環繼續的條件
  • 循環體執行步驟
    1. 檢查條件表達式是否成立
    2. 不成立結束循環,成立執行循環體內語句後回到第一步。

例子:數整數的位數
用戶輸入一個整數,要求輸出整數的位數。好比輸入123,輸出3測試

輸入樣例
123編碼

輸出樣例
3翻譯

程序實現:code

#include <stdio.h>

int main(int argc, char *argv[]) {

    
    
 int x = 0;
 int n = 0;
    
 printf("請輸入一個整數:");
 scanf("%d", &x);
 n++;
 x /= 10;
 while(x > 0) {
  x /= 10;
  n++;
 }
    
 printf("這個整數的位數是%d", n);
  
 return 0;
}

運行結果:blog

請輸入一個整數:123
這個整數的位數是3

複合運算遊戲

  • 5個算術運算符,+、-、、/、% 均可以和賦值運算符"="結合起來,造成複合運算符:「+=」、「-=」、「=」、「/=」、「%=」
    • total += 5;
    • total = total + 5;
  • 注意兩個運算符中間不要有空格

遞增遞減運算符ip

  • "++"和"--"是兩個很特殊的運算符,它們是單目運算符,這個算子還必須是變量。
  • 以上兩個運算符分別叫作遞增和遞減運算符,它們的做用就是給這個變量+1或者-1
    • count ++;
    • count += 1;
    • count = count + 1;
    • 以上3個表達式是等價的
      前綴和後綴
  • ++和--能夠放在變量的前面,叫作前綴形式,也能夠放在變量的後面,叫作後綴形式。
  • a++的值是先將a參與運算,再將a的值自增1,而++a則是先將a的值自增1再參與運算。
  • 遞減運算符同理

好比:it

int a = 0;
int b = a++ ; // a = 1,b = 0,
int c = ++a; // a = 2 ,c = 2

do-while循環

  • 在進入循環體的時候不作條件表達式的檢查,而是在執行完一輪循環體的代碼以後,再來檢查循環的條件是否知足,若是知足則進行下一輪循環,不知足則結束循環。
  • 注意do-while的結尾須要分號的
  • do-while和while循環區別是前者先執行語句再判斷條件,然後者先判斷條件再執行語句,具體使用那種循環須要根據實際狀況而定。
    語法:
    do
    {
    <循環體語句>
    } while( <循環條件> );

3.2 循環計算

猜數遊戲

  • 讓計算機來想一個數,而後讓用戶來猜,用戶每輸入一個數,就告訴他打了仍是小了,直到用戶猜中爲止,最後還要告訴用戶它猜了多少次。

程序實現分析:

  1. 計算機隨機想一個數,記在變量number裏;
  2. 一個負責記次數的變量count初始化爲0
  3. 讓用戶輸入一個數字a
  4. count遞增(加1)
  5. 判斷a和number的大小關係,若是a大,就輸出「大」;若是a小就輸出「小」;
  6. 若是a和number是不相等的(不管大仍是小),程序轉回到第3步;
  7. 不然,程序輸出「猜中」和次數,而後結束。

程序實現:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char *argv[]) {
    
 srand(time(0));
 int number = rand() % 100 + 1;
 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);
 return 0;
}

測試樣例:

我已經想好了一個1到100之間的數。請猜這個1到100之間的數:50
你猜的數大了。請猜這個1到100之間的數:25
你猜的數大了。請猜這個1到100之間的數:13
你猜的數小了。請猜這個1到100之間的數:19
你猜的數大了。請猜這個1到100之間的數:16
你猜的數大了。請猜這個1到100之間的數:15
太好了,你用了6次就猜到了答案。

tips:在7次內必定能猜到答案,詳見討論題

3.3 課後習題

一、 題目內容:
你的程序要讀入一系列正整數數據,輸入-1 表示輸入結束,-1 自己不是輸入的數據。程
序輸出讀到的數據中的奇數和偶數的個數。
輸入格式:
一系列正整數,整數的範圍是(0,100000)。若是輸入-1 則表示輸入結束。

輸出格式:
兩個整數,第一個整數表示讀入數據中的奇數的個數,第二個整數表示讀入數據中的偶數的
個數。兩個整數之間以空格分隔。

輸入樣例:
9 3 4 2 5 7 -1

輸出樣例:
4 2

算法分析:

  1. 讀取用戶輸入一個數記錄在變量number中
  2. 判斷number是奇數或者偶數仍是-1(能被2整除的數就是偶數,反之就是奇數)
    2-1. 若是是-1,程序繼續執行第3步
    2-2. 若是用戶輸入數字超出範圍,提示用戶從新輸入,程序回到第1步
    2-3. 若是是奇數記錄在變量odd中,並自增1,程序回到第1步
    2-4. 若是是偶數記錄在變量even中,並自增1,程序回到第1步
  3. 輸出奇數和偶數,程序結束

程序實現:

#include <stdio.h>

int main(int argc, char *argv[]) {
 int number = 0;
 int odd = 0;
 int even = 0;
    
 printf("請輸入一系列整數範圍在0-100000,輸入-1表示結束輸入:");
 scanf("%d", &number);
 while(number != -1) {  
  if(number < 0 || number > 100000){ //"||"表示 在"||"左邊表達式或者右邊的表達式任意一個成立,也就是說當number<0 或者 number>100000 時表達式成立 
   printf("您輸入的數字%d,超出範圍,不會被統計。\n", number);
  } else if (number % 2 == 0){
   even++;
  } else {
   odd++;
  }
  scanf("%d", &number);
 }
 printf("%d %d", odd, even);
}

//odd 2 
//even 4
//9 3 4 2 5 7 -1
//4 2

測試樣例:

請輸入一系列整數範圍在0-100000,輸入-1表示結束輸入:9 3 4 2 5 7 -1
4 2
--------------------------------

請輸入一系列整數範圍在0-100000,輸入-1表示結束輸入:9 3 4 2 5 7 -2 110000 -1
您輸入的數字-2,超出範圍,不會被統計。
您輸入的數字110000,超出範圍,不會被統計。
4 2
--------------------------------

二、 題目內容:

對數字求特徵值是經常使用的編碼算法,奇偶特徵是一種簡單的特徵值。對於一個整數,從個位
開始對每一位數字編號,個位是 1 號,十位是 2 號,以此類推。這個整數在第 n 位上的數
字記做 x,若是 x 和 n 的奇偶性相同,則記下一個 1,不然記下一個 0。按照整數的順序把
對應位的表示奇偶性的0和1都記錄下來,就造成了一個二進制數字。好比,對於342315,
這個二進制數字就是 001101。
這裏的計算能夠用下面的表格來表示:

按照二進制位值將 1 的位的位值加起來就獲得告終果 13。
你的程序要讀入一個非負整數,整數的範圍是[0,1000000],而後按照上述算法計算出表
示奇偶性的那個二進制數字,輸出它對應的十進制值。
提示:將整數從右向左分解,數位每次加 1,而二進制值每次乘 2。

輸入格式:
一個非負整數,整數的範圍是[0,1000000]。

輸出格式:
一個整數,表示計算結果。

輸入樣例:
342315

輸出樣例:
13

算法分析:

  1. 讀取用戶輸入的數字記錄到number
  2. 判斷用戶輸入的值,超出範圍,提示用戶,程序回到第1步
  3. 不然在合理範圍內求number的特徵值
    3-1. 若是number爲0,程序跳轉第4步
    3-2. 當前的位數digit自增1
    3-3. 取number的個位到tmp中
    3-4. 分別取tmp和digit的奇偶性記錄到isEvenTmp和isEvenDigit中
    3-5. 若是tmp和digit的奇偶性相同,累加2當前的平方數squareOfTwo到eigenvalue中
    3-6. 將number整除10,移除掉已經處理的位數,squareOfTwo乘2獲得下一個平方數,程序回到第3-1
  4. 輸出特徵值eigenvalue,程序結束

tips:有一種特殊狀況用戶第一次輸入的number就是0,那麼咱們的程序會在3-1跳轉到第4步結束,並不會計算特徵值,咱們知道0的特徵值爲0,所以咱們只須要將eigenvalue的默認值設置爲0便可。固然還有別的處理方法,具體使用哪一種咱們須要考量程序的可讀性和效率擇優選擇。你是否能想出其餘的處理方式呢?

程序實現:

#include <stdio.h>

int main(int argc, char *argv[]) {
 int number = 0;
 int digit = 0;
 int tmp = 0;
 int eigenvalue = 0;
 int isEvenTmp = 0;
 int isEvenDigit = 0;
 int squareOfTwo = 1; //2的0次方爲1 
    
 printf("請輸入一個整數範圍在0-1000000:");
 scanf("%d", &number);
 while(number < 0 || number > 1000000 ) {
  printf("%d已超出範圍,請從新輸入:", number);
  scanf("%d", &number); 
 }
    
 while(number != 0){
  digit++;
  tmp = number % 10;
  isEvenTmp = tmp % 2 == 0;
  isEvenDigit = digit % 2 == 0;
  if(isEvenTmp == isEvenDigit){
   eigenvalue += squareOfTwo;
  }
  number /= 10;
  squareOfTwo *= 2; 
 }
    
 printf("%d", eigenvalue);
 return 0;
}

測試樣例:

請輸入一個整數範圍在0-1000000:342315
13
--------------------------------

請輸入一個整數範圍在0-1000000:0
0
--------------------------------

請輸入一個整數範圍在0-1000000:12345
31
--------------------------------

請輸入一個整數範圍在0-1000000:-1
-1已超出範圍,請從新輸入:1100000
1100000已超出範圍,請從新輸入:123
7
--------------------------------

3.4 討論題(不須要掌握)

3、(3-2 循環計算)
標題:猜數遊戲
內容:爲何方法正確的話,100 之內的數最多猜 7 次就夠了?
題目分析:
當咱們知道一個數的範圍時,咱們能夠採起二分法來猜這個數字的大小:

  1. 猜這個範圍的中間值
    2.程序反饋數字是否猜對
    2-1. 若是不對程序會告訴咱們大小,就能把範圍會縮小一半而後回到第1步
    2-2. 不然咱們猜對了

咱們先小範圍測試,把範圍縮小到1-10

tips:

  • 在最壞的狀況下咱們使用二分法了4次猜出正確的數字
  • 在第一次猜5,也多是大的狀況,範圍則爲[1,4]有4個數字,可是咱們要求每次都是最壞的狀況所以取小的狀況範圍[6,10]
  • mid:用戶猜的中間數字
  • com: num和實際數字之間的關係,E=相等,B=大了,S=小了
  • ran: 縮小後的範圍 好比[4-8]表明四、五、六、七、8
  • count: 範圍內的整數個數
  • time: 用戶猜了多少次
  • 變量star表明範圍起始,變量end表明範圍結束。
  • 中間數mid的計算公式:mid = star + (end - star + 1)/2;
  • 縮小的範圍range計算公式:
    • 當用戶猜的數大了的狀況,end = mid- 1; ran = [star,end];
    • 當用戶猜的數小了的狀況,star = mid + 1; ran =[star,end];
  • rang整數個數count的計算公式: count = (end - star + 1);

那麼採用二分法最壞的狀況下須要多少次能猜中100呢?
假設咱們有n個連續的整數,它的範圍爲[1,n]
若是咱們採起從遞增猜方法從1開始猜一直猜到n,一、二、..n 那麼最壞的狀況下咱們就須要猜n次。
對於100個數來講就是要猜100次。

可是咱們用了二分法,若是是最壞的狀況,咱們的範圍中整數的個數每次都會縮小一半,n/2^一、n/2^2...n/2^m,
當n除以2的m次冪等於1時咱們就能猜出程序的那個數字了,也就是說理論上咱們須要猜m次。
所以若是有n個數,計算猜m次的公式是m=log2n; m是以2爲底n的對數。
經過計算log2100 = 6.6438561898,咱們知道理論最多猜7次就能獲得正確答案。

這裏用連續猜和二分法猜數的效率差了大約只有10倍,當這個數字的範圍是1到100萬時,連續猜須要猜100萬次,而二分法只須要猜20次,足足差了5萬倍,若是這個猜數也是由一個程序來完成,A程序使用遞增猜算法(n),B程序使用二分猜算法(log2n),當n等於100萬時他們的效率就差了5萬倍,能夠預見當n增長複雜度還會以幾何式的增長, 因而可知一個好的算法每每會給程序帶來效率的是數量級上的提高。

擴展:理論次數和實際次數

細心的讀者會發現經過上面公式計算所得的理論次數和實際次數在一些狀況下會不同。好比說當範圍爲[1,8]時最多應該猜多少次呢?經過計算咱們知道是3,可是這個結果對嗎?咱們經過表格來模擬一下程序。
範圍[1,8] 程序模擬:

結果是4次,咱們確實在第3次知道告終果,可是須要猜第4次程序纔會告訴咱們猜對了。所以實際猜的次數 rm = log2n + 1;
若是咱們要猜的整數剛好是7的話,咱們算出來的結果是 3次(捨去了log2n小數的部分,好比2.99 舍掉小數部分就是2),模擬下程序的結果是否和咱們理論計算的一致

範圍[1,7] 表格模擬:

確實是3次

範圍[1,6]:

範圍[1,5]:

範圍[1,4]:

擴展-思考: 你能用實現模擬猜數程序嗎?

相關文章
相關標籤/搜索