大整數運算C++


//下面的代碼勉強算是bignum_beta1版本!
//實現了大整數的加減乘除四則運算,以及求兩個整數的最大公約數,以及求乘法逆,miller_rabin素性檢驗,平方_乘法算法
//不足之處,位數還很難擴展至幾千位,以及運算速度有一點慢,既然是beta1,說明bug還是挺多的
//程序缺少測試數據來測試,所以有的結果不敢保證其正確性
//由於使用c++復寫了很多運算符,加入這個文件之后,大數bignum可以看做是一個如同如同int一樣的基本類型
//可以像int一樣加減乘除和輸入輸出

#include<iostream>
#include<string>
#include<ctime>//用於產生隨機數
using namespace std;
const int base=1000;//base用來表示數組中每個數的進制,逢base向前一位進1
const int MAX_LEN=300;//數組的最大長度

class bigNum{
public:
    int num[MAX_LEN];
    int len;
    int flag;//增設一個標志,表示正負,這樣大數包就可以擴展置負數

    friend istream& operator>>(istream& input,bigNum &obj);
    friend ostream& operator<<(ostream& output,bigNum& obj);

    bigNum &operator=(const bigNum &s);//對於"="號的重載
    //類的賦值運算符"="只能重載為成員函數,而不能把它重載為友元函數

    bigNum();//構造函數
    void eucli_setnum(int x);//設置數值
};

void bigNum::eucli_setnum(int x)//設置這個函數主要應對擴展的歐幾里德算法
{
    num[0]=x;
    if(x!=0) 
    len=1;
    else len=0;
}

bigNum::bigNum()//構造函數
{
    memset(num,0,sizeof(num));//清零
    len=0;
    flag=1;//默認的數為正數
}


//關於下面的運算符重載函數,有一點需要特別記住,那就是len一定要記得更新,不然會出錯!

//以下的幾個函數都是邏輯運算符的重載函數
bool operator==(bigNum &a,bigNum &b)//"=="號的重載
{
    for(int i=MAX_LEN-1;i>=0;i--)
        if(a.num[i]!=b.num[i])
            return false;
    return true;
}

bool operator!=(bigNum &a,bigNum &b)//"!="號的重載
{
    for(int i=0;i<MAX_LEN;i++)
        if(a.num[i]!=b.num[i])
            return true;//只要有一個不相等,就返回true
    return false;
}

/*
bool operator!=(bigNum &a,int &b)//"!="號的重載
{
    if(a.num[0]!=b)
    return false;//只要有一個不相等,就返回true
    for(int i=1;i<MAX_LEN-1;i++)
     if(a.num[i]!=0)
    return false;
    return true;
}*/

bool operator>(bigNum &a,bigNum &b)//">"號的重載
{
    for(int i=MAX_LEN-1;i>=0;i--)//從最高位向下搜索
        if(a.num[i]!=b.num[i])//如果有兩個數不相等,必定有一大一小
            if(a.num[i]>b.num[i])
            return true;
            else return false;
    return false;//兩個數相同也返回false

}

bool operator<(bigNum &a,bigNum &b)//"<"號的重載
{
    for(int i=MAX_LEN-1;i>=0;i--)//從最高位向下搜索
    {
        if(a.num[i]!=b.num[i])//如果有兩個數不相等,必定有一大一小
         if(a.num[i]<b.num[i])
            return true;
         else
            return false;
    }
    return false;
}

bool operator<=(bigNum &a,bigNum &b)
{
    for(int i=MAX_LEN-1;i>=0;i--)//從最高位向下搜索
    if(a.num[i]!=b.num[i])//如果有兩個數不相等,必定有一大一小
         if(a.num[i]<b.num[i])
            return true;
         else
            return false;
    return true;//最后相等返回true
}
bool operator>=(bigNum &a,bigNum &b)
{
    for(int i=MAX_LEN-1;i>=0;i--)//從最高位向下搜索
    if(a.num[i]!=b.num[i])//如果有兩個數不相等,必定有一大一小
         if(a.num[i]>b.num[i])
            return true;
         else
            return false;
    return true;//最后相等返回true
}

bigNum &bigNum::operator=(const bigNum &s)//"="號的重載
{
    if(this==&s) return *this;//防止s=s
    for(int i=0;i<MAX_LEN;i++)
        num[i]=s.num[i];
    len=s.len;
    flag=s.flag;
}

//以下幾個函數是四則運算符的重載函數

bigNum operator-(bigNum a,bigNum b);//聲明,防止編譯出錯

