求阶乘与多整数连乘问题

1. 求阶乘问题

对给定的 n(n <= 100),计算并输出 k!(k = 1, 2, 3, …, n)的全部有效数字。

  分析:因为要求的整数可能大大超出一般的整数的位数,所以应使用一维数组存储长整数,数组中的每个元素只存储长整数的一位数字。如有 m 位长整数 num,则用数组 arr[]存储如下:

num = arr[m]*10^(m-1) + arr[m-1]*10^(m-2) + … + arr[2]*10^1 + arr[1]*10^0

并用arr[0]存储长整数 num 的位数 m, 即arr[0] = m。按此约定,数组的每个元素存储 k! 的一位数字,并从低位到高位依次存于数组的 第二个元素、第三个元素、…。例如:5! = 120 在数组中的存储形式为:

3 0 2 1 …

首元素 3 表示长整数是一个 3 位数,接着低位到高位依次是 0、2、1,表示长整数120。计算阶乘 k!,可采用对已求得的阶乘(k-1)!连续累加 (k-1) 次求出。

程序如下:

#include<stdio.h>
#include<malloc.h>
#define M_SIZE 1000

void nextAdd(int arr[], int k)   //已知 a 中的 (k-1)!,计算 k!
{
	int *p, count = arr[0], i, j, carry, r;
	p = (int *)malloc(sizeof(int) * (count+1));
	for(i = 1; i <= count; i++)   //先把 arr 中已计算出的(k-1)!复制到数组 p 中
	{
		p[i] = arr[i];
	}
	for(j = 1; j < k; j++)   //累加 k-1 次
	{
		for(carry = 0, i = 1; i <= count; i++)   //对每次累加进行处理
		{
			r = (i <= arr[0]?(arr[i] + p[i]) : arr[i]) + carry;
			arr[i] = r%10;   //arr[i]的值为相加后的个位数
			carry = r/10;   //取进位,下一高位相加时送入高位
		}
		if(carry) arr[++count] = carry;   //如果最高位有进位,则进位,到此完成一次累加
	}
	free(p);
	arr[0] = count;   //arr[0]存储计算结果的位数
}

void output(int arr[], int k)   //输出 k! 的值
{
	int i;
	printf("%4d! = ", k);
	for(i = arr[0]; i > 0; i--)
	{
		printf("%d", arr[i]);
	}
	printf("\n\n");
}

void main()
{
	int arr[M_SIZE], num, k;
	printf("Which number do you want to calculate the factorial, Enter the number:");
	scanf("%d", &num);
	arr[0] = 1;  //初始化数组值为 1! 的值
	arr[1] = 1;
	output(arr, 1);
	for(k = 2; k <= num; k++)
	{
		nextAdd(arr, k);
		output(arr, k);
		getchar();
	}
}

2. 多整数连乘问题

设一数组arr[]中存储了 n 个整数,求这 n 个整数的乘积。
  分析:由第 1 题的分析知,要求的整数可能大大超出一般的整数的位数,所以应使用一维数组存储长整数。下面的程序利用加法来实现乘法,即把 (num1 * num2) 当成是 num2 个 num1 相加。从而可以解决大整数无法直接相乘的问题。
程序代码如下:

#include<stdio.h>
#include<malloc.h>
#define NUM 10   //乘数个数
#define M_SIZE 1000   //存储结果的数组的最大长度

//把 result 中存储的数与 e 相乘,通过 e 个 result 相加来实现
void multiply(int result[], int e)
{
	int *p, count = result[0], carry, r, k;
	p = (int *)malloc(sizeof(int) * (count + 1));

	for(int i = 1; i <= count; i++)   //把当前result值复制到临时数组p[]中
	{
		p[i] = result[i];
	}

	for(i = 1; i < e; i++)   //计算 result * e
	{
		for(carry = 0, k = 1; k <= count; k++)
		{
			r = (k <= result[0]?(result[k] + p[k]) : result[k]) + carry;
			result[k] = r%10;
			carry = r/10;
		}
		if(carry)result[++count] = carry;
	}

	free(p);
	result[0] = count;

}

void main()
{
	int arr[NUM] = {1000000,1000000,1000000,1000000,1000000,
		1000000,1000000,1000000,1000000,1000000};
	int result[M_SIZE], temp, i = 1, count = 0;   //count用于记录长整数的长度

	int part = 0;   //控制每行只输出10个字符

	temp = arr[0];

	while(temp != 0)   //初始化结果数组值为 arr[0] 的值
	{
		result[i++] = temp%10;
		temp /= 10;
		count++;
	}
	
	result[0] = count;

	for(i = 1; i < NUM; i++)
	{
		multiply(result, arr[i]);
	}
	
	printf("The calculation result is:\n");

	for(i = result[0]; i > 0; i--, part++)   //输出计算结果
	{
		if(part%10 == 0)printf("\n");   //控制每行只输出10个字符

		printf("%d", result[i]);

	}

	printf("\n\n");

}

  此程序能正确计算出结果,但其实其算法的时间复杂度显然是非常大的,每个数组元素再增加一个数量级,计算就要花很长时间才能算出来了。具体要多长时间,我也说不准,反正我测试时等得不耐烦了,还没算出来。其实归根结底,这是个大整数乘法问题,所以用蛮力无法在短时间内解决问题。稍后我会对大整数问题进行学习和总结…(注:对于数组中可能有负整数的情况,可先把所有元素都当成整数来计算,算出结果后再根据负数的个数来给结果赋上正号或负号就可以)

    原文作者:大整数乘法问题
    原文地址: https://blog.csdn.net/runninglion/article/details/10856487
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