程序设计入门-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]:

扩展-思考: 你能用实现模拟猜数程序吗?

相关文章
相关标签/搜索