【C语言习题演练】分支与循环——阶乘问题

大家好,很高兴又和大家见面了!!!今天咱们继续分享分支与循环的相关习题,以此来巩固这个章节的知识点。下面就是我们今天要分享的题目: 

  1. 计算n的阶乘。
  2. 计算1!+2!+3!+……+10!

有兴趣的朋友可以先自己动手完成习题,再回过来看内容解析。接下来,我们就开始讲解咱们今天的习题; 

习题演练——分支与循环篇

1.计算n的阶乘

看到这一题,我们可以马上想到n的阶乘公式:n!=1*2*3*……*(n-2)*(n-1)*n。

计算公式我们有了,接下来就是通过代码来实现这个公式了。

题目解析

第一步:我们需要实现这个公式的话,我们先要找出我们需要的元素:

一个是项数n,一个是接收乘积的n!;

但是我们在编译器中可不能直接定义n!=0,因为在C语言中!=是一个关系操作符,所以我们不能定义n!这个标识符,这里我们就用阶乘的英文factorial的前三个字母fac来表示阶乘;

第二步:我们既然是要求n!那这个n的值应该是根据我们的具体要求来定的,这里我们可以通过输入函数scanf来实现;

第三步:我们既然计算1~n之间的乘积的话,我们是不是需要先生成这些数,才能按要求完成计算呀,这里我们可以参考上一篇中打印1~100之间的奇数这一题来编写代码生成1~n之间的数,这里就需要用到循环语句来实现;

第四步:我们最终要完成的是数与数之间的乘积,既然如此,那我们就需要使这些数相乘,也就是我们需要得到i*(i+1)*(i+2)*……*(i+n-i-1)*(i+n-i),这个应该怎么实现呢?

这里我们就需要展开介绍一下循环语句的特点——每进行一次循环,循环内的指令都会执行一次。

也就是说,i从1开始进入循环的话,那就只生成了1,在递进后再次进入循环才能得到2,那我们现在就需要把每一次生成的数都给存储下来,并且还需要让这些数相乘,既然如此,那我们就通过表格来理清一下思路:

循环语句执行流程
循环次数变量i值存储值fac值
1i=111
2i=221*2
3i=331*2*3
……
n-2i=n-2n-21*2*3*……*(n-2)
n-1i=n-1n-11*2*3*……*(n-2)*(n-1)
ni=nn1*2*3*……*(n-2)*(n-1)*n

从表格中我们可以看到,我们在每一次循环如果fac的值都能与生成的值相乘的话,是不是就可以解决问题了。如果是这样,在第一次循环时我们又应该怎么处理呢?

有朋友很快就想到了,如果我将fac的值初始化为1,让1来与1相乘是不是就可以了呢?也就是我们第一次循环执行的是fac=1*1,第二次是1*1*2以此类推一直到第n项是不是就可以了。

代码实现

有了具体思路,我们就可以开始编写代码了:

//计算n!
//方法1——循环(顺序求解1*2*3*……*n)
int main()
{
	//定义变量n来接收输入值
	int n = 0;
	//定义阶乘变量fac来接收阶乘值,并初始化为1
	int fac = 1;
	//通过输入函数来输入我们需要求解的值
	scanf("%d", &n);
	//定义循环变量i来控制循环次数
	for (int i = 1; i <= n; i++)
	{
		//存储每一次循环的阶乘值
		fac = fac * i;
	}
	//打印阶乘值
	printf("%d!=%d\n", n, fac);
	return 0;
}

这样编码,我们的思路就很清晰了,下面就是检验结果的时间了,我们知道5!=1*2*3*4*5=120,下面我们来测试一下,输入5,看能不能算出5!:

可以看到,代码很好的实现了我们的需求。到这里,这一题就被我们完成了,在开始下一题之前,我来给大家分享一下几种其它的编码思路;

思路拓展 

刚刚第一种思路,我们是按照顺序思维来进行分析的,那我们可不可以通过逆向思维来分析,前面我们从1开始往后求阶乘,现在我们来尝试一下从n来往前求阶乘。在编码前,我们还是通过表格来理清思路:

循环语句执行流程
循环次数变量i值存储值fac值
1i=nn1*n
2i=n-1n-11*n*(n-1)
3i=n-2n-21*n*(n-1)*(n-2)
……
n-3i=331*n*(n-1)*(n-2)*……*3
n-2i=221*n*(n-1)*(n-2)*……*3*2
n-1i=111*n*(n-1)*(n-2)*……*3*2*1

从表格中我们可看到像这样编码,确实可行,接下来,我们直接开始编写代码:

//方法2——循环(逆序求解:n*(n-1)*(n-2)*……*1)
int main()
{
	int n = 0;
	int fac = 1;
	scanf("%d", &n);
	//i需要从n一直减到1,所以i的最小值应该为1
	for (int i = n; i >= 1; i--)
	{
		fac = fac * i;
	}
	printf("%d!=%d\n", n, fac);
	return 0;
}

