#c

高精度计算基础

从基础到应用的高精度算法入门

#c
@鞠大龙
魅客科创中心

CONTENTS

目录

1. 高精度计算概述

1.1 高精度计算概述

  • 高精度计算:处理超出基本数据类型范围的数值计算
  • 核心思想
    • 使用数组存储大数
    • 模拟人工计算过程
    • 逐位进行运算
  • 实现方式:通常使用数组或字符串表示大数
  • 特点
    • 可处理任意长度的数字
    • 计算过程类似手工运算
    • 需要考虑进位和借位
返回目录 信息学 高精度计算基础 魅客科创中心

1.2 高精度计算的应用场景

  • 数论问题
    • 大数阶乘计算
    • 大数幂运算
    • 组合数计算
  • 密码学
    • RSA算法
    • 大数素数测试
    • 离散对数问题
  • 实际应用
    • 金融计算
    • 科学计算
    • 精确计算
返回目录 信息学 高精度计算基础 魅客科创中心

2. 高精度加法

2.1 高精度加法原理

基本思路

  1. 将大数按位存储在数组中(个位在前)
  2. 从低位到高位逐位相加
  3. 处理进位
  4. 最高位可能产生新的一位

示例:计算 123 + 456

步骤1:将数字存入数组
123 → [3,2,1]
456 → [6,5,4]
步骤2:逐位相加并处理进位
  [3,2,1]
+ [6,5,4]
---------
  [9,7,5]
结果:579
返回目录 信息学 高精度计算基础 魅客科创中心

2.2 高精度加法实现

核心代码

// 高精度加法
vector<int> add(vector<int>& A, vector<int>& B) {
    vector<int> C;
    int carry = 0;  // 进位
    
    for (int i = 0; i < A.size() || i < B.size(); i++) {
        if (i < A.size()) carry += A[i];
        if (i < B.size()) carry += B[i];
        C.push_back(carry % 10);
        carry /= 10;
    }
    
    if (carry) C.push_back(carry);
    return C;
}

使用示例

// 将字符串转换为数组
vector<int> str2vec(string& s) {
    vector<int> res;
    for (int i = s.length() - 1; i >= 0; i--) {
        res.push_back(s[i] - '0');
    }
    return res;
}

// 将数组转换为字符串
string vec2str(vector<int>& A) {
    string res;
    for (int i = A.size() - 1; i >= 0; i--) {
        res += A[i] + '0';
    }
    return res;
}
返回目录 信息学 高精度计算基础 魅客科创中心

2.3 高精度加法优化

优化技巧

  1. 预分配空间
vector<int> add(vector<int>& A, vector<int>& B) {
    vector<int> C;
    C.reserve(max(A.size(), B.size()) + 1);  // 预分配空间
    // ... 其余代码相同
}
  1. 使用引用传参:避免不必要的复制
  1. 内存优化
// 直接在较长的数组上进行修改
void add_to(vector<int>& A, vector<int>& B) {
    if (A.size() < B.size()) A.resize(B.size());
    int carry = 0;
    
    for (int i = 0; i < B.size() || carry; i++) {
        if (i < B.size()) carry += B[i];
        if (i < A.size()) {
            carry += A[i];
            A[i] = carry % 10;
        } else {
            A.push_back(carry % 10);
        }
        carry /= 10;
    }
}
返回目录 信息学 高精度计算基础 魅客科创中心

3. 高精度减法

3.1 高精度减法原理

基本思路

  1. 将大数按位存储在数组中(个位在前)
  2. 比较两个数的大小
  3. 从低位到高位逐位相减
  4. 处理借位
  5. 去除结果前导零

示例:计算 456 - 123

步骤1:将数字存入数组
456 → [6,5,4]
123 → [3,2,1]

步骤2:逐位相减并处理借位
  [6,5,4]
- [3,2,1]
---------
  [3,3,3]

结果:333
返回目录 信息学 高精度计算基础 魅客科创中心

3.2 高精度减法实现

核心代码

// 比较两个大数
bool cmp(vector<int>& A, vector<int>& B) {
    if (A.size() != B.size()) 
        return A.size() > B.size();
    for (int i = A.size() - 1; i >= 0; i--)
        if (A[i] != B[i])
            return A[i] > B[i];
    return true;
}

