本期主打的就是通过一些简单地练习题来加深对分支和循环语句应用:


1.输入三个数字,依次由大到小打印出来:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int a = 0;
	int b = 0;
	int c = 0;
	int k;
	scanf("%d%d%d", &a, &b, &c);
	if (a < b)
	{
		k = a;
		a = b;
		b = k;
	}
	if (a < c)
	{
		k = a;
		a = c;
		c = k;
	}
	if (b < c)
	{
		k = b;
		b = c;
		c = k;
	}
	printf("%d %d %d\n", a, b, c);
	return 0;
}

大概思路就是:

不管用户输入什么数字,都先把这三个数字按大小顺序排好,

最后再将其打印出来


2.求1000年~2000年之间的闰年(含1000年和2000年):

首先我们要理解什么是闰年?

知识小补充:

能被4整除并且不能被100整除的是闰年;

能被400整除的是闰年。

因此,我们就能按照以上这三个条件来编写代码:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<math.h>

int main()
{
	int i, count;
	count = 0;
	for (i = 1000; i <= 2000; i++)
	{
		if ((i % 4) == 0 && (i % 100) != 0)
		{
			printf("%d ", i);
			count++;
		}
		else if (i % 400 == 0)
		{
			printf("%d ", i);
			count++;
		}
	}
	printf("\n count= %d 个\n", count);
	return 0;
}

当然如果熟练掌握“并”与“或”的关系的话,

我们可以直接将三个条件放在一起,比如:

if ((i % 4) == 0 && (i % 100) != 0 || (i % 400) == 0)

这样代码的执行效率也会更高。


3.求出100~200的素数:

什么是素数?

素数又称质数,

一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;

否则称为合数(规定1既不是质数也不是合数)。

因为求的是100~200间的素数,那么必定大于1,

因此主要的关键就是找出除1和它自身之外,是否还有其它数可以将其整除

根据这个逻辑,我们可以编写出如下代码:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<math.h>

int main()
{
	int i,k;
	int count = 0;
	for (i = 100; i <= 200; i++)
	{
		for (k = 2; k < i; k++)
		{
			if ((i%k)==0)
			{
				break;
			}
		}
		if (k == i)
		{
			printf("%d ", i);
			count++;
		}
	}
	printf("\n\n%d\n", count);
	return 0;
}

这个代码能不能再优化呢?其实是可以的。

我们想如果一个数能被除了1和其本身之外的数整除,

那么这个数必定能拆分成2个因式。

比如16=2x8=4x4,再比如36=2x18=3x12=4x9=6x6

假设一个合数为a,其因式为b,c(b<c)

不难发现存在这样一个规律:

b≤√a

假设要在1000~2000这样的一个范围内寻找素数,

知道了这个逻辑就不需要将循环从1000一直执行到2000了,

只需要将循环执行到1500就可以了;

如此代码的效率又高了一点,优化后:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<math.h>

int main()
{
	int i, k;
	int count = 0;
	//sprt - 开平方的数学库函数
	for (i = 100; i <= 200; i++)
	{
		for (k = 2; k <= sqrt(i); k++)
		{
			if ((i % k) == 0)
			{
				break;
			}
		}
		if (k >sqrt(i))
		{
			printf("%d ", i);
			count++;
		}
	}
	printf("\n\n%d\n", count);
	return 0;
}

代码运行结果如下:

分支和循环语句(3)_#include


当然,关于素数的求解上述代码都算作一种方法:试除法

我们只不过是在调整素数的查找范围罢了。

还有更牛逼的算法比如埃拉托斯特尼筛和线性筛-欧拉筛,

具体可参考这篇文章:素数求解的N种方法


4.求最大公约数:

分支和循环语句(3)_#include_02


如图,第一种方法叫:更相减损之术

按照图示逻辑,可以写出如下代码:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<math.h>

int main()
{
	int m, n, x1, x2;
	scanf("%d%d", &m, &n);
	x1 = m;
	x2 = n;
	while (m != n)
	{
		if (m > n)
		{
			m = m - n;
		}
		else
		{
			n = n - m;
		}
	}
	printf("最大公约数为:%d\n", m);
	printf("最小公约数为:%d\n", x1 * x2 / m);
	return 0;
}


图中第二种方法叫:辗转相除法

其核心原理是:

两个数的最大公约数等于其中较小的数字和二者之间余数的最大公约数

按照图示逻辑,就是将两数相除取余,将90放到378的位置,

余数18放到原90的位置,再相除取余,如此循环,直到余数为0.

按照这么一个思路,可知其代码实现逻辑如下:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<math.h>

int main()
{
	int m, n, r;
	scanf("%d%d", &m, &n);
	while (r = m % n)
	{
		m = n;
		n = r;
	}
	printf("%d\n", n);
	return 0;
}

对比两法,我认为法二更容易理解,代码的实现也比较简单。


5.猜数游戏:

//电脑生成一个随机数

//猜数字

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include<string.h>

void menu()
{
	printf("******************************\n");
	printf("******  1.play  2.exit  ******\n");
	printf("******************************\n");
}

void game()
{
	int ret, guess;
	ret = rand() % 100 + 1;//生成随机数
	while (1)
	{
		printf("1~100随便猜个数吧\n");
		scanf("%d", &guess);
		if (guess > ret)
			printf("猜大了哦\n");
		else if (guess < ret)
			printf("猜小了哦\n");
		else
		{
			printf("恭喜你,猜对了!\n");
			break;
		}
	}
}

int main()
{
	int input = 0;
	//拿时间戳来设置随机数的生成起始点
	srand((unsigned int)time(NULL));
	//时间戳	
	//当前计算机的时间-计算机的起始时间(1970.1.1.0:0:0)=(xxxx)秒
	//上述计算得出的秒数即为时间戳
	do
	{
		menu();
		printf("请选择(1/2)>:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 2:
			printf("已退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input != 2);
  
	return 0;
}

实际上整个游戏的设计并不难,重点是如何制作一个随机数发生器。

在C语言中,我们一般使用rand()来生成一个随机数

它会随机生成一个位于0~RAND_MAX(32767)之间的整数。

但当我们多次单纯地运行rand()来生成随机数时,会发现每次产生的随机数都一样

实际上,rand()生成的随机数是伪随机数,是根据某个数值按照某个公式推算出来的

这个数值我们称之为"种子",种子与随机数之间的关系呈正态分布:

分支和循环语句(3)_分支与循环_03

种子每次启动计算机时是随机的,但一旦计算机启动后它就不再变化了;

即每次启动计算机后,种子为定值,因此根据公式推算出来的随机数也是固定的。

如此,想要生成十分接近随机的数值,我们就得通过srand()函数来重新“播种”。

srand()函数的语法为:

void srand (unsigned int seed);

要想种子不断地发生变化,我们可以拿时间作为参数,

在不同的时间播种,每次产生的种子也不同。

srand((unsigned)time(NULL));

游戏运行结果如下:

分支和循环语句(3)_分支与循环_04


6.关机程序的编写:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

int main()
{
	char input[20] = { 0 };
	system("shutdown -s -t 60");
again:
	printf("输入:\"我是猪\",否则你的电脑将在1分钟内关机!\n请输入:");
	scanf("%s", input);
	if (strcmp(input, "我是猪") == 0)
	{
		system("shutdown, -a");
	}
	else
	{
		goto again;
	}
	return 0;
}