1. 程式人生 > >大整數_加_減_乘_除_大整數_基於vector_高效計算

大整數_加_減_乘_除_大整數_基於vector_高效計算

    本文給出的是, 相對偏重於計算效率的大整數運算模板, 程式碼數量整體多餘前面介紹的基於deque的大整數計算模板, 當然如果僅需要大整數與int型運算, 那麼請參考本人有關大整數和int型計算的部落格.

    為何稱本文給出的高效計算的模板, 原因可概括為兩點, 第一: 使用相比deque執行效率更高的vector(前提是在指定操作下), 第二: 對於大整數除法, 由於採用更高效的試商方案, 使得試商次數減至1次, 計算效率大幅度提高, 但代價是實現相對複雜, 為了說明本模板中大整數除法的試商原理, 我於模板程式碼下方給出了其原理解釋和形式化的數學證明, 不過因為篇幅關係, 給出的是手寫版的截圖.

約定: 下面的模板中, a和b均為HEX進位制數, a的位數 = a.size(), b的位數 = b.size(), a[0], b[0]分別對應a, b的最低位.

const int HEX = 1e6;//運算採取的進位制
vector<int> operator + (const vector<int> &a, const vector<int> &b){//返回a + b的結果
	vector<int> res; res.reserve(max(a.size(), b.size()) + 1);//設定保留容量
	int carry = 0, len = max(a.size(), b.size());
	for(int i = 0, tmp; i < len; ++i) //計算a - b的第i位
		tmp = (i < a.size()? a[i]: 0) + (i < b.size()? b[i]: 0) + carry
		, carry = tmp / HEX, res.push_back(tmp % HEX);
	if(carry) res.push_back(1);
	return res;
}
vector<int> operator - (const vector<int> &a, const vector<int> &b){//返回a - b的結果,要求a >= b
	vector<int> res; res.reserve(a.size());
	for(int i = 0, rent = 0, tmp; i < a.size(); ++i)//計算a - b結果的第i位, rent:借位標誌 
		tmp = a[i] - (i < b.size()? b[i]: 0) - rent
		, rent = (tmp < 0? 1: 0), res.push_back(tmp < 0? tmp + HEX: tmp);
	while(res.size() > 1 && !res.back()) res.pop_back();//去除最高位多餘0 
	return res;
}
vector<int> operator * (const vector<int> &a, const int b){//返回a * b(b為int型數,b < HEX)的結果
	vector<int> res; res.reserve(a.size() + 1);
	int carry = 0;//進位值
	for(long long i = 0, tmp; i < a.size(); ++i)
		tmp = (long long)a[i] * b + carry
		, carry = tmp / HEX, res.push_back(tmp % HEX);
	if(carry) res.push_back(carry);
	while(res.size() > 1 && !res.back()) res.pop_back();//去除最高位多餘0
	return res;
}
vector<int> operator * (const vector<int> &a, const vector<int> &b){//返回a * b的結果
	vector<int> res(1, 0), tmp; res.reserve(a.size() + b.size());
	for(int i = 0; i < b.size(); ++i)
		tmp = a * b[i], tmp.insert(tmp.begin(), i, 0)//在tmp前新增i個0
		, res = res + tmp;
	return res;
}
bool comp_vector(const vector<int> &a, const vector<int> &b){//若a <= b返回true,否則返回fasle
	if(a.size() != b.size()) return a.size() < b.size();
	return !lexicographical_compare(b.rbegin(), b.rend(), a.rbegin(), a.rend()); 
}
vector<int> operator / (const vector<int> &a, const vector<int> &b){//返回a / b的整數部分
	vector<int> res; res.reserve(a.size() - b.size() + 1);
	vector<int> rem(1, 0); rem.reserve(b.size());
	for(int i = a.size() - 1; i >= 0; --i) {
		if(!rem.back()) rem.clear();	
		rem.insert(rem.begin(), a[i]); 
		if(rem.size() < b.size()){
			res.push_back(0); 
			continue;
		}
		if(rem.size() <= 2){
			long long tmp1 = 0, tmp2 = 0;
			for(int j = rem.size() - 1, k = b.size() - 1; j >= 0; --j, --k){
				tmp1 = tmp1 * HEX + rem[j];
				if(k < 0) continue;
				tmp2 = tmp2 * HEX + b[k];
			}
			res.push_back(tmp1 / tmp2), rem = rem - (b * res.back());
			continue;
		}
		long long tmp1 = 0, tmp2 = 0;//至此rem.szie() >= 3;
		for(int j = rem.size() - 1, k = b.size() - 1; j >= (int)rem.size() - 3; --j, --k){
			//注意:上面的j >= (int)rem.size() - 3中的(int)不可去除,若去除當j為
			//-1時將自動被轉型為無符號數,從而引發非常隱蔽的錯誤 
			tmp1 = tmp1 * HEX + rem[j];
			if(rem.size() > b.size() && j == rem.size() - 3) continue;
			tmp2 = tmp2 * HEX + b[k];
		}
		if(tmp2 == HEX){
			res.push_back(HEX - 1), rem = rem - (b * res.back());
			continue;
		}
		int quo = (tmp1 + 1) / tmp2;
		vector<int> vec_tmp = b * quo;
		res.push_back(comp_vector(vec_tmp, rem)? quo: quo - 1), rem = rem - (b * res.back());
	}
	reverse(res.begin(), res.end());//注意:將res反轉才是正確的商 
	while(res.size() > 1 && !res.back()) res.pop_back();
	return res;
}
void show(const vector<int> &num){//列印num表示的整數值 
	printf("%d", num.back());
	for(int i = num.size() - 2; i >= 0; --i) printf("%06d", num[i]);
} 

