《程序員密碼學》之大數算數&Eratosthenes素數篩選

10進制大數算法,支持+、-、*、/、%以及一般的比較運算符,支持字符串方式讀入以及輸出大數

#pragma once
#include <iostream>
#include <string>
using namespace std;

#define SWAP(x,y,t) ((t)=(x),(x)=(y),(y)=(x))
#define MIN(x,y) (x)>(y)?(y):(x)
#define MAX(x,y) (x)>(y)?(x):(y)
#define BASE 10000       //大數以每位十進制數的方式存儲

//乘法實現,計算A*B,結果返回到 C,函數結果爲乘法結果的長度

#define MAX_SIZE 128
//一般將算術和關係操作符定義非成員函數,而將賦值操作符定義爲成員:
//大數以小端格式存儲
struct BigNum
{
	int len;
    	int v[MAX_SIZE];
	BigNum()
	{
    
		len=0;memset(v,0, sizeof(v));

	}
	BigNum(const char* num)
	{
		*this=num;
	}
	BigNum(int num)
	{
		len=0;
		for(; num>0; num/=BASE) 
			v[len++] = num%BASE;
	}
	BigNum&	operator=(const char* num)
	{
		int		l=strlen(num);
		int		n=(l%4)?(l%4):4;
		len=0;
		char	x[5]={0};
		memcpy(x,num, n );
		v[(l+4-1)/4-1]	= atoi(x);
		len++;
		for(int i=(l+4-1)/4-2,j=n; i>=0; i--,j+=4)
		{
			memcpy(x, num+j, 4);
			v[i]	= atoi(x);
			len++;
		}
		return *this;
	}
};

bool	operator<(const BigNum& left,const BigNum& right)
{
	int i;
	if(left.len!=right.len)	return left.len<right.len;
	for(i=left.len-1; i>=0 && left.v[i]==right.v[i]; i--)
		;
	return i>0? left.v[i]<right.v[i]: 0;
}

bool	operator>(const BigNum& left,const BigNum& right)
{
	return right<left;
}

bool	operator==(const BigNum& left,const BigNum& right)
{
	return (!(left<right)) && (!(right<left));
}

bool	operator<=(const BigNum& left,const BigNum& right)
{
	return (left<right) || (left==right);
}

bool	operator>=(const BigNum& left,const BigNum& right)
{
	return right<=left;
}

BigNum	operator+(const BigNum& left,const BigNum& right)
{
	int n = MAX(left.len,right.len);
	int x=0,i;
	BigNum	result;
	for(i=0; i<n; i++)
	{
		if(i<left.len)  x+=left.v[i];
		if(i<right.len) x+=right.v[i];
		result.v[i] = x%BASE;
		x/=BASE;
	}
	if(x!=0)	result.v[i++] = x;
	result.len	= i;
	return result;
}

BigNum	operator-(const BigNum& left,const BigNum& right)
{
	BigNum	result;
	int i,x=0;
	for(i=0; i<left.len; i++)
	{
		result.v[i] = left.v[i] - x;
		if(i<right.len) result.v[i] -= right.v[i];
		if(result.v[i]<0)	result.v[i] += BASE, x = 1;
		else	x=0;
	}
	result.len	= i;
	while(result.len>0&&result.v[result.len-1]==0)	result.len--;
	return result;
}

BigNum  operator*(const BigNum& left,const BigNum& right)
{
        BigNum  result;
         int             c0,c1,c2;//存數臨時結果
         int             tx,ty;
         int             len=left.len+right.len;
         int             *templ,*tempr;
        c0 = c1 = c2 = 0;
         for(int i=0; i<len; i++)//i爲本次計算在結果中所處的級別,這裏一個整形爲一個級別
        {
                ty              = MIN(i, right.len-1);
                tx              = i-ty;
                templ   = (int*)(left.v + tx);
                tempr   = (int*)(right.v+ ty);

                 int n = MIN( left.len-tx, ty+1);//本級別可計算的次數
                c0 = c1; c1 = c2; c2 = 0; //上次計算結果向下移一個級別,進入本次計算
                 for(int j=0; j<n; j++)
                {
                         int x=*templ++, y=*tempr--;
						 c0 += x*y;
            /*_asm   //mul operation
            {   
                                pushad
                mov eax,x
                mov ebx,y
                mul ebx          //無符號乘法指令
                add c0,eax
                //adc c1,edx
                //adc c2,0
                popad
                        }*/
                }
                result.v[i]=c0%BASE;
                c1+=c0/BASE;
                c2+=c1/BASE;
        }
         //計算長度
         for(int i=len-1; i >= 0; i--)
                 if (result.v[i]==0)
                        len--;
                 else
                         break;
        result.len      = len;
         return result;
}

