标签:分治策略 大整数乘法
在某些情况下,需要处理很大的整数,它无法在计算机中精确的表述和处理。若要精确的表示大整数,就必须使用软件的方法来实现大整数的运算。最常用的解决大整数运算的方法是使用一个二重循环,其算法时间复杂度为O(m*n)(其中m,n分别为两个大整数的长度);而选用分治方法则可以将算法时间复杂度降到O(n^(log3))(两个大整数的长度同为n)。但分治方法的算法实现较为复杂,针对这个问题,本文借助标准C++实现了分治方法求解大整数乘法的算法。
下面分别介绍两种算法原理,及其实现:
1.使用二重循环控制两个数的各位数按位交叉相乘的过程:
(1)两个乘数存储在字符串数组中,计算结果存储在整型数组中;
(2)k位数字与j位数字相乘的表达式为:(sl[k]-48)*(s2[j]-48);
(3)结果数组中的元素只保存一位数字,因此要考虑余数与进位,表达式为:
b = a[i] + s1[i]-48)*(s2[j]-48) + d。
算法实现如下:
#include <iostream> using namespace std; void algrithem1(const char s1[], const char s2[], int a[]) { int n1 = strlen(s1); int n2 = strlen(s2); int highestpos = 0; for(int i1=0, k=n1-1; i1<n1; i1++, k--) { for(int i2=0, j=n2-1; i2<n2; i2++, j--) { int i = i1 + i2; int carrier = (s1[k] - 48) * (s2[j] - 48); while(carrier > 0) { int temp = a[i] + carrier; a[i] = temp % 10; carrier = temp /10; i++; } highestpos = i; } } a[highestpos] = -1; }
2.假设X,Y都是n为的二进制整数,现在要计算他们的乘积,可如下表述:
X = AB, Y = CD
X*Y = (A*2^(n/2) + B)(C*2^(n/2) + D)
= A*C*2^n + (A*D + C*B)*2^(n/2) + B*D
算法时间复杂度为O(n^2)。
对上述模型进行改进如下:
X*Y = A*C*2^n + [(A-B)(D-C) + A*C + B*D]*2^(n/2) + B*D
这样乘法的次数减少了,算法复杂度为O(n^log3)。
算法实现如下:
#include <iostream> #include <string> #include <vector> #include <cassert> #include <stdexcept> #define OUT(x) cout << #x << (x) << endl; using namespace std; class ParaErrEx : public logic_error { public: ParaErrEx(const string& msg) : logic_error(msg) { } }; class Mstring { public: int sign; //the sign of a great number string num; //restore the great number Mstring() : sign(0), num("") { } Mstring(int sign, string num="") : sign(sign), num(num){ } Mstring(const Mstring& cp) { sign = cp.sign; num = cp.num; } Mstring& operator=(const Mstring& cp) { if(&cp != this) { sign = cp.sign; num = cp.num; } return *this; } int length() const{ return num.length(); } Mstring insert(int pos, char s) { num.insert(pos, string(1, s)); return *this; } Mstring substr(int strt, int len) const{ return Mstring(sign, num.substr(strt, len)); } char operator[](int n) const { assert(n >= 0 && n < num.length()); return num[n]; } Mstring operator-() const{ return Mstring(-sign, num); } }; Mstring operator+(Mstring a, Mstring b); Mstring operator-(Mstring a, Mstring b); Mstring operator*(const Mstring& a, const Mstring& b); Mstring operator<<(const Mstring& a, int n); Mstring abs(const Mstring& a); bool operator>(const Mstring& a, const Mstring& b); bool operator==(const Mstring& a, const Mstring& b); static void reSizeToTheSame(Mstring& a, Mstring& b); static bool checkNumLength(int n); static void reSizeNum(Mstring& a, Mstring& b); static void adjustRst(const Mstring& a); static Mstring algrithem2(const Mstring& xx, const Mstring& yy); Mstring twoGreatMul(const Mstring& x, const Mstring& y); Mstring operator+(Mstring a, Mstring b) { if(a.length() != b.length()) { reSizeToTheSame(a, b); } assert(a.length() == b.length());//check if previous operations succedssful /* *Finish the add operation according to different situations. */ Mstring s; //for return use if(a.sign == b.sign) {//situ1: a, b have the same sign, directly add them s.sign = a.sign; int carrier = 0; for(int i=0; i<a.length(); i++) { int sum = (a[i] - 48) + (b[i] - 48) + carrier; if(sum > 9) { s.insert(s.length(), (sum)%10 + 48); carrier = sum/10; }else { s.insert(s.length(), (sum + 48)); carrier = 0; } } if(carrier > 0) { s.insert(s.length(), carrier + 48); } }else if(a.sign > b.sign){//situ2: a positive or zero, and b negative /*Compare the abs value of a and b, then decide the return value‘s *sign and num according to the compare result.*/ if(abs(a) > abs(b)) { s.sign = a.sign; int borrow = 0; for(int i=0; i<a.length(); i++) { int minus = (a[i]-48+borrow) - (b[i]-48); if (minus < 0) { minus = (a[i]-48+borrow+10) - (b[i]-48); borrow = -1; }else { borrow = 0; } assert(minus >= 0); s.insert(s.length(), (‘0‘+minus)); } }else if(abs(a) == abs(b)){ s.sign = 1; s.num = "0"; }else { s.sign = -1; int borrow = 0; for(int i=0; i<b.length(); i++) { int minus = (b[i]-48+borrow) - (a[i]-48); if (minus < 0) { minus = (b[i]-48+borrow+10) - (a[i]-48); borrow = -1; }else { borrow = 0; } assert(minus >= 0); s.insert(s.length(), ‘0‘+minus); } } }else { //situ3: a negtive, and b positive or zero s = operator+(b, a);//turn to situ2 } return s; } Mstring operator-(Mstring a, Mstring b) { return operator+(a, -b); } Mstring operator*(const Mstring& a, const Mstring& b) { assert(a.sign==1 && b.sign==1);//make sure a and b both positive assert(a.length()==1 && b.length()==1);//make sure one bit muliple one bit Mstring s; int carrier = (a[0] - 48) * (b[0] - 48); int i=0; if(carrier > 9) { s.insert(0, ‘0‘ + carrier % 10); s.insert(1, ‘0‘ + carrier / 10); }else { s.insert(0, ‘0‘ + carrier); } return s; } Mstring operator<<(const Mstring& a, int n) { assert(a.length() != 0 && n>=0); Mstring s; s.sign = a.sign; s.num = a.num; for(int i=0; i<n; i++) { s.insert(0, ‘0‘); } return s; } Mstring abs(const Mstring& a) { int sign; if(a.sign == -1) { sign = 1; }else { sign = a.sign; } return Mstring(sign, a.num); } bool operator>(const Mstring& a, const Mstring& b) { assert(a.length()==b.length()); assert(a.sign == 1 && b.sign == 1); for(int i=a.length()-1; i>=0; i--) { if(a[i] > b[i]) { return true; }else if(a[i] < b[i]) { return false; } } return false; } bool operator==(const Mstring& a, const Mstring& b) { assert(a.sign==1 && b.sign==1 && a.length()==b.length()); for(int i=0; i<a.length(); i++) { if(a[i] != b[i]) { return false; } } return true; } static void reSizeToTheSame(Mstring& a, Mstring& b) { int maxlen = a.length() > b.length() ? a.length() : b.length(); int i = a.length(); while(i < maxlen) { a.insert(a.length(), ‘0‘); i++; } i = b.length(); while(i < maxlen) { b.insert(b.length(), ‘0‘); i++; } } static bool checkNumLength(int n) { assert(n > 0); if(n == 1 || n == 2) { return true; }else { while(n != 2) { if(n%2!=0) { return false; } n = n/2; } } return true; } static void reSizeNum(Mstring& a, Mstring& b) { assert(a.length() > 0 && b.length() > 0); int maxlen = a.length() > b.length() ? a.length() : b.length(); int newlen = 1; if( !checkNumLength(maxlen) ) { while(newlen < maxlen) { newlen *= 2; } }else { newlen = maxlen; } int i = a.length(); while(i < newlen) { a.insert(0, ‘0‘); i++; } i = b.length(); while(i < newlen) { b.insert(0, ‘0‘); i++; } } static Mstring adjustRst(Mstring& a) { int pos = a.num.find_last_not_of("0"); if(pos != string::npos) { a.num = a.num.substr(0, pos+1); int left = 0; int right = a.length()-1; while(right > left) { a.num[left] = a.num[left] ^ a.num[right]; a.num[right] = a.num[left] ^ a.num[right]; a.num[left] = a.num[left++] ^ a.num[right--]; } }else { a.num = "0"; } return a; } static Mstring algrithem2(const Mstring& xx, const Mstring& yy) { if(!( (xx.length() >= 0 && yy.length() >= 0) && (xx.sign == 1 || xx.sign == -1) && (yy.sign == 1 || yy.sign == -1) ) ) { string str = "Parameter error happened! Check" " Mstring object‘s sign(-1 or 1)" " and num(length at least 1)."; throw ParaErrEx(str); } /* *Seperately comupte sign multiple and * num multiple of x and y. */ Mstring s; //for return use s.sign = xx.sign * yy.sign;//compute sign Mstring x = abs(xx);//make sure x is positive now Mstring y = abs(yy);//make sure y is positive now if( !( (x.length() == y.length()) && checkNumLength(x.length()) && checkNumLength(x.length()) ) ) { reSizeNum(x, y); assert(x.length() == y.length()); assert(checkNumLength(x.length())); assert(checkNumLength(x.length())); } int n = x.length(); if(n == 1) { if(x.num=="0" || y.num=="0") { s.sign = 1; s.num = "0"; return s; }else { Mstring temp = x*y; s.num = temp.num; return s; } }else { Mstring a = x.substr(0, n/2); Mstring b = x.substr(n/2, n/2); Mstring c = y.substr(0, n/2); Mstring d = y.substr(n/2, n/2); Mstring m1 = algrithem2(a, c); Mstring m2 = algrithem2(a-b, d-c); Mstring m3 = algrithem2(b, d); Mstring temp = (m1<<n) + ( (m1+m2+m3)<<(n/2) ) + m3; s.num = temp.num; return s; } } Mstring twoGreatMul(const Mstring& x, const Mstring& y) { Mstring rst = algrithem2(x, y); adjustRst(rst); return(rst); }
3.一个测试代码:
int main(void) { char s1[255] = "1234567812345678"; char s2[255] = "12345678"; int a[255] = {0}; algrithem1(s1, s2, a); int i = 0; while(a[i] != -1) { cout << a[i]; i++; } cout << endl; Mstring a1(1, "1234567812345678"); Mstring b1(1, "12345678"); Mstring s; try { s = twoGreatMul(a1, b1); }catch(ParaErrEx& e) { cout << e.what() << endl; } cout << s.sign << endl; cout << s.num << endl; system("pause"); }
本文出自 “Remys” 博客,转载请与作者联系!
标签:分治策略 大整数乘法
原文地址:http://remyspot.blog.51cto.com/8218746/1552622