c/c++蓝桥杯经典编程题100道(5)阶乘计算

阶乘计算

->返回c/c++蓝桥杯经典编程题100道-目录


目录

阶乘计算

一、题型解释

二、例题问题描述

C语言实现

解法1:基础循环(难度★)

解法2:递归实现(难度★☆)

解法3:大数阶乘(难度★★★)

C++实现

解法1:循环实现(难度★)

解法2:模板元编程(难度★★★)

解法3:类封装(难度★★)

总结对比(补充)


一、题型解释

阶乘定义:n! = 1×2×3×...×n
常见题型:

  1. 基础阶乘计算:直接计算 n! 的值(注意数据溢出问题)

  2. 递归实现:用递归思想实现阶乘

  3. 大数阶乘:当 n > 20 时,普通数据类型会溢出,需要用数组或字符串存储结果

  4. 数学问题结合:如排列组合、数论问题中阶乘的应用


二、例题问题描述

例题1:输入整数 n,输出 n! 的值(假设 n <= 20
例题2:输入整数 nn <= 1000),输出精确的 n! 值(大数问题)

C语言实现

解法1:基础循环(难度★)

c

#include 

int main() {
    int n;
    long long result = 1;  // 使用长整型避免小范围溢出
    scanf("%d", &n);
    
    for(int i = 1; i <= n; i++) {  // 循环从1到n
        result *= i;               // 累乘计算阶乘
    }
    printf("%lld", result);
    return 0;
}

详细逻辑说明

  1. 初始化result 初始化为1,因为阶乘的乘法基数为1。

  2. 循环过程

    • 循环变量 i 从1递增到 n,每次迭代将 result 乘以 i

    • 例如:当 n=5 时,计算过程为 1*1 → 1*2 → 2*3 → 6*4 → 24*5 = 120

  3. 数据溢出

    • long long 类型最大值为 9,223,372,036,854,775,807,而 20! = 2,432,902,008,176,640,000 未超过此值,但 21! 会溢出。

    • 因此该解法仅适用于 n <= 20


解法2:递归实现(难度★☆)

c

#include 

long long factorial(int n) {
    if(n == 0 || n == 1) return 1;  // 递归终止条件
    return n * factorial(n-1);      // 递归调用自身
}

int main() {
    int n;
    scanf("%d", &n);
    printf("%lld", factorial(n));
    return 0;
}

详细逻辑说明

  1. 递归定义

    • 基线条件(Base Case):当 n=0 或 n=1 时返回1,因为 0! = 1 且 1! = 1

    • 递归关系:n! = n * (n-1)!,通过不断缩小问题规模求解。

  2. 调用栈分析

    • 例如 factorial(3) 的调用过程:

      复制

      factorial(3)
      → 3 * factorial(2)
      → 3 * (2 * factorial(1))
      → 3 * (2 * 1)
      → 6
    • 每次递归调用会占用栈空间,若 n 过大(如 n=1e4),可能导致栈溢出。

  3. 限制

    • 递归深度与 n 成正比,实际应用中 n 不宜超过数千(取决于栈大小)。


解法3:大数阶乘(难度★★★)

c

#include 
#define MAX 3000  // 预分配足够空间存储大数

int main() {
    int n, digits = 1;  // digits记录当前位数
    int num[MAX] = {1}; // 数组逆序存储数字(个位在索引0)
    
    scanf("%d", &n);
    
    for(int i = 2; i <= n; i++) {    // 从2开始乘到n
        int carry = 0;               // 进位初始化为0
        for(int j = 0; j < digits; j++) { // 逐位相乘
            int product = num[j] * i + carry;
            num[j] = product % 10;   // 当前位结果
            carry = product / 10;    // 计算进位
        }
        // 处理剩余进位
        while(carry > 0) {           // 进位可能多位
            num[digits] = carry % 10;
            carry /= 10;
            digits++;                // 位数增加
        }
    }
    
    // 逆序输出结果
    for(int i = digits-1; i >= 0; i--) {
        printf("%d", num[i]);
    }
    return 0;
}

