在算法競賽與實際開發中,階乘增長極快。13! 已超過 32 位整數範圍,21! 突破 64 位長整數。若輸入 n ≤ 1000,結果將擁有超過 2500 位數字,遠超任何基本類型容量。本文詳細解析《算法競賽入門經典》第 5.22 題——利用「數組模擬手算乘法」求解 n! 精確值的經典算法。
輸入不超過 1000 的正整數
n,輸出n! = 1×2×3×…×n的精確結果。
樣例輸入:30
樣例輸出:265252859812191058636308480000000
將大整數拆解為個位、十位、百位……分別存入數組 f[0]、f[1]、f[2]…,低位在前、高位在後,完全模擬手算「逐位相乘、進位」的過程。
1000! ≈ 4×10²⁵⁶⁷,約 2568 位,開 maxn = 3000 足夠。f[0] = 1 代表 1!,其餘為 0。i = 2 … n,將當前數組代表的大整數乘以 i。s = f[j] * i + c; f[j] = s % 10; c = s / 10;/*
* 算法競賽入門經典 5.22 階乘精確值
* 數組模擬高精度乘法
*/
#include <stdio.h>
#include <string.h>
#define MAXN 3000 // 1000! 約 2568 位,3000 綽綽有餘
int f[MAXN]; // f[0] 存個位,f[1] 存十位……
int main() {
int i, j, n;
scanf("%d", &n);
memset(f, 0, sizeof(f));
f[0] = 1; // 1! = 1
for (i = 2; i <= n; i++) { // 從 2! 累乘到 n!
int c = 0; // 進位
for (j = 0; j < MAXN; j++) { // 低位→高位逐位相乘
int s = f[j] * i + c;
f[j] = s % 10; // 當前位
c = s / 10; // 新進位
}
}
// 跳過前導 0
for (j = MAXN - 1; j >= 0; j--) if (f[j]) break;
// 逆序輸出
for (i = j; i >= 0; i--) printf("%d", f[i]);
printf("\n");
return 0;
}
#define vs const:為何選宏?#define MAXN 3000 只是編譯期文本替換,不佔用運行時內存,無類型,適合作為數組長度常量。const int MAXN = 3000; 有類型、有地址、佔用數據段,某些老舊編譯器不允許作為數組長度。5! 為例)| 步驟 | i | j | s = f[j]*i + c | f[j] | c | 說明 |
|---|---|---|---|---|---|---|
| 1 | 2 | 0 | 2 | 2 | 0 | 2! = 2 |
| 2 | 3 | 0 | 6 | 6 | 0 | 3! = 6 |
| 3 | 4 | 0 | 24 | 4 | 2 | 4! 低位 4,進 2 |
| 4 | 4 | 1 | 2 | 2 | 0 | 高位 2,得 24 |
| 5 | 5 | 0 | 20 | 0 | 2 | 5! 低位 0,進 2 |
| 6 | 5 | 1 | 12 | 2 | 1 | 十位 2,進 1 |
| 7 | 5 | 2 | 1 | 1 | 0 | 百位 1,得 120 |
因 n! (n≥1) 必不為 0,直接從高位向低掃描首個非零元素即可;若輸入可能為 0,需單獨處理 0! = 1。
O(n × maxn) ≈ 3×10⁶ 次操作,極快。O(maxn) = 3000 int ≈ 12 KB。1000! 實際 2568 位,建議留 20% 餘量。c = 0。base = 10⁴ 或 10⁹ 壓縮數組長度,減少循環次數。掌握「數組模擬手算」是解決所有高精度問題的基石,建議反覆手寫直到肌肉記憶形成。