代码编写好之后,我们来测试一下,这样编写的代码是否能实现我们的功能:

我们可以看到,像这样编写代码确实也能解决这个问题。接下来我们围绕这两个思路还有没有其它的编写方法呢?下面我把我自己想到的方式分享给大家:

 //方法3——循环——逆序求解(n*(n-1)*(n-2)*……*1)
int main()
{
	int n = 0;
	int fac = 1;
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		fac *= (n - i);
	}
	printf("%d!=%d\n", n, fac);
	return 0;
}
 
//方法4——定义宏——顺序求解(1*2*3*……*n)
#define Fac(n,i,fac) (n>i?(fac*=i):(fac*=n))
int main()
{
	int n = 0;
	int fac = 1;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		fac = Fac(n, i, fac);
	}
	printf("%d!=%d\n", n, fac);
	return 0;
}
 
 
 
//方法5——定义宏——逆序求解(n*(n-1)*(n-2)*……*1)
#define Fac(n,i,fac) (i==n?(fac*=n):(fac*=i))
int main()
{
	int n = 0;
	int fac = 1;
	scanf("%d", &n);
	for (int i = n; i >= 1; i--)
	{
		fac = Fac(n, i, fac);
	}
	printf("%d!=%d\n", n, fac);
	return 0;
}

//方法6——定义宏——逆序求解(n*(n-1)*(n-2)*……*1)
#define Fac(n,i,fac) (i==n?(fac*=1):(fac*=(n-i)))
int main()
{
	int n = 0;
	int fac = 1;
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		fac = Fac(n, i, fac);
	}
	printf("%d!=%d\n", n, fac);
	return 0;
}

 这里有几点需要说明一下:

1.目前我们是在分支与循环的章节来求解这一题,所以使用的主要还是循环来实现,等后面我们介绍完函数篇章,我们还可以来用函数递归与迭代的方式来求解这一题,这里我就不继续展开分享了;

2.这几种方法主要还是通过定义宏来实现的,零基础的朋友们可能还不知道什么是定义宏,函数也不太了解,没关系,这里我们只需要知道有这种方式求解就行,目前不需要深究;

3.除了上述的这些方法,肯定还会有其它的方法来求解这一题,具体可以如何做到,这就将评论区留给朋友们你们自行分享吧。

第一题的求解过程我们就分享到这里了,接下来我们继续来看第二题;

2.计算1!+2!+3!+……+10!

在上一题中,我们求的是第n项的阶乘,现在题目需要我们求解的是前10项的和,这里我们有应该怎么去解决呢?做题前先对题目进行解析,理清解题思路;

题目解析

第一步:要求前10项的和,我们应该先生成前10项才行,每一项的生成可以参考上一题;

第二步:我们要将每次生成的阶乘给存储起来,存储的方式肯定是通过变量,这里我们依旧可以使用fac来进行存储阶乘值;

第三步:我们需要求的是阶乘的和,这里我们可以通过定义变量sum来存储阶乘的和,具体如何接收,这里还是通过表格来理解:

循环语句执行流程
循环次数变量i值fac值sum值
1i=11*11!
2i=21*1*21!+2!
……
10i=101*1*2*……*101!+2!+……+10!

通过表格,现在我们就看的很清楚了,此时我们只需要在求阶乘的代码内加上一个sum并与fac做加法,问题就解决了,这里我们可以看到,第一次循环sum是需要存储1!,既然这样,sum的初始值就只能是0,才不会改变fac的大小;

代码实现

有了思路,那我们就开始进行编码吧:

//计算1!+2!+……+10!
//方法1——循环(顺序求解)
int main()
{
	//要求的是前10项的和,所以n的值可以确定为10
	int n = 10;
	//定义阶乘变量
	int fac = 1;
	//定义求和变量
	int sum = 0;
	//通过循环生成每一项
	for (int i = 1; i <= n; i++)
	{
		//通过阶乘变量存储阶乘值
		fac *= i;
		//通过打印函数打印出阶乘生成过程
		printf("%2d!=%-8d  ", i, fac);
		//通过求和变量存储阶乘之和
		sum += fac;
		//通过打印函数打印出阶乘求和过程
		printf("前%2d项阶乘的和sum=%-8d\n", i, sum);
	}
	printf("前10项阶乘的和sum=%d\n", sum);
	return 0;
}

这里可能会有朋友好奇,为什么我打印的是后是%2d和%-8d,别着急,我们先看一下打印效果再来给大家解释:

 从打印结果中我们可以看到,此时我们不仅完成了题目要求,我们还展示了阶乘的生成过程与求和过程,并且排版是不是很整齐啊。这个排版就是我要给大家介绍的%nd和%-nd;

打印整型(%nd与%-nd)

我们可以看到当我们打印%2d时,打印结果的排版是呈现右对齐的状态,数的左边加入了一个空格;

当我们打印%-8d的时候,我们可以看到,打印结果是呈现左对齐的状态,数的右边加入了1~7个空格;