BigNum	operator/(const BigNum& a,const BigNum& b)
{
	BigNum tmp, mod, res;
	int i, lf, rg, mid,base=BASE;
	mod.v[0] = mod.len = 0;
	for (i = a.len - 1; i >= 0; i--) 
	{
		//從最高4位開始,每次增加一個4位試商
		mod = mod * base + a.v[i];
		//二分法尋找mod/b的商
		for (lf = 0, rg = base -1; lf < rg; ) 
		{
			mid = (lf + rg + 1) / 2;
			if (b * mid <= mod) 
				lf = mid;
			else 
				rg = mid - 1;
		}
		//存儲結果並將剩餘的數繼續試商
		res.v[i] = lf;
		mod = mod - b * lf;
	}
	res.len = a.len;
	while (res.len > 0 && res.v[res.len - 1] == 0) res.len--;
	return res; // return mod 就是%運算
}

BigNum	operator%(const BigNum& a,const BigNum& b)
{
	BigNum tmp, mod, res;
	int i, lf, rg, mid,base=BASE;
	mod.v[0] = mod.len = 0;
	for (i = a.len - 1; i >= 0; i--) 
	{
		//從最高4位開始,每次增加一個4位試商
		mod = mod * base + a.v[i];
		//二分法尋找mod/b的商
		for (lf = 0, rg = base -1; lf < rg; ) 
		{
			mid = (lf + rg + 1) / 2;
			if (b * mid <= mod) 
				lf = mid;
			else 
				rg = mid - 1;
		}
		//存儲結果並將剩餘的數繼續試商
		res.v[i] = lf;
		mod = mod - b * lf;
	}
	res.len = a.len;
	while (res.len > 0 && res.v[res.len - 1] == 0) res.len--;
	return mod;// %運算
}

ostream& operator<<(ostream& os,const BigNum& num)
{
        printf( "%d",num.v[num.len-1]);
         for(int i=num.len-2; i>=0; i--)
                printf( "%04d",num.v[i]);
         return os;
}

istream& operator>>(istream& in,BigNum& num)
{
	string str;
	in>>str;
	num=str.c_str();
	return in;
}



X86版本的大數乘法簡陋實現,沒有書上的可移植性,結果以16進制存儲

只是爲了試試效果~

#include <stdio.h>

#define MIN(x,y) (x)>(y)?(y):(x)
/*#define MUL(x,y) _asm {\
                          "pushad\r\n"\
                          "mov eax,x\r\n" \
                          "mov ebx,y\r\n" \
                          "mul ebx\r\n"\
                          "add c0,eax\r\n" \
                          "adc c1,edx\r\n" \
                          "adc c2,0\r\n"\
                          "popad\r\n"\
                          }
*/
//乘法實現,計算A*B,結果返回到C,函數結果爲乘法結果的長度,大數整體採用小端模式存儲
int mul(int *A,int lena, int *B,int lenb, int *C)
{
   int    ix, iy, iz, tx, ty, pa;
   int    c0, c1, c2, *tmpx, *tmpy;

   c0=c1=c2=0;
   /* get size of output and trim */
   pa = lena+lenb;
  
   for (ix = 0; ix < pa; ix++) {
      /* get offsets into the two bignums */
      ty = MIN(ix, lenb-1);
      tx = ix - ty;

      /* setup temp aliases */
      tmpx = A + tx;
      tmpy = B + ty;

      /* this is the number of times the loop will iterrate, essentially its
         while (tx++ < a->used && ty-- >= 0) { ... }
       */
      iy = MIN(lena-tx, ty+1);

      /* execute loop */
      c0=c1;c1=c2;c2=0;
      for (iz = 0; iz < iy; ++iz) {
          int i=*tmpx++,j= *tmpy--;
                  _asm   //mul operation
                  {
                          pushad
                          mov eax,i
                          mov ebx,j
                          mul ebx
                          add c0,eax
                          adc c1,edx
                          adc c2,0
                          popad
                  }
      }

      /* store term */
     C[ix]=c0;
  }
         return pa;
}

