From 1dffae442cb35ab9daab39d33dbd5724d748aac3 Mon Sep 17 00:00:00 2001 From: tomsmeding Date: Thu, 13 Oct 2016 23:39:10 +0200 Subject: divmod: recursive -> iterative --- bigint.cpp | 160 +++++++++++++++++++++++++++++++++++------------------------- bigint.h | 2 +- numalgo.cpp | 1 + 3 files changed, 95 insertions(+), 68 deletions(-) diff --git a/bigint.cpp b/bigint.cpp index 7c9fec7..e57752f 100644 --- a/bigint.cpp +++ b/bigint.cpp @@ -177,9 +177,9 @@ Bigint& Bigint::operator=(slongdigit_t v){ return *this; } sign=v>=0?1:-1; - v*=sign; - digits[0]=v; - if(v>digits[0])digits.push_back(v>>digit_bits); + longdigit_t uv=sign*v; //unsigned version of v + digits.push_back(uv); + if(uv>digits[0])digits.push_back(uv>>digit_bits); checkconsistent(); return *this; } @@ -344,7 +344,7 @@ Bigint Bigint::operator>>(int sh) const { pair Bigint::divmod(const Bigint &div) const { int bitcdiff=bitcount()-div.bitcount(); if(bitcdiff<0)bitcdiff=0; - pair p=divmod(div,1,bitcdiff/29+10); //ignores all signs + pair p=divmod(*this,div,bitcdiff/29+10); //ignores all signs /* To let the result come out correctly, we apply case analysis to the signs of the arguments. * As a guiding example, these two cases can be examined. * The code was tested with many large random numbers (also negative), using a Python script. @@ -381,71 +381,97 @@ pair Bigint::divmod(const Bigint &div) const { //ignores all signs, and always returns positive numbers! //This function is opaque and way more complicated than it should be. Sorry for that. -//Strategy: find a quotient (and guess=quotient*div) such that guess<=*this, but *this-guess -//is small. Then subtract guess from *this, and repeat. -pair Bigint::divmod(const Bigint &div,int depth,int maxdepth) const { - if(depth>maxdepth)assert(false); //Sanity check +//Strategy: find a quotient (and guess=quotient*div) such that guess<=a, but a-guess +//is small. Then subtract guess from a, and repeat. +pair Bigint::divmod(Bigint a,const Bigint &div,const int maxiter){ if(div.digits.size()==0)throw domain_error("Bigint divide by zero"); - if(digits.size()==0)return make_pair(Bigint::zero,Bigint::zero); - - int cmp=compareAbs(div); - if(cmp==0)return make_pair(Bigint::one,Bigint::zero); - if(cmp<0){ - pair p(make_pair(Bigint::zero,*this)); - p.second.sign=1; - return p; - } - //now *this is greater in magnitude than the divisor - - int thisbtc=bitcount(),divbtc=div.bitcount(); - assert(divbtc<=thisbtc); - Bigint quotient,guess; - if(thisbtc<=2*digit_bits){ - //simple integral division - longdigit_t thisnum=(digits.size()==2?((longdigit_t)1<=digit_bits){ //both *this and div are large - //take 2*digit_bits of *this and 1*digit_bits of div; quotient gives a good guess - int spill=__builtin_clz(digits.back()); - longdigit_t thishead2=((longdigit_t)digits.back()<<(spill+digit_bits))|((longdigit_t)digits[digits.size()-2]<0)thishead2|=digits[digits.size()-3]>>(digit_bits-spill); - longdigit_t divhead=((longdigit_t)div.digits.back()<>=digit_bits-__builtin_clz(div.digits.back()); - longdigit_t factor=thishead2/(divhead+1); //+1 to make sure the quotient guess is <= the actual quotient - quotient=factor; - quotient<<=thisbtc-digit_bits-divbtc; //shift amount may be negative if thisbtc and divbtc are less than digit_bits apart - if(quotient==0)quotient=1; //prevents against (HUGE+1)/HUGE where HUGE==HUGE - guess=quotient*div; - guess.sign=1; - } else { //divbtc0)thishead2|=digits[digits.size()-3]>>(digit_bits-spill); - longdigit_t factor=thishead2/div.digits[0]; - quotient=factor; - quotient<<=thisbtc-2*digit_bits; - guess=quotient*div; - guess.sign=1; - } - - //Now actually subtract out our guess - cmp=guess.compareAbs(*this); - assert(cmp<=0); //we specifically made our guessed quotient to be <= the actual quotient - if(cmp<0){ - Bigint rest(*this); - rest.sign=1; - rest-=guess; //also correct for *this and guess negative - pair dm=rest.divmod(div,depth+1,maxdepth); - dm.first+=quotient; - return dm; - } else { //then cmp==0 - return make_pair(quotient,Bigint::zero); + + pair result; + + a.sign=1; + + for(int iter=0;iter=digit_bits){ //both a and div are large + //We're going to take 2*digit_bits of a and 1*digit_bits of div + int spill=__builtin_clz(a.digits.back()); //number of zero bits on top of a + longdigit_t ahead2= //top 64 bits of a (not yet) + ((longdigit_t)a.digits.back()<<(spill+digit_bits))| + ((longdigit_t)a.digits[a.digits.size()-2]<0){ //if there was some spill, we need to pull in a bit of the third digit + ahead2|=a.digits[a.digits.size()-3]>>(digit_bits-spill); + } + //now ahead is top 64 bits of a + + longdigit_t divhead= //top 2 digits of divisor + ((longdigit_t)div.digits.back()<>=digit_bits-__builtin_clz(div.digits.back()); //shift out some bits such that divhead is top 32 bits of div + + longdigit_t factor=ahead2/(divhead+1); //+1 to make sure the quotient guess is <= the actual quotient + quotient=factor; + quotient<<=abtc-digit_bits-divbtc; //shift amount may be negative if abtc and divbtc are less than digit_bits apart + if(quotient==0)quotient=1; //prevents against (HUGE+1)/HUGE where HUGE==HUGE + guess=quotient*div; + guess.sign=1; //for if div is negative + } else { //divbtc0){ + ahead2|=a.digits[a.digits.size()-3]>>(digit_bits-spill); + } + + longdigit_t factor=ahead2/div.digits[0]; + quotient=factor; + quotient<<=abtc-2*digit_bits; + guess=quotient*div; + guess.sign=1; + } + + //Now actually subtract out our guess + a-=guess; + result.first+=quotient; + if(a==0){ + result.second=0; + break; + } } + + return result; } bool Bigint::operator==(const Bigint &o) const {return compare(o)==0;} diff --git a/bigint.h b/bigint.h index cff7bd4..c66dcf6 100644 --- a/bigint.h +++ b/bigint.h @@ -32,7 +32,7 @@ private: //basically a debug check, but it can't hurt to have it since it takes few cpu cycles. void checkconsistent(); - std::pair divmod(const Bigint&,int depth,int maxdepth) const; //ignores all signs + static std::pair divmod(Bigint a,const Bigint &div,int maxdepth); //ignores all signs public: Bigint(); diff --git a/numalgo.cpp b/numalgo.cpp index ee77afc..a8f2321 100644 --- a/numalgo.cpp +++ b/numalgo.cpp @@ -40,6 +40,7 @@ Bigint expmod(const Bigint &b,const Bigint &e,const Bigint &m){ assert(e>=0); assert(m>=1); if(m==1)return Bigint::zero; + if(e==0)return b; Bigint res(1); vector bits(e.bits()); for(int i=bits.size()-1;i>=0;i--){ -- cgit v1.2.3