高精度計算_大整數_加_減_乘_除_大整數_基於vector_高效計算_模板
阿新 • • 發佈:2018-11-09
本文給出的是, 相對偏重於計算效率的大整數運算模板, 程式碼數量整體多餘前面介紹的基於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); return carry? res.push_back(1), res: 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(針對b = 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), 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); return carry? res.push_back(1), res: 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(針對b = 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), 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; }
測試程式執行截圖(部分):