在某些情况下,需要处理很大的整数,它无法在计算机中精确的表述和处理。若要精确的表示大整数,就必须使用软件的方法来实现大整数的运算。最常用的解决大整数运算的方法是使用一个二重循环,其算法时间复杂度为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
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 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
#include
#include
#include
#include
#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 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= 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= 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(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 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<
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");
}