C语言实现的一些小案例

1. 凑硬币

1.1 所有情况

	int x = 0;
	printf("请输入你想要凑出的元数:");
	scanf("%d", &x);
	for (int one = 0;one<x*10; one++)
	{
		for (int two = 0; two < x * 5; two++)
		{
			for (int five = 0; five < x * 2; five++)
			{
				if (one + two * 2 + five * 5 == x * 10)
				{
					printf("%d个1角,%d个2角,%d个5角可以得到%d元\n", one, two, five, x);
				}
			}
		}
	}

运行结果(以2元为例):
![在这里插入图片描述](https://img-blog.csdnimg.cn/04e04da5dc3e4326bda3f73d7a79646e.png

1.2 某一种情况

得到一种结果后,便跳出嵌套循环。

1>接力break

在循环中加入break,当符合条件时便终止循环。

for (int one = 0;one<x*10; one++)
	{
		for (int two = 0; two < x * 5; two++)
		{
			for (int five = 0; five < x * 2; five++)
			{
				if (one + two * 2 + five * 5 == x * 10)
				{
					printf("%d个1角,%d个2角,%d个5角可以得到%d元\n", one, two, five, x);
					exit = 1;
					break;
				}
			}
			if (exit)break;
		}
		if (exit)break;
	}

运行结果:
在这里插入图片描述
若循环嵌套过多,这种方法不免有些麻烦

2>goto语句

在想要跳转的地方输入同样的标号,即可实现跳转。

for (int one = 0; one < x * 10; one++)
	{
		for (int two = 0; two < x * 5; two++)
		{
			for (int five = 0; five < x * 2; five++)
			{
				if (one + two * 2 + five * 5 == x * 10)
				{
					printf("%d个1角,%d个2角,%d个5角可以得到%d元\n", one, two, five, x);
					goto out;
				}
			}
		}
	}
	out:

2. 平均数

对于如果求平均数,相信大家信手拈来,所以此处的平均数为:未知数量的平均数,可以利用循环输入的方式完成,同时统计输入的数字个数,便可得到结果。

while (scanf("%d", &x) !=EOF)

每输入一个数,需要敲一次回车得到当前已经输入的数值的平均。

<暂时还未想到:将所有的数值输入之后,得到一个最终值>

	int count = 0;
	int x = 0;
	int sum = 0;
	double aver = 0.0;
	while (scanf("%d", &x) !=EOF)
	{
		count++;
		sum += x;
		aver = 1.0 * sum / count;
		printf("当前平均值:%f\n", aver);
	}

运行结果:
在这里插入图片描述

3. 最大公约数

3.1 枚举法

从最小的那个数开始递减,若某一个可以同时被这这个数整除,则此数为最大公约数。

int a = 0;
	int b = 0;
	int min = b;
	scanf("%d %d", &a, &b);
	if (a < b)
	{
		min = a;
	}
	while (1)
	{
		if (a % min == 0 && b % min == 0)
		{
			printf("最大公约数:%d\n", min);
			break;
		}
		else
		{
			min--;
		}
	}

运行结果:
在这里插入图片描述

不难看出此种方法有些麻烦。

3.2 辗转相除

辗转相除法(欧几里得算法):以除数和余数反复做除法运算,当余数为0时,取当前算式的除数为最大公约数。

 a   b  t       
12  18  12		12/18=0(12)
18  12  6		18/12=1(6)
12  6   0		12/6=2(0)
int a = 0;
	int b = 0;
	int temp = 0;
	scanf("%d %d", &a, &b);
	while (temp)
	{
		temp = a % b;
		a = b;
		b = temp;
	}
	printf("最大公约数:%d\n", b);

4. 素数

素数又称质数 。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数。即ta的因数只有1和本身。
所以在验证ta是否为素数时,便可以从2到num-1取余,若在此范围内有一个结果为0则不是素数。
再一想,也可以从2到num/2呀!在这里以8为例:既然有2×4=8,那么也应有4×2=8,即因数一个会较大另一个会较小。
类推一下,ta的最大因数会在ta的开平方附近,所以是2-sqrt(num),这样一来,循环的次数大大减少了。

4.1确定某一数是否为素数:

若在循环体内,有一个取余的结果为0,则该数就不是素数,循环终止。最后只要比较循环次数与sqrt(num)的大小,便可知道该数是否为素数了。

	int num = 0;
	int i = 0;
	printf("请输入要确定的数字:");
	scanf("%d", &num);
	for (i = 2; i <= sqrt(num); i++)
	{
		if (num % i == 0)
		{
			break;
		}
	}
	if (i > sqrt(num))
	{
		printf("%d是素数\n", num);
	}
	else
	{
		printf("%d不是素数\n", num);
	}

运行结果:
在这里插入图片描述

4.2 确定某一范围内的素数:

先输入想要确定的范围,用嵌套循环实现所有数字的判断。
此时,判断是否为素数时,并没有选用上一种方式(比较循环次数与sqrt(num)),而是借助一个新的变量:若有其他因数,则将其赋值0并终止循环。
要注意该变量设置的位置,在循环内部(每次检验一个新的数字是否为素数时,此时的IsPrime为1)
在此我还另加入一个if语句来人为的判断1是否为素数<没有想到其他好的方法,求助求助>
另,定义了一个变量来实现素数个数的统计与输出时的换行。

	int num1 = 0;
	int num2 = 0;
	int count = 0;
	printf("请输入要确定的范围:");
	scanf("%d %d", &num1, &num2);
	for (int j = num1; j <= num2; j++)
	{
		int IsPrime = 1;
		if (j == 1)
		{
			IsPrime = 0;
		}
		for (int i = 2; i <= sqrt(j); i++)
		{
			if (j % i == 0)
			{
				IsPrime = 0;
				break;
			}
		}
		if (IsPrime)
		{
			printf("%2d",j);
			count++;
			if (count % 10 == 0)
			{
				printf("\n");
			}
			else
			{
				printf(" ");
			}
		}
	}
	printf("\n共有%d个\n", count);

运行结果(以1到100为例):

4.3确定前n个素数

变量j为素数的当前个数,每当输出一个素数,j加1。变量n为数字开始值,即从2开始开始确定素数。

	int num = 0;
	printf("请输入要显示的素数个数:");
	scanf("%d", &num);
	int n = 2;
	for (int j = 0; j < num; n++)
	{
		int IsPrime = 1;
		for (int i = 2; i <= sqrt(n); i++)
		{
			if (n % i == 0)
			{
				IsPrime = 0;
				break;
			}
		}
		if (IsPrime)
		{
			printf("%d ", n);
			j++;
		}
		if (j % 10 == 0)
		{
			printf("\n");
		}
	}

运行结果:
在这里插入图片描述
除此之外,对于素数还有很多种确定玩法,如:某个数字之后的n个素数是;某个数字之前的n个素数等等

5. 前n项和

分数的求和:1 + 1/2 + 1/3 + 1/4 + …
仔细看就会发现,上述式子中的分母是1逐次加一的,而分子又都是1,所以不难想到此时我们可以借助循环语句。

	int n = 0;
	double sum = 0.0;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		sum += 1.0 / i;
	}
	printf("f(%d)=%f\n", n, sum);