// 高精度减法
vector<int> sub(vector<int>& A, vector<int>& B) {
    vector<int> C;
    int borrow = 0;  // 借位
    
    for (int i = 0; i < A.size(); i++) {
        borrow = A[i] - borrow;
        if (i < B.size()) borrow -= B[i];
        C.push_back((borrow + 10) % 10);
        borrow = borrow < 0;
    }
    // 去除前导零
    while (C.size() > 1 && C.back() == 0)
        C.pop_back();
    return C;
}

// 处理可能为负的情况
pair<vector<int>, bool> sub_sign(
    vector<int>& A, vector<int>& B) {
    if (cmp(A, B))
        return {sub(A, B), true};
    else
        return {sub(B, A), false};
}
返回目录 信息学 高精度计算基础 魅客科创中心

3.3 高精度减法优化

优化技巧

  1. 原地修改
void sub_to(vector<int>& A, vector<int>& B) {
    int borrow = 0;
    for (int i = 0; i < A.size(); i++) {
        borrow = A[i] - borrow;
        if (i < B.size()) borrow -= B[i];
        A[i] = (borrow + 10) % 10;
        borrow = borrow < 0;
    }
    while (A.size() > 1 && A.back() == 0)
        A.pop_back();
}
  1. 快速比较:对于长度相差较大的数,可以直接判断

  2. 位压缩:可以将多个数位压缩存储,减少内存使用

返回目录 信息学 高精度计算基础 魅客科创中心

4. 高精度乘法

4.1 高精度乘法原理

基本思路

  1. 将大数按位存储在数组中(个位在前)
  2. 用单精度数乘以大数的每一位
  3. 处理进位
  4. 去除结果前导零

示例:计算 123 × 4

步骤1:将数字存入数组
123 → [3,2,1]
4 → 4

步骤2:逐位相乘并处理进位
  [3,2,1]
×     4
---------
  [2,9,4]

结果:492
返回目录 信息学 高精度计算基础 魅客科创中心

4.2 高精度乘法实现

核心代码

// 高精度乘以单精度
vector<int> mul(vector<int>& A, int b) {
    vector<int> C;
    int carry = 0;
    
    for (int i = 0; i < A.size(); i++) {
        carry += A[i] * b;
        C.push_back(carry % 10);
        carry /= 10;
    }
    
    while (carry) {
        C.push_back(carry % 10);
        carry /= 10;
    }
    
    while (C.size() > 1 && C.back() == 0)
        C.pop_back();
    return C;
}

使用示例

// 计算阶乘
vector<int> factorial(int n) {
    vector<int> result = {1};  // 初始值为1
    
    for (int i = 2; i <= n; i++) {
        result = mul(result, i);
    }
    
    return result;
}

// 计算2的幂
vector<int> power_of_two(int n) {
    vector<int> result = {1};  // 初始值为1
    
    for (int i = 0; i < n; i++) {
        result = mul(result, 2);
    }
    
    return result;
}
返回目录 信息学 高精度计算基础 魅客科创中心

4.3 高精度乘法优化

优化技巧

  1. 预分配空间
vector<int> mul(vector<int>& A, int b) {
    vector<int> C;
    C.reserve(A.size() + 10);  // 预分配足够空间
    // ... 其余代码相同
}
  1. 特殊情况处理
vector<int> mul(vector<int>& A, int b) {
    if (b == 0) return {0};
    if (b == 1) return A;
    
    vector<int> C;
    int carry = 0;
    // ... 其余代码相同
}
  1. 位运算优化
// 当b是2的幂时的优化
vector<int> mul_power_of_two(vector<int>& A, int k) {
    vector<int> C = A;
    int carry = 0;
    
    for (int i = 0; i < C.size(); i++) {
        int cur = C[i] << k;
        C[i] = (cur + carry) % 10;
        carry = (cur + carry) / 10;
    }
    
    while (carry) {
        C.push_back(carry % 10);
        carry /= 10;
    }
    
    return C;
}
返回目录 信息学 高精度计算基础 魅客科创中心