前一个结果和后一个结果之间有10个字符的举例,多出来的两个空格是因为我在打印结果中加入了两个空格;

由此我们可以得出整型打印的以下结论:

当打印%nd或%-nd时,输出结果会根据n的大小来决定打印的位数;

当打印的是%nd,输出结果会成右对齐状态,如果实际结果的位数<n,则打印时不足的位数会用空格填补;

当打印的是%-nd,输出的结果会成左对齐状态,如果实际结果的位数<n,则打印时不足的位数会用空格填补;

 如图所示,图中就很形象的展示了整型打印的两种对齐方式,这里我是拿n=8举例,并不是说整型打印最多只能让n=8;

思路拓展

这题到现在我们就已经解决完了,但是这题的解题方式肯定不止这一种,同样的我们也可以按顺序和逆序两种方式来求解,求解的具体方法也有很多,可以像我一样使用循环直接求解,也可以通过定义宏来进行求解,这里我给大家分享一下如何通过定义宏来完成逆序求解;

#define定义宏

因为阅读本篇文章的朋友一部分可能很熟悉#define的相关知识点,还有部分朋友可能是刚刚开始学习,不太熟悉这一块内容,所以这里我先说结论;

#define定义宏可以简单的理解为通过#define这个指令定义的一个功能,这个功能的实现形式有多种,这里我们介绍的形式是类似于函数(函数的相关知识点我们会在后面进行分析)。

定义宏的格式:
#define 标识符(变量) (表达式1?表达式2:表达式3)
标识符的值 

 这个代码的意思就是我们在借助标识符完成这个功能时,标识符的值会根据后面的三目操作符的值来确定:表达式1为真,则表达式2的值为标识符的值,当表达式1为假,则表达式3的值为标识符的值

通过宏定义解题

这里我们不要混淆了,我们现在介绍的宏定义是只能完成一个功能的,所以这个宏要直接实现fac和sum两个内容肯定是不现实的。这个问题也很好解决,我们可以将其中一个功能通过宏定义完成就行,也可以将2者都进行宏定义。这里我们介绍第一种,将其中一个功能通过宏定义来实现。

第一步:我们需要通过变量n来进行n次循环生成1~n个阶乘,阶乘的生成需要通过循环变量i来实现,所以我们这里需要通过两个循环才能逆向生成n~1的阶乘;

第二步:在所有的阶乘都生成后我们才能求和,这里就可以使用宏定义来完成求和;

第三步:我们要清楚宏定义的相关元素分别是什么?

通过第一种解题方式,我们知道,此时我们需要的变量是i、fac、sum、n,那我们在宏中具体需要哪些变量呢?

从宏定义的格式我们可以看到,它是需要3个表达式的,第一个用作判断,后面两个来进行求值。第一个判断的内容是什么呢?

有的朋友反应很快,通过表示符的取值方式,能很快的联想到这个是类似于分支语句的判定,所以我们这里判断的就是实现具体内容的条件。

在一个阶乘生成后,我们就需要通过宏既然这样我们只需要判断n与1的关系就可以了。

这里我们想用逆序的方式求解,那我们的第一个数肯定是n循环结束的值肯定是1,所以我们可以在表达式1中来判断n>1是否成立:

n>1时,我们需要执行的是sum+=fac;

n=1时,我们只需要执行sum+=1就可以了。

代码如下:

//方法2——宏定义(逆序求解)
#define Sum(n,fac,sum) (n>1?(sum+=fac):(sum+=1))
int main()
{
	int n = 0;
	int fac = 1;
	int sum = 0;
	//第一种解法我们直接将10赋值给n,这里我们通过scanf来进行赋值
	scanf("%d", &n);
	for (n; n >= 1; n--)
	{
		for (int i = n; i >= 1; i--)
		{
			fac *= i;
		}
		printf("%2d!=%-8d  ", n , fac);
		sum = Sum(n, fac, sum);
		printf("后%2d项阶乘的和sum=%-8d\n", n , sum);
		fac = 1;
	}
	printf("前10项阶乘的和sum=%d\n", sum);
	return 0;
}

下面我们就来测试一下代码的运行情况:

 可以看到,我们现在也通过宏定义进行了逆序求解。

现在咱们就完成了这两题的介绍,第一题我给大家分享的写法,大家可以下去尝试一下,这第二题你们可以尝试通过不同的写法来对其进行求解。

再强调一下,我们现在还没有介绍函数,所以大家尽量用分支与循环的方式求解,可以通过宏定义来求解,以此来巩固相关知识点。

结语

这两题我们在巩固循环语句的同时,又简单介绍了一下宏定义的相关内容。宏定义大家如果还没理解的话也没关系,后续我会继续分享相关的知识点。最后感谢大家的翻阅,希望这篇能够给大家提供一些不同的解题方法,帮助大家更好的将知识点融会贯通,咱们下一篇再见。

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值