UVa OJ 623 阶乘

本文介绍了一种使用数组存储和计算大数阶乘的方法,解决了传统整型数据无法表示超长数字的问题。通过逐位相乘和进位处理,实现了高效的大数阶乘计算。

UVa OJ 623 

500!


In these days you can more and more often happen to see programs which perform some useful calculations being executed rather then trivial screen savers. Some of them check the system message queue and in case of finding it empty (for examples somebody is editing a file and stays idle for some time) execute its own algorithm.

As an examples we can give programs which calculate primary numbers.


One can also imagine a program which calculates a factorial of given numbers. In this case it is the time complexity of order O(n) which makes troubles, but the memory requirements. Considering the fact that 500! gives 1135-digit number no standard, neither integer nor floating, data type is applicable here.


Your task is to write a programs which calculates a factorial of a given number.

Assumptions: Value of a number ``n" which factorial should be calculated of does not exceed 1000 (although 500! is the name of the problem, 500! is a small limit).

Input 

Any number of lines, each containing value ``n" for which you should provide value of n!

Output 

2 lines for each input case. First should contain value ``n" followed by character `!'. The second should contain calculated value n!.

Sample Input 

10
30
50
100

Sample Output 

10!
3628800
30!
265252859812191058636308480000000
50!
30414093201713378043612608166064768844377641568960512000000000000
100!
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

用数组来保存运算结果。为了方便起见,我们用f[0]保存个位,f[1]保存十位...(为什么要逆序表示?方便进位),每次模拟手算来计算n!。在输出时要忽略前导0。注意,如果结果本身就是0,那么忽略前导零将会导致什么都不输出。幸好n!肯定不等于0.


#include <cstdio>
#include <cstdlib>
#include <cstring> 

using namespace std;

const int MAXN = 3000;
int f[MAXN];

bool foo(int n, int f[])
{
    if (n < 0)
    {
        printf("n must be positive!\n");
        return false;
    }
    else
    {
        f[0] = 1;
        for (int i = 2; i <= n; ++i)
        {
            int c = 0;    // c是进位 
            for (int j = 0; j < MAXN; ++j)
            {
                int s = f[j] * i + c;
                f[j] = s % 10;
                c = s / 10;
            }
        }
        int end = MAXN-1;
        for (; end >= 0; --end)
        {
            if (f[end]) break;
        }
        printf("%d!\n", n);
        for (int i = end; i >= 0; --i)
        {
            printf("%d", f[i]);
        }
        printf("\n");
    }
    return true;
}

int main(int argc, char *argv[])
{
    int n = 0;
    while (scanf("%d", &n) != EOF)
    {
        memset(f, 0, sizeof(f));
        foo(n, f);
    }
    return 0;
}