5. 优化技巧

5. 优化技巧

  • 内存管理
    • 预分配数组空间
    • 使用引用传参
    • 原地修改避免复制
  • 算法优化
    • 特殊情况快速处理
    • 位运算加速
    • 并行计算
  • 代码优化
    • 循环展开
    • 减少函数调用
    • 使用移位代替乘除
返回目录 信息学 高精度计算基础 魅客科创中心

6. 经典例题

6.1 大数阶乘

问题描述:计算n的阶乘(n ≤ 1000)。

思路:使用高精度乘法,从1乘到n。

代码实现

vector<int> factorial(int n) {
    vector<int> result = {1};  // 初始值为1
    
    for (int i = 2; i <= n; i++) {
        // 乘以当前数
        int carry = 0;
        for (int j = 0; j < result.size(); j++) {
            carry += result[j] * i;
            result[j] = carry % 10;
            carry /= 10;
        }
        
        // 处理剩余进位
        while (carry) {
            result.push_back(carry % 10);
            carry /= 10;
        }
    }
    
    return result;
}
返回目录 信息学 高精度计算基础 魅客科创中心

6.2 大数加法

问题描述:计算斐波那契数列的第n项(n ≤ 10000)。

思路:使用高精度加法,逐项计算。

代码实现

vector<int> fibonacci(int n) {
    if (n <= 1) return {n};
    
    vector<int> a = {0};  // f(0)
    vector<int> b = {1};  // f(1)
    
    for (int i = 2; i <= n; i++) {
        vector<int> c = add(a, b);  // f(i) = f(i-1) + f(i-2)
        a = b;
        b = c;
    }
    
    return b;
}

// 优化版本:原地修改
vector<int> fibonacci_optimized(int n) {
    if (n <= 1) return {n};
    
    vector<int> a = {0};  // f(0)
    vector<int> b = {1};  // f(1)
    
    for (int i = 2; i <= n; i++) {
        // 直接在b上进行修改
        int carry = 0;
        if (b.size() < a.size()) b.resize(a.size());
        
        for (int j = 0; j < a.size(); j++) {
            carry += a[j];
            if (j < b.size()) {
                carry += b[j];
                b[j] = carry % 10;
            } else {
                b.push_back(carry % 10);
            }
            carry /= 10;
        }
        
        while (carry) {
            b.push_back(carry % 10);
            carry /= 10;
        }
        
        // 交换a和b
        a.swap(b);
        b = move(a);
    }
    
    return b;
}
返回目录 信息学 高精度计算基础 魅客科创中心

6.3 大数减法

问题描述:给定两个大数a和b,计算它们的差的绝对值。

思路:使用高精度减法,注意比较大小和处理借位。

代码实现

vector<int> absolute_difference(string& a, string& b) {
    // 将字符串转换为数组
    vector<int> A = str2vec(a);
    vector<int> B = str2vec(b);
    
    // 确保A >= B
    if (!cmp(A, B)) swap(A, B);
    
    // 执行减法
    vector<int> result;
    int borrow = 0;
    
    for (int i = 0; i < A.size(); i++) {
        borrow = A[i] - borrow;
        if (i < B.size()) borrow -= B[i];
        result.push_back((borrow + 10) % 10);
        borrow = borrow < 0;
    }
    
    // 去除前导零
    while (result.size() > 1 && result.back() == 0)
        result.pop_back();
    
    return result;
}
返回目录 信息学 高精度计算基础 魅客科创中心

7. 总结

7. 总结

核心要点

  • 高精度计算的基本思想
    • 数组存储大数
    • 模拟手工运算
    • 处理进位和借位
  • 实现技巧
    • 预分配空间
    • 原地修改
    • 特殊情况处理
  • 常见错误
    • 进位处理不当
    • 前导零处理错误
    • 内存分配不足

应用场景

  • 大数运算
    • 阶乘计算
    • 斐波那契数列
    • 组合数计算
  • 实际问题
    • 金融计算
    • 科学计算
    • 密码学应用

进阶方向

  • 高精度除法
  • 高精度开方
  • FFT/NTT优化