bigNum operator+(bigNum a,bigNum b)//加法的重載
{
    bigNum sum;//存儲結果
    int i;

     if(a.flag<0 && b.flag>0)//a為負,b為正,則a+b=b-|a|
    {
        a.flag=1;//這里對a進行了修改(將a變為正數),以便於進行減法運算,這也是重寫不用引用的reason
        sum=b-a;
        if(b>a)
         sum.flag=1;//結果為正
        else
         sum.flag=-1;//結果為負
        return sum;
    }

    if(a.flag>0 && b.flag<0)//a為正,b為負,則b+a=a-|b|
    {
        b.flag=1;
        sum=a-b;
        if(a>b)
        sum.flag=1;//結果為正
        else
        sum.flag=-1;//結果為負
        return sum;
    }
    //余下的情況是a,b兩者符號相同,即a+b=(|a|+|b|)*flag,flag與a,b符號一致


    for(i=0;i<MAX_LEN;i++)
    {
        sum.num[i]+=a.num[i]+b.num[i];
        if(sum.num[i]>base)//超出base,則要進位
        {
            sum.num[i]-=base;
            sum.num[i+1]++;
        }
        if(sum.num[i]!=0) sum.len=i+1;//len要同步更新

    }
    sum.flag=a.flag;//如果a,b不是一正一負,那么a,b必定同號
    return sum;
}

bigNum operator-(bigNum a,bigNum b)//減法的重載
{
    bigNum sum;//存儲結果
    if(a.flag<0 && b.flag>0)//a為負,b為正,則a-b=-(|a|+|b|)
    {
        a.flag=1;
        sum=b+a;
        sum.flag=-1;//兩個負數相加,結果一定為負數
        return sum;
    }

    if(a.flag>0 && b.flag<0 && a>b)//a為正,b為負,則a-b=|a|+|b|
    {
        b.flag=1;
        sum=b+a;
        sum.flag=1;//兩個正數相加,結果一定為正數
        return sum;
    }
//下面a,b的符號值一致

    if(a<b)//a<b,則|a|-|b|<0,轉化為-(|b|-|a|)
    {
        sum=b-a;
        sum.flag=-b.flag;
        return sum;
    }
//下面表示的就是|a|>|b|,且a,b同號
    for(int i=0;i<MAX_LEN;i++)
    {
        a.num[i]-=b.num[i];
        if(a.num[i]<0)//不夠減時向前借位
        {
            a.num[i]+=base;
            a.num[i+1]--;
        }
        if(a.num[i]!=0) a.len=i+1;//len要同步更新
    }
    return a;
}

bigNum operator*(bigNum &a,bigNum &b)//對於乘法的重載
{//乘法的flag已經設置完畢
    bigNum sum;
    int i,j;
    for(i=0;i<b.len;i++)//用第二個數b乘以第一個數a
    {
        for(j=0;j<a.len;j++)
            sum.num[i+j]+=b.num[i]*a.num[j];//先乘起來,后面統一進位
    }

    for(i=0;i<MAX_LEN;i++)//循環統一處理進位問題
    {
        if(sum.num[i]>=base)
        {
            sum.num[i+1]+=sum.num[i]/base;
            sum.num[i]%=base;
        }
        if(sum.num[i]!=0) sum.len=i+1;//len要同步更新
    }
    //現在設置數的正負
    if(a.flag+b.flag==0) sum.flag=-1;
    else sum.flag=a.flag;
    return sum;
}

int substract(int *p1,int *p2,int n1,int n2)
{
    int i;
    //被除數不能小於除數
    if(n1<n2) return -1;//p2數的長度不能大於p1數的長度
    if(n1==n2)//兩數長度一致情況下(所占用數組長度),p2數要小於p1數
    {
        for(i=n1-1;i>=0;i--)
        {
            if(p1[i]>p2[i]) break;
            else if(p1[i]<p2[i]) return -1;

        }
    }

    for(i=0;i<n1;i++)
    {//減去一個p2值
        p1[i]-=p2[i];
        if(p1[i]<0)
        {
            p1[i]+=base;
            p1[i+1]--;
        }
    }

    for(i=n1-1;i>=0;i--)
        if(p1[i])
            return i+1;//返回所占用的數組長度
    return 0;

}

bigNum operator/(bigNum a,bigNum b)//除法的重載
{//除法的flag設置完畢
    bigNum sum;
    int i,j;

    if(a<b)//a<b時返回0
        return sum;
    int nTimes=a.len-b.len;

    if(nTimes>0)
    {
        for(i=a.len-1;i>=nTimes;i--)
            b.num[i]=b.num[i-nTimes];//朝高位移動
        for(;i>=0;i--)
            b.num[i]=0;//低位補0
        b.len=a.len;
    }

    for(j=0;j<=nTimes;j++)
    {
        int nTmp;
        //一直減到不夠減為止

        while((nTmp=substract(a.num,b.num+j,a.len,b.len-j))>=0)
        {
            a.len=nTmp;
            sum.num[nTimes-j]++;//每減成功一次,則將商的對應為加1
        }
        if(sum.len==0 && sum.num[nTimes-j]!=0)
            sum.len=nTimes-j+1;//同步更新len
    }
    //現在設置數的正負
    if(a.flag+b.flag==0) sum.flag=-1;
    else sum.flag=a.flag;

    return sum;
}

bigNum operator%(bigNum &a,bigNum &b)//取模運算的重載
{
    return a-b*(a/b);
}

