大数运算模板

大整数相加

个人认为用 vector<int> 是最好的,也见过很多用字符串来写,但是字符串的单位是char,只有8位,乘法运算中,可能会出现溢出的问题。

模拟竖式运算。把数的每个数字放到 vector<int>,然后把数组倒过来,方便做进位运算。因为最高位补1,对数组来说 push_front 代价太大。做完运算后逆序输出。

例题: https://www.luogu.org/problem/P1601

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
//进位
void carry(vector<int>& a) {
	for (int i = 0; i < a.size(); i++) {
		//如果有进位,并且不是最高位,向高位进位
		if (i != a.size() - 1 && a[i] >= 10) {
			a[i + 1] += 1;
			a[i] -= 10;
		}
		//如果有进位,且是最高位,高位添1
		else if (a[i] >= 10) {
			a.push_back(1);
			a[i] -= 10;
		}
	}
}
// a = a + b
void add(vector<int>& a, vector<int>b) {
	//避免下面循环越界,让a的位数长于b
	if (b.size() > a.size())a.swap(b);
	//逐位相加
	for (int i = 0; i < b.size(); i++) {
		a[i] += b[i];
	}
	carry(a);
}

int main() {
	vector<int>a, b;
	string s1, s2;
	cin >> s1 >> s2;
	//将输入的数字倒过来存,倒过来更方便运算
	for (int i = s1.length() - 1; i >= 0; i--) {
		a.push_back(s1[i] - '0');
	}
	for (int i = s2.length() - 1; i >= 0; i--) {
		b.push_back(s2[i] - '0');
	}
	add(a, b);
	//倒过来输出
	for (int i = a.size() - 1; i >= 0; i--) {
		printf("%d", a[i]);
	}

	return 0;
}

大整数相乘

普通高精

倒过来运算,和加法差不多,最后统一进一次位。int 范围足够了,不会溢出

例题: https://www.luogu.org/problem/P1303

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

void carry(vector<int>& a) {
	for (int i = 0; i < a.size(); i++) {
		if (i != a.size() - 1 && a[i] >= 10) {
			a[i + 1] += a[i] / 10;
			a[i] %= 10;
		}
		else if (a[i] >= 10) {
			a.push_back(a[i] / 10);
			a[i] %= 10;
		}
	}
	//if (a[a.size() - 1] == 0) a.pop_back();
	//一般两个非零数相乘,最多只需要pop一次,但如果是0*某个数的话,这里就不只一次了
	while (a.size() > 1 && a[a.size() - 1] == 0) a.pop_back();
}
//a=a*b
void multi(vector<int>& a, vector<int>b) {
	//乘法运算,开两个数位数之和,确保不会超范围
	vector<int>t(a.size() + b.size());
	if (a.size() < b.size())a.swap(b);
	int k = 0;
	for (int i = 0; i < b.size(); i++) {
		for (int j = 0; j < a.size(); j++) {
			t[j + k] += (a[j] * b[i]);
		}
		k++;
	}
	carry(t);
	a = t;
}

int main() {
	vector<int>a, b;
	string s1, s2;
	cin >> s1 >> s2;
	for (int i = s1.length() - 1; i >= 0; i--) {
		a.push_back(s1[i] - '0');
	}
	for (int i = s2.length() - 1; i >= 0; i--) {
		b.push_back(s2[i] - '0');
	}
	multi(a, b);
	//倒过来输出
	for (int i = a.size() - 1; i >= 0; i--) {
		printf("%d", a[i]);
	}
	return 0;
}

快速傅里叶变换(FFT)

我不会

大整数相减

模拟竖式运算, a − b a-b ab 如果 a > b a>b a>b 结果一定是个正数,不需要考虑太多。如果 b > a b>a b>a,就计算 b − a b-a ba 然后对结果加负号。

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

bool operator<(const string s1, const string s2) {//重载<运算符,用于判断输入的a和b哪个大。
	if (s1.length() < s2.length())return true;
	if (s1.length() > s2.length())return false;
	if (s1.compare(s2) < 0)return true;
	return false;
}

bool flag = false;
//进位
void carry(vector<int>& a) {
	for (int i = 0; i < a.size(); i++) {
		if (i != a.size() - 1 && a[i] < 0) {
			a[i + 1] -= 1;
			a[i] += 10;
		}
	}
	//形如556-553,倒过来 - 最后前面会有两个0
	while (a.size() > 1 && a[a.size() - 1] == 0)
        a.pop_back();
}
// a = a - b
void subtract(vector<int>& a, vector<int>b) {
	for (int i = 0; i < b.size(); i++) {
		a[i] -= b[i];
	}
	carry(a);
}

int main() {
	vector<int>a, b;
	string s1, s2;
	cin >> s1 >> s2;
	if (s1 < s2) {
		flag = true;
		s1.swap(s2);
	}
	for (int i = s1.length() - 1; i >= 0; i--) {
		a.push_back(s1[i] - '0');
	}
	for (int i = s2.length() - 1; i >= 0; i--) {
		b.push_back(s2[i] - '0');
	}
	subtract(a, b);
	//倒过来输出
	if (flag)putchar('-');
	for (int i = a.size() - 1; i >= 0; i--) {
		printf("%d", a[i]);
	}
	return 0;
}

大整数除以小整数

小整数就是是可以用 int 或者 long long 表示出来的。

模拟竖式除法,这个是最好写的。

例题:https://www.luogu.com.cn/problem/P1480

#include <string>
#include <vector>
#include <iostream>
using namespace std;

string div(string a, int b = 2) {
	int x = 0;
	string res;
	int i = 0;
	for (; i < a.length(); i++) {
		a[i] ^= 48;
		x = x * 10 + a[i];
		res.push_back(x / b + '0');
		x %= b;
	}
	for (i = 0; res.length() > 1 && res[0] == 48; i++)
		res.erase(res.begin(), res.begin() + 1);   //去前导0
	//x中为最后的余数
	return res;
}

int main() {
	int b;
	string str;
	cin >> str >> b;
	cout << div(str, b);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值