返回目录 信息学 高精度计算基础 魅客科创中心

8. 练习题目

8.1 初级练习题

  1. [HDU 1002] A + B Problem II

    • 难度:★★☆☆☆
    • 描述:计算两个不超过1000位的非负整数之和
    • 思路:直接使用高精度加法模板,注意格式化输出
  2. [POJ 2506] Tiling

    • 难度:★★☆☆☆
    • 描述:计算特殊平铺方案数,需要高精度加法
    • 思路:使用高精度加法,类似于斐波那契数列
  3. [HDU 1715] Big Number

    • 难度:★★☆☆☆
    • 描述:计算斐波那契数列第n项(n≤1000)
    • 思路:使用高精度加法,优化空间使用
返回目录 信息学 高精度计算基础 魅客科创中心

8.2 中级练习题

  1. [CSES 1713] Counting Divisors

    • 难度:★★★☆☆
    • 描述:计算大数的因数个数,涉及高精度运算
    • 思路:使用高精度除法和质因数分解
  2. [CSP-J 2021] 阶乘求和

    • 难度:★★★☆☆
    • 描述:计算1!+2!+3!+...+n!的值(n≤1000)
    • 思路:使用高精度加法和乘法,注意优化累加过程
  3. [CSES 1618] Trailing Zeros

    • 难度:★★★☆☆
    • 描述:计算n!末尾零的个数,需要高精度处理
    • 思路:高精度乘法计算阶乘,分析因子2和5
返回目录 信息学 高精度计算基础 魅客科创中心

8.3 高级练习题

  1. [USACO 2019 Dec Gold] Milk Pumping

    • 难度:★★★★☆
    • 描述:计算最优路径,涉及高精度分数运算
    • 思路:使用高精度除法,结合最短路算法
  2. [USACO 2020 Jan Gold] Time is Mooney

    • 难度:★★★★☆
    • 描述:动态规划问题,需要高精度处理收益
    • 思路:高精度加减法,结合DP优化
  3. [USACO 2018 Open Gold] Talent Show

    • 难度:★★★★☆
    • 描述:二分答案,涉及高精度分数比较
    • 思路:使用高精度除法和比较,注意精度控制
返回目录 信息学 高精度计算基础 魅客科创中心

8.4 综合应用题

  1. [NOIP 2012 提高组] 幸运数

    • 难度:★★★★★
    • 描述:计算满足特定条件的大数,涉及高精度运算
    • 思路:综合运用高精度加减乘,注意数据结构优化
  2. [NOIP 2015 提高组] 运输计划

    • 难度:★★★★★
    • 描述:图论问题中的路径计数,需要高精度处理
    • 思路:图论算法结合高精度,注意溢出处理
  3. [NOIP 2017 提高组] 时间复杂度

    • 难度:★★★★★
    • 描述:计算程序的精确执行次数,需要高精度
    • 思路:递推关系结合高精度,注意优化策略
返回目录 信息学 高精度计算基础 魅客科创中心
高精度计算基础

#c

演讲者备注不会在演示模式中显示给观众,仅供演讲者参考。 每个关键页面都添加了备注,帮助演讲者更好地讲解内容。

封面页: - 欢迎学生,简要介绍本次课程的主题和目标 - 强调高精度计算在算法竞赛中的重要性 - 本讲义将从基本概念到实际应用,系统讲解高精度计算的基础算法

目录页: - 简要介绍讲义结构 - 高精度计算基本概念、加减乘法实现、优化技巧、经典例题

转场页: - 引入高精度计算的基本概念 - 高精度计算在算法竞赛中的应用

高精度计算概述: - 定义和核心思想 - 应用场景

转场页: - 高精度加法的实现

转场页: - 高精度减法的实现

转场页: - 高精度整数和单精度整数的乘法实现

转场页: - 高精度计算的优化方法

转场页: - 高精度计算的经典例题

转场页: - 总结高精度计算的核心思想和应用

转场页: - 高精度计算练习题目

练习题目部分结束,这些题目涵盖了高精度计算的各个难度和应用场景。 鼓励学生尝试解决这些问题,加深对高精度计算的理解。