istream& operator>>(istream& input,bigNum& obj)//重載輸入函數
{//輸入flag已經設置完畢

    string str;
    input>>str;

    int l=str.size();//l為字符串長度
    int i,k,j;
    for(j=0,i=base;i!=1;)
        if(i>0)
        {
            j++;
            i=i/10;
        }//j用來表示base的位數

    int p=l/j,q=l%j;//輸入的數按照每個可以存放j個的標准,恰好放進,一共占用p個位置
    if(q) obj.len=p+1;//當然,不一定恰好放進,就需要p+1個位置來放
    else obj.len=p;

    if(str[0]=='-')//輸入為負數
        obj.flag=-1;
    else
        obj.flag=1;//設置符號位,正數則flag為1,否則為-1


    for(i=0;i<q;i++)//用來存放不能整除的高位部分
    {
        if(str[i]=='-') i++;//如果是負數的話,第一位不用處理
        obj.num[p]=obj.num[p]*10+str[i]-'0';
    }
    p--;

    for(;p>=0;p--)//下面的字符,以j為一組,字符個數恰好能夠被j整除,一組組存入num數組里
    {
        for(k=1;k<=j;k++)
        {
         obj.num[p]=obj.num[p]*10+str[i]-'0';
         i++;
        }
    }
  return input;
}

ostream& operator<<(ostream& output,bigNum& obj)
{//輸出flag就已經設置好了
    int i;
    for(i=MAX_LEN-1; (i>=0)&&(obj.num[i]==0);i--);
    if(i>=0)
    {
        if(obj.flag==-1) output<<'-';
        for(;i>=0;i--)
        output<<obj.num[i];
    }
    else
    output<<'0';//整個數組都是0
    return output;
}


bigNum extended_euclidean(bigNum n,bigNum m,bigNum &x,bigNum &y)//擴展的歐幾里德算法的另一種形式  
{  
    bigNum x1, x2, x3=n;  
    x1.eucli_setnum(1);
    x2.eucli_setnum(0);

    bigNum y1, y2, y3=m;  
    y1.eucli_setnum(0);
    y2.eucli_setnum(1);
    bigNum zero;
    while(x3%y3!=zero)  
    {  
        bigNum d=x3/y3;  
        bigNum t1,t2,t3; 

        t1=x1-d*y1; 
        t2=x2-d*y2;  
        t3=x3-d*y3; 

        x1=y1; x2=y2; x3=y3;  
        y1=t1; y2=t2; y3=t3;  
    }  
    x=y1; y=y2;  
    return y3;  
}  

bigNum gcd(bigNum &n,bigNum &m)//求兩個大數的最大公約數
{
    bigNum x,y;
    return extended_euclidean(n,m,x,y);    
}

//求乘法逆其實也沒有特別好的算法,主要還是依靠歐幾里德算法
bigNum mutirinverse(bigNum &n,bigNum &m)//求乘法逆
{
    bigNum x,y;
    extended_euclidean(m,n%m,x,y);  
    return x;
}


//平方——乘法算法
bigNum Square_and_Mutiply(bigNum a,bigNum m,bigNum n)
{
    bigNum sum,zero,two;
    two.eucli_setnum(2);
    sum.eucli_setnum(1);
    int length=1;
    int bin[300];
    //先將m轉化為二進制
    do
    {
        sum=m%two;
        bin[length++]=sum.num[0];
        m=m/two;
    }while(m!=zero);

    sum.eucli_setnum(1);

    while(length>=0)
    {
      sum=(sum*sum)%n;
      if(bin[length]==1)
      {
            sum=(sum*a)%n;
      }
      length--;
    }
    return sum;
}


//最后一個函數,用於素數判定的Miller-Rabin算法
bool wintess(bigNum a,bigNum n)
{
    bigNum m,x,y,one,two,zero;
    one.eucli_setnum(1);two.eucli_setnum(2);
    bigNum i,j;

    m=n-one;
    while(m%two==zero)
    {
        m=m/two;
        j=j+one;
    }
   x=Square_and_Mutiply(a,m,n);
   for(i.eucli_setnum(1);i<=j;i=i+one)
   {
       y=Square_and_Mutiply(x,two,n);
       if((y==one)&&(x!=one)&&(x!=n-one))
           return true;
       x=y;
   }
   if(y!=one) return true;
   return false;

}
bool Miller_Robin(int times,bigNum &n)
    //n為大於3的奇數,輸出n是否通過素性檢驗
{
    bigNum a,one,two,random;
    one.eucli_setnum(1);two.eucli_setnum(2);
    if(n==one) return false; if(n==two) return true;
    srand((unsigned)time(0));
    for(int i=1;i<=times;i++)
    {
        random.eucli_setnum(rand());
        a=random%(n-two)+two;
        if(wintess(a,n)) return false;
    }
    return false;
}

int main()
{
    bigNum a,b;
    while(1)
    {
        cin>>a;
        cin>>b;
        cout<<a*b<<endl;
    }
    system("pause");
    return 0;
}

byte傳輸的最小單位

1bit =8 byte;

密碼學算法最重要的就是大整數的運算和字符的裝換 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM