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