值得注意的是,要知道这是分数的求和,所以他的值要设为double类型的,此时我们就可以知道任意n项分数的和了。

除此之外,我们可能还会遇到这种类型的式子:1 - 1/2 + 1/3 - 1/4 + … ,不难发现,此式与上述式子类似,只是分母为偶数时,该项是被减去的。
很容易就会想到,将该式子的项分为两类:奇数项和偶数项。所以可以利用if语句来求和。
再想想?“ + ”和“ - ”是交替出现的,那么利用一个变量,让ta实现的每一项的正负交换,就相当于1 + (- 1/2) + 1/3 + (- 1/4) + … 。

	int n = 0;
	double sum = 0.0;
	int flag = 1;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		sum += flag * 1.0 / i;
		flag = -flag;
	}
	printf("f(%d)=%f\n", n, sum);

其中,也可以将此变量改为double类型变量,这样,求和式中的1.0也就可以删去了。

6. 整数求逆

将数字的个位剥离出来,将其乘10,再加上十位…

123
123%10---3(个)   3				123/10---12
12%10---2(十)    3*10+2--32	12/10---1
1%10---1(百)     32*10+1--321	1/10---0(循环结束)

将700 -->7,即数字中的0不显示:

int num = 0;
	scanf("%d", &num);
	int temp = num;
	int ret = 0;
	while (temp > 0)
	{
		int a = temp % 10;
		ret = a + ret * 10;
		temp /= 10;
	}
	printf("%d逆序之后:%d", num, ret);

将700 -->007,即数字中的0显示:

	int num = 0;
	scanf("%d", &num);
	int ret = 0;
	while (num > 0)
	{
		int a = num % 10;
		printf("%d", a);
		num /= 10;
	}

我们可以看到除了while循环中的操作有些许不同外,还有一处明显不同,即第一个代码中将数字赋给了另一个变量,之后的操作中使用的变量也是新的变量。
为什么呢?因为这样做,如果我们仍想要使用开始的数据时,依然可以使用。而直接对原始数据进行操作时,如果仍想显示原始数据的话,不免有些麻烦。

7.整数分解

借助之前的整数求逆,然后将逆反之后的数字从个位开始依次将数字剥离下来,即可将该整数分解。
不过,这种方法有个缺陷,那就是对于整百的数字,如100,则无法显示后面的0。用另一种整数求逆的方法(100 -->0 0 1)也无法获得,因为这种方法是将每一位的数字依次输出的。
之前的代码就是上面整数求逆的第一种方法,所以就不再重复。下面的代码则是之后的代码。

while (ret)
	{
		int b = ret % 10;
		printf("%d", b);
		ret /= 10;
		if (ret)
		{
			printf(" ");
		}
		else
		{
			printf("\n");
		}
	}

正向分解数字:先确定该数字的位数,当数据类型为int时,相除可以得到该数的最高位,然后将最高位剥离之后,循环该操作。

123
123/100 - - - 1		123%100 - -23		100/10 - -10
23/10 - - - 2		23%10 - -3			10/10 - -1
3/1 - - - 3	

判断位数时,要注意循环条件

	int num = 0;
	scanf("%d", &num);
	int digits = 1;
	int temp1 = num;
	//确定要除的数字,判断位数
	while (temp1 > 9)
	{
		digits *= 10;
		temp1 /= 10;
	}
	int temp2 = num;
	while (digits)
	{
		//开始分解
		int a = temp2 / digits;
		printf("%d", a);
		temp2 %= digits;
		digits /= 10;
		//数字间用空格隔开,最后一个数字后无空格
		if (digits == 0)
		{
			printf("\n");
		}
		else
		{
			printf(" ");
		}
	}

8. 生成随机数

8.1 rand函数

在返回值类型这里插入图片描述无参函数,返回值类型为int(整型) 头文件:<stdlib.h>
在这里插入图片描述rand函数返回一个介于0和RAND_MAX(32767)之间的伪随机整数。在调用rand函数之前使用strand函数来设置伪随机数生成器。

8.2srand函数

在这里插入图片描述strand函数:设置一个随机开始的起点,参数为整型

若要生成的数字随机,则需要一个变化的或是随机参数,而时间是一直在变化的。

8.3Time函数

在这里插入图片描述返回值类型为整型

8.4代码

srand((unsigned int)time(NULL));
int ret = rand();

9. 多个字符从两端开始显示

9.1 代码所实现功能的简要概述

多个字符从两端移动,向中间汇聚。

最终要显示you are smart!!!!这一字符串,具体实现过程如下:
由y###############!
到yo#############!!
.....
最终you are smart!!!!