下面是對於除法試商方案的原理解釋及證明:

下面的測試程式充分展示了本模板使用方法:

//高精度大整數加,減,乘,除(基於vector_高效計算)
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <windows.h>
using namespace std;
const int HEX = 1e6;//運算採取的進位制
vector<int> operator + (const vector<int> &a, const vector<int> &b){//返回a + b的結果
	vector<int> res; res.reserve(max(a.size(), b.size()) + 1);//設定保留容量
	int carry = 0, len = max(a.size(), b.size());
	for(int i = 0, tmp; i < len; ++i) //計算a - b的第i位
		tmp = (i < a.size()? a[i]: 0) + (i < b.size()? b[i]: 0) + carry
		, carry = tmp / HEX, res.push_back(tmp % HEX);
	if(carry) res.push_back(1);
	return res;
}
vector<int> operator - (const vector<int> &a, const vector<int> &b){//返回a - b的結果,要求a >= b
	vector<int> res; res.reserve(a.size());
	for(int i = 0, rent = 0, tmp; i < a.size(); ++i)//計算a - b結果的第i位, rent:借位標誌 
		tmp = a[i] - (i < b.size()? b[i]: 0) - rent
		, rent = (tmp < 0? 1: 0), res.push_back(tmp < 0? tmp + HEX: tmp);
	while(res.size() > 1 && !res.back()) res.pop_back();//去除最高位多餘0 
	return res;
}
vector<int> operator * (const vector<int> &a, const int b){//返回a * b(b為int型數,b < HEX)的結果
	vector<int> res; res.reserve(a.size() + 1);
	int carry = 0;//進位值
	for(long long i = 0, tmp; i < a.size(); ++i)
		tmp = (long long)a[i] * b + carry
		, carry = tmp / HEX, res.push_back(tmp % HEX);
	if(carry) res.push_back(carry);
	while(res.size() > 1 && !res.back()) res.pop_back();//去除最高位多餘0
	return res;
}
vector<int> operator * (const vector<int> &a, const vector<int> &b){//返回a * b的結果
	vector<int> res(1, 0), tmp; res.reserve(a.size() + b.size());
	for(int i = 0; i < b.size(); ++i)
		tmp = a * b[i], tmp.insert(tmp.begin(), i, 0)//在tmp前新增i個0
		, res = res + tmp;
	return res;
}
bool comp_vector(const vector<int> &a, const vector<int> &b){//若a <= b返回true,否則返回fasle
	if(a.size() != b.size()) return a.size() < b.size();
	return !lexicographical_compare(b.rbegin(), b.rend(), a.rbegin(), a.rend()); 
}
vector<int> operator / (const vector<int> &a, const vector<int> &b){//返回a / b的整數部分
	vector<int> res; res.reserve(a.size() - b.size() + 1);
	vector<int> rem(1, 0); rem.reserve(b.size());
	for(int i = a.size() - 1; i >= 0; --i) {
		if(!rem.back()) rem.clear();	
		rem.insert(rem.begin(), a[i]); 
		if(rem.size() < b.size()){
			res.push_back(0); 
			continue;
		}
		if(rem.size() <= 2){
			long long tmp1 = 0, tmp2 = 0;
			for(int j = rem.size() - 1, k = b.size() - 1; j >= 0; --j, --k){
				tmp1 = tmp1 * HEX + rem[j];
				if(k < 0) continue;
				tmp2 = tmp2 * HEX + b[k];
			}
			res.push_back(tmp1 / tmp2), rem = rem - (b * res.back());
			continue;
		}
		long long tmp1 = 0, tmp2 = 0;//至此rem.szie() >= 3;
		for(int j = rem.size() - 1, k = b.size() - 1; j >= (int)rem.size() - 3; --j, --k){
			//注意:上面的j >= (int)rem.size() - 3中的(int)不可去除,若去除當j為
			//-1時將自動被轉型為無符號數,從而引發非常隱蔽的錯誤 
			tmp1 = tmp1 * HEX + rem[j];
			if(rem.size() > b.size() && j == rem.size() - 3) continue;
			tmp2 = tmp2 * HEX + b[k];
		}
		if(tmp2 == HEX){
			res.push_back(HEX - 1), rem = rem - (b * res.back());
			continue;
		}
		int quo = (tmp1 + 1) / tmp2;
		vector<int> vec_tmp = b * quo;
		res.push_back(comp_vector(vec_tmp, rem)? quo: quo - 1), rem = rem - (b * res.back());
	}
	reverse(res.begin(), res.end());//注意:將res反轉才是正確的商 
	while(res.size() > 1 && !res.back()) res.pop_back();
	return res;
}
void show(const vector<int> &num){//列印num表示的整數值 
	printf("%d", num.back());
	for(int i = num.size() - 2; i >= 0; --i) printf("%06d", num[i]);
} 
int main() {
	long long start = GetTickCount();
	vector<int> a(1, 1), j(1, 1), one(1, 1);
	for(int i = 1; i <= 1000; ++i) a = a * j, j = j + one;
	printf("Test case 1: a = "), show(a), printf("\n"); 
	long long end = GetTickCount();
	cout << "1000!計算完成,用時" << (end - start) << "ms" << endl;
	j.clear(), j.push_back(1);
	for(int i = 1; i <= 1000; ++i) a = a / j, j = j + one;
	printf("Test case 2: "), show(a);
	return 0;
}

測試程式執行截圖(部分):