2.C语言初阶练习题(2)

目录

TEXT 1 (等差数列求合)

TEXT 2 (打印素数)

TEXT 3 (求最大公约数)

TEXT 4 (打印1000~2000之间的闰年)

TEXT 5 (求增长率)

TEXT 6 (逆序打印)

TEXT 7 (圆塔范围问题)

TEXT 8 (求1!+2!+...+n!)

TEXT 9 (求完数)

TEXT 10(求水仙花数(迭代和递归))


TEXT 1 (等差数列求合)

计算1+2+3+...+100的合

#include<stdio.h>

int main()
{
	int i = 0;
	int sum = 0;
	for (i = 1; i <= 100; i++)
	{
		sum += i;
	}
	printf("%d\n", sum);
	return 0;
}

这里sum+=i与sum=sum+i的意思完全一致,表示为将变量i的值加到sum上。

TEXT 2 (打印素数)

打印100~200之间的所有素数

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

int main()
{
	int i = 0;
	int j = 0;
	for (i = 100; i <= 200; i++)
	{
		int num = 0;//计数,每次循环开始时归零
		for (j = 2; j <= sqrt(i); j++)
		{
			if (i % j == 0)
			{
				num++;
			}
		}
		if (num == 0)
		{
			printf("%d\n", i);
		}
	}
	return 0;
}

如果一个数x是非素数,那么必然有(其中一个因子)<=(x的开方)。

例如:16,组成16的可能性有三种:1和16、2和8、4和4,这里1、2、4均小于等于16的开方,也就是4;当16能被1、2、4整除时,也必能被16、8和4整除。所以在写代码找因子时,只需要找到其平方根即可停止。

sqrt为开平方,一般格式为sqrt(),其头文件是<math.h>,返回值为double类型,可以用整型变量接收sqrt的返回值。

TEXT 3 (求最大公约数)

输入两个正整数,求最大公约数。

#include<stdio.h>
int main()
{
	int a = 0, b = 0;
	scanf("%d%d", &a, &b);
	if (a < b)//将大数给a,小数给b
	{
		int i = a;
		a = b;
		b = i;
	}
	while (1)
	{
		int c = 0;
		if (a % b != 0)
		{
			c = b;
			b = a % b;
			a = b;
		}
		else
		{
			printf("%d\n", b);
			break;
		}
	}
	return 0;
}

利用辗转相除法是可以最快得到最大公约数的,这里while(1)设置成死循环,找到最大公约数时break跳出循环,任何两个正整数都有公约数1,不用担心程序死循环。

介绍一下辗转相除法:例如求40和14的最大公约数

注意:辗转相除法必须是大数除以小数!所以在输入完两个数后,判断一下,将大的数赋给a,小的数赋给b。

TEXT 4 (打印1000~2000之间的闰年)

打印1000~2000年之间的闰年

#include<stdio.h>
int main()
{
	int year = 0;
	int count = 0;
	for (year = 1000; year <= 2000; year += 4)
	{
		if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
		{
			printf("%d ", year);
			count++;
		}
	}
	printf("\n1000年到2000年之间有%d个闰年\n", count);//243
	return 0;
}

闰年:四年一闰,百年不闰,四百年一闰。

用count计数,每打印一次闰年+1,最后得出1000~2000年之间有243个闰年。

因为year在for循环中初始化为1000,1000能够整除4,所以每次循环之后year+=4,这样可以大大减少循环次数。

TEXT 5 (求增长率)

假设我国国民生产总值年增长率为r=9%,计算n=10年后我国的国民生产总值比现在相比,增长了百分之多少。公式为:p=\left ( 1+r \right )^{n}

#include<math.h>
#include<stdio.h>
int main()
{
	int n = 10;
	double r = 0.09;
	double p = pow((1+r), n);
	printf("十年后增长的百分比为%.0lf%%\n", p*100);
	return 0;
}

求次方需要用到函数pow,pow函数的一般格式为pow(a,b),这里a为底数,b为指数。使用pow函数需包含头文件<math.h>。因为求的是百分比,打印时需要将p*100,并且将小数部分省略。

TEXT 6 (逆序打印)

给定整数,将其逆序打印。

#include<stdio.h>

int main()
{
	int num = 12345678;
	while (1)
	{
		if (num / 10 != 0)
		{
			printf("%d", num % 10);
			num=num/10;
		}
		else
		{
			printf("%d", num);
			break;
		}
	}
	return 0;
}

如何得到最后一个数?(例如124)

  • 第一次循环:124%10=4,将4打印,124/10=12;
  • 第二次循环:12%10=2,将2打印,1/10=0;
  • 最后:0%10=0,进入else,打印1,break跳出循环;
  • 屏幕上显示的便是421了。

TEXT 7 (圆塔范围问题)

4个半径为1的圆塔,其圆心的坐标分别为:(2,2)、(-2,2)、(-2,-2)、(2,-2)。除圆塔外,其余地方均为空地。现输入坐标x、y,判断该点是否在圆塔内(包括边界)。

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

int main()
{
	double x = 0.0, y = 0.0;
	scanf("%lf%lf", &x, &y);
	double d1 = sqrt(pow((x - 2), 2) + pow((y - 2), 2));
	double d2 = sqrt(pow((x + 2), 2) + pow((y - 2), 2));
	double d3 = sqrt(pow((x + 2), 2) + pow((y + 2), 2));
	double d4 = sqrt(pow((x - 2), 2) + pow((y + 2), 2));
	if (d1 > 1 && d2 > 1 && d3 > 1 && d4 > 1)
	{
		printf("该点位在空地上\n");
	}
	else
	{
		printf("该点位在圆塔上\n");

	}
	return 0;
}