9.2 具体代码

	char arr[] = { "you-are-smart!!!!" };
	int sz = sizeof(arr) / sizeof(arr[0]);//计算字符串的长度
	int left = 0;
	int right = sz-2;//字符串以'\0'为结束标志,下标从0开始
	int i = 0;
	int j = 0;

	for (i = 0; i < ((sz-1)/2); i++)
	{
		int a = left + i;
		//字符从左端向中间显示
		for (j = 0; j <= a; j++)
		{
			printf("%c", arr[j]);
		}
		//中间的以空格形式显示
		for (i=0 ; i < (sz-1-2*(a+1)); i++)
		{
			printf(" ");
		}
		i = a;//上面的for循环已经使得i为(sz-1-2*(a+1))-1
		       //若没有此操作,整个大循环只会进行一次
		       //
	    //字符从右端向中间显示
		int b = right - i; 
		for (j = (sz - 2); j >= b; j--)
		{
			printf("%c", arr[b+sz-2-j]);//以正常顺序显示,先显示前面的
		}	
		printf("\n");//每显示一行就换行
	}
	//若字符串(我们看到的)个数为奇数个,则大循环结束后显示整个字符串
	//若没有此操作,直接在循环中完成,则中间一个字符会显示2次
	if (sz % 2 == 0)
	{
		for (i = 0; i < (sz - 1); i++)
		printf("%c", arr[i]);
	}

9.3 运行结果

在这里插入图片描述

10. 青蛙跳台阶

问题描述:
青蛙每次可以1或2个台阶,问:青蛙跳n阶台阶,共有多少次跳法?

10.1 解题思路

台阶数(n)方法
11 —(1)
21/1,2—(2)
31/2,1/1/1,2/1—(3)
41/1/2,2/2,1/2/1,1/1/1/1,2/1/1—(5)
51/2/2,1/1/1/2,2/1/2,1/1/2/1,2/2/1,1/2/1/1,1/1/1/1/1,2/1/1/1—(8)

当台阶数为1时,只有一种跳法;
当台阶数为2时,可以1个1个的跳,也可以一次跳2个。
当台阶数为3时,可以这样想:n=1的跳法 + 一次跳2个,n=2的跳法 + 一次跳1个。因为台阶数为1和2的跳法,我们已经全部考虑到了,所以在n=1和2的基础上,分别跳2个和1个即可。

10.2 思路回归

可以联想到斐波那契数列【1,1,2,3,5,8,13,21,34,55…】
斐波那契数列是,第n个数=n-1个数+n-2个数(n>2)
这与青蛙跳台阶的思路是一致的。

10.3 代码

int Frog(int n)
{
	if(n==1)
		return 1;
	else if(n==2)
		return 2;
	else
		return Fib(n-1)+Fib(n-2);
}

不过这种方法在台阶数较小的时候,可以很方便的计算出结果。但是当台阶数过大时,会造成很多重复的运算。以斐波那契数为例,创建变量来观察一个重复的运算有多少次。

int c=0;
int Fib(int n)
{
	//得到第n个斐波那契数时,第5个斐波那契数会计算多少次
	if(n==5)
		c++;
	if(n<2)
		return 1;
	else
		return Fib(n-1)+Fib(n-2);
}

可以
可以看到,当我们想要知道第30个斐波那契数是多少时,第5个斐波那契数需要计算121393次,这是一个非常大的数字。因此在计算较大的数时,我们需要改变一下思路。
这种递归的思路是,为了计算当前值,我们不得不去计算上一个值和上上一个值,以此类推直到我们需要的这2个值是n=1和n=2。
而我们人工手算斐波那契数时,是知道了n=1和n=2的值,两者相加得到n=3的值,依次计算,直至得到想要的斐波那契数,所以我们也可按照这种思路来得到一个新的程序。

int Fib(int n)
{
	int a=1;//第1个(n-2)
	int b=1;//第2个(n-1)
	int c=1;//第n个
	while(n>2)
	{
		c=a+b;//按照计算规则,(n-1)+(n-2)得到第n个
		a=b;//上一次运算中的(n-1),在这一次运算中成为(n-2)
		b=c;//上一次运算中的(n),在这一次运算中成为(n-1)
		n--;//计算第n个,共需要n-2次加法运算;
	}
	return c;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好运包围

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值