详细逻辑说明

  1. 存储设计

    • 使用数组 num 逆序存储大数,例如 123 存储为 [3,2,1],便于处理进位时扩展位数。

  2. 乘法过程

    • 外层循环遍历乘数 i(从2到n),每次将当前大数与 i 相乘。

    • 内层循环逐位计算:

      • product = num[j] * i + carry:当前位的乘积加上进位。

      • num[j] = product % 10:取个位作为当前位的值。

      • carry = product / 10:计算新的进位。

    • 内层循环结束后,处理剩余的进位(可能多位,如 carry=123 需分解为 3,2,1)。

  3. 时间复杂度

    • 外层循环执行 n-1 次,内层循环执行 digits 次(digits 随 n 增加而增长)。

    • 总复杂度为 O(n²),适用于 n <= 1000

  4. 空间优化

    • 预分配数组大小 MAX 需足够大(1000! 约有2568位)。


C++实现

解法1:循环实现(难度★)

cpp

#include 
using namespace std;

int main() {
    int n;
    long long result = 1;  // 使用与C相同的数据类型
    cin >> n;
    
    for(int i = 2; i <= n; i++) {  // 从2开始乘(1乘无意义)
        result *= i;
    }
    cout << result;
    return 0;
}

详细逻辑说明

  • 与C语言实现逻辑完全一致,仅输入输出语法不同。

  • cin 和 cout 提供了更简洁的流式操作。


解法2:模板元编程(难度★★★)

cpp

#include 
using namespace std;

template
struct Factorial {
    static const long long value = N * Factorial::value;
};

// 模板特化处理基线条件
template<>
struct Factorial<0> {
    static const long long value = 1;
};

int main() {
    // 编译期计算,直接输出结果
    cout << Factorial<5>::value;  // 输出120
    return 0;
}

详细逻辑说明

  1. 模板展开机制

    • 编译器在编译时递归实例化模板,例如 Factorial<5> 展开为 5 * Factorial<4>::value,直到 Factorial<0> 特化返回1。

    • 最终结果在编译期确定,运行时直接输出常量。

  2. 限制

    • N 必须为编译期常量,无法处理用户输入动态值。

    • 当 N 过大时(如 N=30),可能导致编译器内存不足或递归深度限制。


解法3:类封装(难度★★)

cpp

#include 
#include 
using namespace std;

class BigFactorial {
public:
    vector calculate(int n) {
        vector res = {1};          // 初始化为1(存储方式同C语言)
        for(int i = 2; i <= n; i++) {   // 从2乘到n
            multiply(res, i);           // 调用成员函数处理乘法
        }
        return res;                     // 返回结果向量
    }

private:
    void multiply(vector& num, int factor) {
        int carry = 0;
        for(int &digit : num) {         // 遍历每一位
            int product = digit * factor + carry;
            digit = product % 10;       // 更新当前位
            carry = product / 10;       // 计算进位
        }
        while(carry > 0) {              // 处理剩余进位
            num.push_back(carry % 10);
            carry /= 10;
        }
    }
};

int main() {
    BigFactorial calc;
    auto result = calc.calculate(100);  // 计算100!
    // 逆序输出结果
    for(auto it = result.rbegin(); it != result.rend(); ++it) {
        cout << *it;
    }
    return 0;
}

详细逻辑说明

  1. 类设计

    • calculate() 方法负责整体计算流程,返回存储结果的 vector

    • multiply() 私有方法实现大数与整数的乘法操作。

  2. 乘法细节

    • 使用范围循环 for(int &digit : num) 遍历每一位,更新当前位并计算进位。

    • 进位处理与C语言版本逻辑一致,但借助 vector 动态扩容,无需预定义数组大小。

  3. 输出优化

    • 结果以逆序存储在 vector 中,输出时使用反向迭代器 rbegin() 和 rend() 正序打印。


总结对比(补充)

方法 关键逻辑步骤 适用场景
基础循环 累乘、处理溢出、简单循环 小数值快速计算
递归实现 递归分解问题、栈空间消耗 教学演示/小规模计算
大数阶乘 数组逆序存储、逐位计算进位、动态扩展位数 工程中精确计算大阶乘
模板元编程 编译期模板展开、静态成员存储结果 需要编译期常量的场景
类封装 面向对象封装、动态数组管理、代码可复用性 C++工程中的模块化设计

->返回c/c++蓝桥杯经典编程题100道-目录

你可能感兴趣的:(c语言,c++,蓝桥杯,算法)