<think>好的,我需要解决用户关于东华大学OJ平台上大数阶乘计算的问题。首先,用户需要的是算法或解决方案,所以我要先回顾大数阶乘的基本处理方法。 大数阶乘的难点在于普通数据类型无法存储非常大的数,比如1000!这样的结果远远超过long long的范围。因此,必须使用数组或字符串来模拟大数运算。常见的做法是用一个整型数组来存储每一位数字,然后模拟手工乘法的过程。 接下来,我需要考虑如何实现这个算法。例如,对于计算n!,可以初始化一个数组,比如result[0] = 1,长度初始为1。然后从2到n逐个相乘,每次乘法都要处理进位问题。比如,当计算到i=3时,当前的result数组是[6],那么i=4的时候,6*4=24,这时候需要将24拆分成个位4和进位2,结果变为[4,2],即24。依此类推,直到处理完所有乘数。 另外,要注意数组的存储顺序。通常为了方便进位操作,数组的低位存储数字的低位,比如数字123存为[3,2,1],这样在乘法时,每一位乘以后,进位可以逐步处理到更高位。不过,有些实现可能使用高位在前的方式,需要根据具体情况调整。 用户提供的引用内容中有两个代码示例,一个是求数列之和,另一个是寻找完全数。虽然这些和大数阶乘没有直接关系,但可以注意到东华OJ的题目可能需要处理输入输出格式,比如多组测试数据、EOF结束等。所以在编写代码时,要注意输入的处理,比如使用while循环读取输入直到EOF。 接下来,我需要确定大数阶乘的具体实现步骤。例如: 1. 初始化一个足够大的数组,或者动态分配内存,用来存储结果的每一位数字。 2. 将初始值设为1,表示0!或1!的结果。 3. 从2开始循环到n,每次将当前结果数组与i相乘,处理进位。 4. 最后将数组中的数字逆序输出,因为存储顺序可能和显示顺序相反。 还要考虑优化问题。比如,尽量减少乘法的次数,或者优化进位处理的方式。例如,在每次乘法时,可以预先估计结果的长度,避免频繁的数组扩容。或者,在计算过程中去除前导零,但阶乘的结果不会有零,所以可能不需要处理这个。 另一个需要注意的问题是数组的大小。假设n最大是1000,那么1000!的位数大约是2568位(可以用斯特林公式估算),所以数组需要足够大,或者动态调整大小。 然后,我需要验证这个算法的正确性。例如,手动计算小数的阶乘,比如5!,看是否符合预期。另外,测试边界情况,比如n=0或n=1,这时候结果应该是1。 最后,根据东华OJ的输入输出要求,调整代码的输入输出部分。例如,题目可能需要多组测试数据,每个测试用例输入一个n,输出对应的阶乘结果。因此,代码结构可能需要包含循环读取输入,并在每次计算后输出结果。 总结一下,我需要写出一个使用数组模拟乘法的大数阶乘算法,处理大数存储和进位问题,同时符合OJ平台的输入输出格式要求。</think>### 大数阶乘计算解决方案 大数阶乘的核心问题在于普通数据类型无法存储超长整数,需通过**数组模拟乘法**实现。以下是分步解决方案: --- #### **1. 算法原理** - **数组存储法**:用整型数组存储阶乘结果,低位在前(如123存为`[3,2,1]`) - **逐位乘法**:从$1$到$n$依次相乘,处理进位 - **动态扩容**:根据结果位数自动扩展数组长度 --- #### **2. 实现步骤** **步骤1:初始化数组** ```cpp int result[MAX] = {1}; // MAX根据n的最大值设定(如1000!需约2600位) int digits = 1; // 当前位数 ``` **步骤2:逐次相乘** ```cpp for (int i = 2; i <= n; i++) { int carry = 0; for (int j = 0; j < digits; j++) { int temp = result[j] * i + carry; result[j] = temp % 10; carry = temp / 10; } // 处理剩余进位 while (carry > 0) { result[digits++] = carry % 10; carry /= 10; } } ``` **步骤3:逆序输出** ```cpp for (int i = digits - 1; i >= 0; i--) { cout << result[i]; } ``` --- #### **3. 优化策略** 1. **估算数组大小**:使用斯特林公式$digits ≈ \frac{\ln(2\pi n)}{2} + n\ln n - n + 1$[^1] 2. **压缩存储**:每个数组元素存储多位数字(如0-9999) 3. **预处理素数**:通过质因数分解优化乘法次数[^2] --- #### **4. 完整代码示例** ```cpp #include <iostream> using namespace std; #define MAX 3000 // 足够存储1000!的结果 int main() { int n; while (cin >> n) { int result[MAX] = {1}; int digits = 1; for (int i = 2; i <= n; i++) { int carry = 0; for (int j = 0; j < digits; j++) { int temp = result[j] * i + carry; result[j] = temp % 10; carry = temp / 10; } while (carry > 0) { result[digits++] = carry % 10; carry /= 10; } } for (int i = digits - 1; i >= 0; i--) { cout << result[i]; } cout << endl; } return 0; } ``` --- #### **5. 注意事项** - **输入范围**:需明确题目中$n$的最大值 - **前导零处理**:阶乘结果无前导零,直接输出即可 - **时间复杂度**:$O(n^2)$,适用于$n \leq 10000$量级[^2] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值