【基础算法】高精度运算深度解析与优化

🔭 个人主页: 散峰而望

《C语言:从基础到进阶》《编程工具的下载和使用》《C语言刷题》《算法竞赛从入门到获奖》《人工智能》《AI Agent》
愿为出海月,不做归山云


🎬博主简介

【算法竞赛】高精度运算深度解析与优化

  • 前言
  • 高精度
    • [1. 高精度加法](#1. 高精度加法)
    • [2. 高精度减法](#2. 高精度减法)
    • [3. 高精度乘法](#3. 高精度乘法)
    • [4. 高精度除法](#4. 高精度除法)
  • 结语

前言

算法竞赛中经常需要处理超出标准数据类型范围的整数运算,例如大数阶乘、斐波那契数列的大项计算或密码学相关题目。标准数据类型如 int 或 long long 的存储范围有限,无法直接处理上百位的数字运算。高精度运算通过模拟人工计算过程,将大数拆分为数组或字符串逐位处理,成为解决此类问题的核心方法。

高精度

当数据的值特别大,各种类型都存不下的时候,此时就要用高精度算法来计算加减乘除:

  • 先用字符串读入这个数,然后用数组逆序存储该数的每一位;
  • 利用数组,模拟加减乘除运算的过程。

高精度算法本质上还是模拟算法,用代码模拟小学列竖式计算加减乘除的过程。

之所以用数组逆序存储该数的每一位 ,是因为我们计算的是从个位开始,而逆序正好是从下标 0 位开始,模拟最低位到最高位*。同时处理进位时,正序的话,进位不知道往什么位置进位。

1. 高精度加法

高精度加法

算法原理:

模拟小学「列竖式」计算「两数相加」的过程。

  1. 用字符串读入数据;
  2. 将字符串的每一位拆分,逆序放在数组中;
cpp 复制代码
  x  =  " 4 3 9 "
下标      0 1 2
int a[] = [9, 3, 4]
下标       0  1  2
  1. 模拟列竖式计算的过程:
    a. 对应位累加 --> x
    b. 处理进位 --> x / 10
    c. 处理余数 --> x % 10
  1. 处理结果的位数。

核心代码:

cpp 复制代码
void add(int c[], int a[], int b[])
{
	for(int i = 0; i < lc; i++)
	{
		c[i] += a[i] + b[i];
		c[i + 1] = c[i] / 10;
		c[i] %= 10;
	}
	if(c[lc]) lc++;
}

参考代码:

cpp 复制代码
#include <iostream>

using namespace std;

const int N = 1e6 + 10;

int a[N], b[N], c[N];
int la, lb, lc;

void add(int c[], int a[], int b[])
{
	for(int i = 0; i < lc; i++)
	{
		c[i] += a[i] + b[i];
		c[i + 1] = c[i] / 10;
		c[i] %= 10;
	}
	if(c[lc]) lc++;
}

int main()
{
	string x, y;
	cin >> x >> y;
	
	//拆分每一位,逆序放在数组中
	la = x.size();
	lb = y.size();
	lc = max(la, lb);
	//先指定lc的最大的数范围后,如果超过就再加
    for(int i = 0; i < la; i++) a[la - 1 - i] = x[i] - '0';
    for(int i = 0; i < lb; i++) b[lb - 1 - i] = y[i] - '0';
	
	//模拟加法过程
	add(c, a, b);
	
	//输出
	for(int i = lc - 1; i >= 0; i--)
	{
		cout << c[i];
	 } 
	return 0; 
 } 

2. 高精度减法

高精度减法

算法原理:

模拟小学「列竖式」计算「两数相减」的过程。

  1. 用字符串读入数据;
  2. 判断两个数的大小,让较大的数在前。注意字典序 vs 数的大小:
    a. 位数相等:按字典序比较;
    b. 位数不等:按照字符串的长度比较。
    即先比较长度再比较字典序,不然会出现下面这种情况
cpp 复制代码
数:     101 > 99
字符串: "101" < "99"
  1. 将字符串的每一位拆分,逆序放在数组中;
  2. 模拟列竖式计算的过程:
    a. 对应位求差;
    b. 处理借位;
  3. 处理前导零:因为减数完位数可能会往后退,比如十位退到个位,此时在十位的没有存储其他东西,就要把此处的空间释放掉。

核心代码:

cpp 复制代码
void sub(int c[], int a[], int b[])
{
    for(int i = 0; i < lc; i++)
    {
        c[i] += a[i] - b[i]; // 对应位相减,然后处理借位 
        if(c[i] < 0)
        {
            c[i + 1] -= 1; // 借位 
            c[i] += 10;
        }
    }
    // 处理前导零 
    while(lc > 1 && c[lc - 1] == 0) lc--;
}

参考代码:

cpp 复制代码
#include <iostream>

using namespace std;

const int N = 1e6 + 10;

int a[N], b[N], c[N];
int la, lb, lc;

// 比大小 
bool cmp(string& x, string& y)
{
    // 先比较长度 
    if(x.size() != y.size()) return x.size() < y.size();
    // 再按照字典序的方式比较 
    return x < y;
}    

void sub(int c[], int a[], int b[])
{
    for(int i = 0; i < lc; i++)
    {
        c[i] += a[i] - b[i]; // 对应位相减,然后处理借位 
        if(c[i] < 0)
        {
            c[i + 1] -= 1; // 借位 
            c[i] += 10;
        }
    }
    // 处理前导零 
    while(lc > 1 && c[lc - 1] == 0) lc--;
}

int main()
{
    string x, y; cin >> x >> y;
    // 比大小 
    if(cmp(x, y))
    {
        swap(x, y);
        cout << '-';
    }
    
    //拆分每一位,然后逆序放在数组中 
    la = x.size(); lb = y.size(); lc = max(la, lb);
    for(int i = 0; i < la; i++) a[la - i - 1] = x[i] - '0';
    for(int i = 0; i < lb; i++) b[lb - i - 1] = y[i] - '0';
    
    //模拟减法的过程 
    sub(c, a, b); // c = a - b
    
    //输出
    for(int i = lc - 1; i >= 0; i--) cout << c[i];
    return 0;
}

3. 高精度乘法

高精度乘法

算法原理:

无进位相乘再相加:

  • 还是「列竖式」,但是每一位相乘的时候不考虑进位,直接把乘的结果放在对应位上;
  • 等到所有对应位置「乘完」并且「累加完」之后,「统一处理进位」。

如下图所示:

即可理解为下标 + 下标 = 对应下标相乘位置 => c[i + j] += a[i] * b[j]

  1. 0 下标和 0 下标相乘在 0 下标位置,和 1 下标相乘在 1 下标位置,和 2 下标相乘在 2 下标位置
  2. 1 下标和 0 下标相乘在 1 下标位置,和 1 下标相乘在 2 下标位置,和 2 下标相乘在 3 下标位置
  3. 2 下标和 0 下标相乘在 2 下标位置,和 1 下标相乘在 3 下标位置,和 2 下标相乘在 4 下标位置

具体解法:

  1. 用字符串读入数据;
  2. 将字符串的每一位拆分,逆序放在数组中;
  3. 模拟无进位相乘再相加的过程:
    a. 对应位求乘积;
    b. 乘完之后处理进位;
    c. 处理余数;
  4. 处理前导零。

核心代码:

cpp 复制代码
void mul(int c[], int a[], int b[])
{
    // 无进位相乘,然后相加 
    for(int i = 0; i < la; i++)
    {
        for(int j = 0; j < lb; j++)
        {
            c[i + j] += a[i] * b[j]; 
        }
    }
    // 处理进位 
    for(int i = 0; i < lc; i++)
    {
        c[i + 1] += c[i] / 10;
        c[i] %= 10;
    }
    // 处理前导零 
    while(lc > 1 && c[lc - 1] == 0) lc--;
}

参考代码:

cpp 复制代码
#include <iostream>

using namespace std;

const int N = 1e6 + 10;

int a[N], b[N], c[N];
int la, lb, lc;
 
void mul(int c[], int a[], int b[])
{
    // 无进位相乘,然后相加 
    for(int i = 0; i < la; i++)
    {
        for(int j = 0; j < lb; j++)
        {
            c[i + j] += a[i] * b[j]; 
        }
    }
    // 处理进位 
    for(int i = 0; i < lc; i++)
    {
        c[i + 1] += c[i] / 10;
        c[i] %= 10;
    }
    // 处理前导零 
    while(lc > 1 && c[lc - 1] == 0) lc--;
}

int main()
{
    string x, y; cin >> x >> y;
    //拆分每一位,逆序放在数组中 
    la = x.size(); lb = y.size(); lc = la + lb;
    for(int i = 0; i < la; i++) a[la - 1 - i] = x[i] - '0';
    for(int i = 0; i < lb; i++) b[lb - 1 - i] = y[i] - '0';
    //模拟乘法的过程 
    mul(c, a, b); // c = a * b
    //输出
    for(int i = lc - 1; i >= 0; i--) cout << c[i];
    return 0;
}

4. 高精度除法

高精度除法

算法原理:

模拟小学「列竖式」计算「两数相除」的过程(注意:我们这里是「高精度 ÷ 低精度」,而「高精度 ÷ 高精度」题目比较少见,其模拟方式是用减法方法,可以下去自行了解)

定义一个指针 i 从「高位」遍历被除数,一个变量 t 标记当前「被除的数」,记除数是 b

  • 更新一个当前被除的数 t = t × 10 + a[i]
  • t / b 表示这一位的商,t % b 表示这一位的余数
  • 用 t 记录这一次的余数,遍历到下一位的时候重复上面的过程

被除数遍历完毕之后,t 里面存的就是余数,但是商可能存在前导 0 ,注意清空。

核心代码:

cpp 复制代码
void sub(int c[], int a[], int b)
{
    LL t = 0; // 标记每次除完之后的余数 
    for(int i = la - 1; i >= 0; i--)
    {
        // 计算当前的被除数 
        t = t * 10 + a[i];
        c[i] = t / b;
        t %= b;
    }
    // 处理前导 0 
    while(lc > 1 && c[lc - 1] == 0) lc--;
}

参考代码:

cpp 复制代码
#include <iostream>

using namespace std;
 
const int N = 1e6 + 10;

typedef long long LL;

int a[N], b, c[N];
int la, lc;

void sub(int c[], int a[], int b)
{
    LL t = 0; // 标记每次除完之后的余数 
    for(int i = la - 1; i >= 0; i--)
    {
        // 计算当前的被除数 
        t = t * 10 + a[i];
        c[i] = t / b;
        t %= b;
    }
    // 处理前导 0 
    while(lc > 1 && c[lc - 1] == 0) lc--;
}

int main()
{
    string x; cin >> x >> b;
    la = x.size();
    for(int i = 0; i < la; i++) a[la - 1 - i] = x[i] - '0';
    // 模拟除法的过程 
    lc = la;
    sub(c, a, b); // c = a / b
    for(int i = lc - 1; i >= 0; i--) cout << c[i];
 
    return 0;
}

结语

高精度运算在算法竞赛中占据重要地位,尤其在处理大整数运算时不可或缺。通过模拟手工计算的方法,高精度加法、减法、乘法和除法能够突破语言原生数据类型的限制,实现任意位数的精确计算。

掌握高精度运算的核心在于理解逐位处理与进位借位的逻辑,并优化存储与计算率。在实际竞赛中,灵活运用高精度算法可解决大数阶乘、大数取模、高精度浮点运算等复杂问题。

进一步优化高精度运算可结合快速傅里叶变换(FFT)加速乘法,或采用压位存储减少计算次数。持续练习与深入理解高精度算法,能显著提升解决复杂数学问题的力。

愿诸君能一起共渡重重浪,终见缛彩遥分地,繁光远缀天

相关推荐
郝学胜-神的一滴5 小时前
跨平台动态库与头文件:从原理到命名的深度解析
linux·c++·程序人生·unix·cmake
代码中介商5 小时前
C++ 仿函数(Functor)深度解析:从基础到应用
开发语言·c++
小雅痞5 小时前
[Java][Leetcode middle] 209. 长度最小的子数组
java·算法·leetcode
王老师青少年编程5 小时前
csp信奥赛C++高频考点专项训练之字符串 --【字符串基础】:[NOIP 2018 普及组] 标题统计
c++·字符串·csp·高频考点·信奥赛·专项训练·标题统计
做时间的朋友。5 小时前
精准核酸检测
java·数据结构·算法
冯诺依曼的锦鲤5 小时前
从零实现高并发内存池:TCMalloc 核心架构拆解
c++·学习·算法·架构
Thomas_Lee_OR5 小时前
多Agent路径规划 LaCAM for multi-agent path finding (MAPF)
算法·路径规划·仓储机器人·mapf
如君愿5 小时前
考研复习 Day28 | 习题--计算机网络第四章(网络层 中)、数据结构(树与二叉树 下)
数据结构·计算机网络·考研·课后习题·记录考研
一切皆是因缘际会5 小时前
可落地数字生命工程:从记忆厮杀到自我意识觉醒全链路,AGI内生智能硅基生命心智建模
人工智能·深度学习·算法·机器学习·ai·系统架构·agi
nlpming6 小时前
opencode Agent 详解
算法