//平方計算,函數結果爲乘法結果的長度
int square(int *A,int lena, int *C)
{
   int    ix, iy, iz, tx, ty, pa;
   int    c0, c1, c2, *tmpx, *tmpy;

   c0=c1=c2=0;
   /* get size of output and trim */
   pa = lena*2;
 
   for (ix = 0; ix < pa; ix++) {
      /* get offsets into the two bignums */
      ty = MIN(ix, lena-1);
      tx = ix - ty;

      /* setup temp aliases */
      tmpx = A + tx;
      tmpy = A + ty;

      /* this is the number of times the loop will iterrate, essentially its
         while (tx++ < a->used && ty-- >= 0) { ... }
       */
      iy = MIN(lena-tx, ty+1);
          iy = MIN(iy, (ty-tx+1)>>1);

      /* execute loop */
      c0=c1;c1=c2;c2=0;
      for (iz = 0; iz < iy; ++iz) {
          int i=*tmpx++,j= *tmpy--;
                  _asm   // 由於是平方,加兩次,省去一半的乘法指令
                  {
                          pushad
                          mov eax,i
                          mov ebx,j
                          mul ebx
                          add c0,eax
                          adc c1,edx
                          adc c2,0
                                                  add c0,eax
                                                  adc c1,edx
                                                  adc c2,0
                          popad
                  }
      }

          if ((ix&1)==0)  // 偶數位的話需要加上中間未重複的結果
          {
          int i=A[ix>>1],j= A[ix>>1];
          _asm   //mul operation
          {           
                          pushad
                      mov eax,i     
                          mov ebx,j   
                      mul ebx
              add c0,eax
              adc c1,edx
              adc c2,0
              popad
          }
          }

      /* store term */
     C[ix]=c0;
  }
         return pa;
}

//求冪,lena:A的長度(4字節爲單位),exponent:指數,C:存儲結果
//這個版本的求冪只能求2^n形式的冪
int power(int *A,int lena, int exponent, int *C)
{
         int len=lena,*temp,*dst=C;
         int squ=0;// 可直接平方的次數
         for(int t=exponent; t!=1; t>>=1)
                squ++;
         for(int i=0; i < squ; i++)
        {
                memset(C, 0 , 1024);
                len = square(A,len, C);
                SWAP(A,C,temp);
        }
         if (dst!=A) memcpy(dst,A,len);
         return len;
}

void print_bignum(int *bignum,int u)
{
         int first=1;
        printf( "0x");
         for(int i=u-1; i >= 0; i--)
                 if ((bignum[i]==0)&&first)
                         continue;
                 else
                {
                        first = 0;
                        printf( "%08X",bignum[i]);
                }
}

int main()
{
         int a[] = {0x87654321,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,\
                0x87654321,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,\
                0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678};

         int b[] = {0x87654321,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,\
                0x87654321,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,\
                0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678,0x12345678};

         int c[256]={0};
         int len=mul(a,sizeof (a)/sizeof( int),b,sizeof (b)/sizeof( int), c);
        print_bignum(c,len);
        getchar();
         return 0;
}

 


 
 
素數篩選
#include <iostream>
#include <cmath>
#include <vector>

using namespace std;
//查找指定範圍內的所有素數
//100以內的所有素數
unsigned int prime_tab[] = {
         2,  3,  5,  7,  11,  13,  17,
        19, 23, 29, 31,  37,  41,  43,
        47, 53, 59, 61,  67,  71,  73,
        79, 83, 89, 97
};

//Eratosthenes 素數篩選,取得0-num內所有的素數,c++容器版
int Eratosthenes(vector<int >& tab, unsigned int num)
{
#define TEST(p,x) (((p)%(x))!=0)       //是否滿足素數條件
        
         if(num <= 100)
        {
                 for(int i=0; i < sizeof(prime_tab)/ sizeof(int ); i++)
                {
                         if(prime_tab[i]<num)
                                tab.push_back(prime_tab[i]);
                         else
                                 break;
                }
                 return tab.size();
        }

         unsigned int i=(int)sqrt(( double)num);
        Eratosthenes(tab, i); /*遞歸初始化i以內的素數表 */
         for(; i < num; i++)// 篩選
        {
                 bool flag=false ;
                 int  n=tab.size();
                 for(vector<int >::iterator iter=tab.begin(),end=iter+n; iter != end ; iter++)
                {
                         if( !TEST(i,*iter) )
                                 break;
                         if( (iter+1)==end )
                                flag= true;
                }
                 if(flag) tab.push_back(i);
        }

#undef TEST(p,x)

         return tab.size();
}

//Eratosthenes 素數篩選,取得 num內所有的素數
int Eratosthenes2(unsigned int num)
{
         unsigned int count=0;
        
         char *tab = new char[num+1];
        memset(tab+2,1, num-2);

         for(unsigned int i=2; i <= ( unsigned int )sqrt((double)num); i++)
                 if(tab[i]==1)
                         for(unsigned int j=i; j*i <= num; j++)
                                tab[i*j] = 0;
         for(unsigned int i=2; i <= num ; i++)
                 if(tab[i]==1)
                {
                        printf( "%d\t",i);
                        count++;
                }

        printf( "\n%d  ",count);

         delete [] tab;
         return count;
}

int main()
{
        vector< int> tab;        //素數表容器
        tab.reserve(2000);       //預留空間
        cout<<Eratosthenes(tab,150000); //函數返回統計個數
         for(int i=0; i < tab.size(); i++)  //輸出素數
                printf( "%d\t", tab[i]);
        getchar();
         return 0;
}



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章