计算点(x、y)到四个圆心的距离(sqrt和pow上面有讲,记得包含头文件<math.h>),如果四个距离均大于1(圆的半径),那么该点在空地上,否则就在圆塔上。

TEXT 8 (求1!+2!+...+n!)

输入一个正整数n,求1!+2!+...+n!的合。

#include<stdio.h>

int main()
{
	int n = 0;
	scanf("%d", &n);
	int i = 0;
	int j = 0;
	int sum = 0;
	for (i = 1; i <= n; i++)
	{
		int ret = 1;//每次重置ret=1
		for (j = 1; j <= i; j++)
		{
			ret *= j;
		}
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;
}

对于求1!+2!+...+n!,首先要会求n!是多少。

//求n!

#include<stdio.h>

int n = 0;
int j = 0;
int ret = 1;
scanf("%d",&n);

     for (j = 1; j <= n; j++)
		{
			ret *= j;
		}

上述部分代码为求n!的阶乘,再将其放到for循环中,将每次阶乘的结果累加即可。

这里注意,ret为每次求n的阶乘时的计数变量,当求完该阶乘时,在求下一个数的阶乘之前要将其重置为1,否则ret会保留历史数据,导致结果越来越大。

TEXT 9 (求完数)

一个数如果等于它的因子之和,这个数就被称为完数。

例如:6的因子为1、2、3,1+2+3=6,所以6是完数。(因子就是所有可以整除这个数的数,但不包括这个数的自身)

求1000以内的所有完数,并按如下格式输出:

6是完数,它的因子是:1 2 3

#include<stdio.h>

int main()
{
	int i = 0;
	int j = 0;
	for (i = 1; i < 1000; i++)
	{
		int sum = 0;
		int k = 0;
		int arr[50] = { 0 };//存放因子
		for (j = 1; j < i; j++)//判断完数部分
		{
			if (i % j == 0)
			{
				sum += j;
				arr[k++] = j;//后置++,先使用,再++
			}
		}
		if (sum == i)
		{
			printf("%d是完数,它的因子是:", i);
			for(k=0;arr[k]!='\0';k++)//遍历打印因子
			{
				printf("%d ", arr[k]);
		    }
			putchar('\n');
		}
	}
}

创建一个arr的数组来将因子存放起来,因为要求1000以内的完数,所以这里arr的空间不能太小,要留出足够大的空间来存放因子。用两个for循环进行完数的判断,然后将完数的因子遍历打印出来即可。

TEXT 10(求水仙花数(迭代和递归))

求所有的水仙花数,水仙花数是一个三位数,各位数字的立方和等于其本身。

例如:153=1^{3}+5^{3}+3^{3},故153是水仙花数。

迭代方法:

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

int main()
{
	int i = 0;
	int j = 0;
	for (i = 100; i < 1000; i++)
	{
		int ret = i;
		int sum = 0;
		for(j = 1; j <= 3; j++)//求各位数的立方和
		{
			sum += (int)pow((ret%10), 3);//(int)为强制类型转换
			ret = ret / 10;
		}
		if (sum == i)//立方和等于其本身,便是水仙花数
		{
			printf("%d是水仙花数\n", i);//153 370 371 407
		}
	}
	return 0;
}

因为水仙花数是三位数,所以在大循环中,i的范围设定为100 <= i < 1000。

然后开始每次求i的各位数的立方和:为了不改变i的值,创建一个ret变量,将i值赋给ret,每次循环结束后,将会赋予ret新的i值。

这里的思想与TEXT 6的逆序打印有些类似,每次找到最后一位数,求其立方和,用sum记录下来,记录完后将ret/10。第一次循环找到个位数的立方和,第二次循环找到十位数的立方和,第三次循环找到百位数的立方和,因为i为三位数,所以只需要循环三次即可。

sum += (int)pow((ret%10), 3),这里(int)为强制类型转换,将pow的double类型返回值转换为整型,让sum接收。(当然,即使不转换在VS编译器中也能成功运行)

通过上述代码可以观察到,在整个函数中,我们创建了两个局部变量来存放我们过程中的结果,整个代码写的非常冗杂,利用递归的方法可以简化我们的代码。

递归方法:

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

int flo(int i)
{
	int sum = 0;
	if (i / 10 != 0)
	{
		sum = flo(i / 10);
	}
	return sum += pow((i % 10), 3);
}

int main()
{
	int i = 0;
	for (i = 100; i < 1000; i++)
	{
		if (flo(i) == i)
		{
			printf("%d是水仙花数\n", i);
		}
	}
	return 0;
}

递归的思想稍微有些抽象,这里用图展示更好理解一些。

例如:求125各位的立方

  • 首先,125可以拆分为12和5,求5的立方+求12各位的立方
  • 其次,12可以拆分为2和5,求2的立方+求1的各位的立方
  • 最后,1就它自己,求1的立方。

下面为递归调用时的流程图:

return sum += pow((i % 10), 3);可以理解为:

1、sum += pow((i % 10), 3);

2、return sum;

递归:就是利用函数自己调用自己,从而实现化繁为简,大事化小。

递归主要分为递、和归两部分,先递再归。

每次递归时,要越来越接近if表达式中的界限,若是没有界限,那么将无限递归,陷入死循环。

例如125递归时,当1/10=0时,表达式为假,便不再调用函数,开始进行下一条语句,计算sum的值,然后return到上一个递归中,直到全部return完,递归结束。

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

是元笙阿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值