Which is a good C++ BigInteger class for programming contests?

前端 未结 1 1305
隐瞒了意图╮
隐瞒了意图╮ 2021-01-20 13:27

I was just wondering which will the best BigInteger class in C++ for programming contests which do not allow external libraries?

Mainly I was looking for a class whi

相关标签:
1条回答
  • 2021-01-20 13:39

    So far I've only needed unsigned integer big numbers for codechef, but codechef only gives 2KB, so I don't have the full implementation up there anywhere, just the members needed for that problem. My code also assumes that long long has at least twice as many bits as a unsigned, though that's pretty safe. The only real trick to it is that different biguint classes may have different data lengths. Here's summaries of the more interesting functions.

    #define BIG_LEN()  (data.size()>rhs.data.size()?data.size():rhs.data.size())
        //the length of data or rhs.data, whichever is bigger
    #define SML_LEN()  (data.size()<rhs.data.size()?data.size():rhs.data.size())
        //the length of data or rhs.data, whichever is smaller
    const unsigned char baselut[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
                                       0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
                                      25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
                                      41,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
                                      25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40
                                      };
    const unsigned char base64lut[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,62, 0, 0, 0,63,
                                        52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
                                         0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
                                        15,16,17,18,19,20,21,22,23,24,25, 0, 0, 0, 0, 0,
                                         0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
                                        41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0
                                        };
        //lookup tables for creating from strings
    
    void add(unsigned int amount, unsigned int index)
        adds amount at index with carry, simplifies other members
    void sub(unsigned int amount, unsigned int index)
        subtracts amount at index with borrow, simplifies other members
    biguint& operator+=(const biguint& rhs)
        resize data to BIG_LEN()
        int carry = 0
        for each element i in data up to SML_LEN()
            data[i] += rhs.data[i] + carry
            carry = ((data[i]<rhs[i]+carry || (carry && rhs[i]+carry==0)) ? 1u : 0u);
       if data.length > rhs.length
           add(carry, SML_LEN())
    biguint& operator*=(const biguint& rhs) 
        biguint lhs = *this;
        resize data to data.length + rhs.length
        zero out data
        for each element j in lhs
            long long t = lhs[j]
            for each element i in rhs (and j+i<data.size)
                t*=rhs[i];
                add(t&UINT_MAX, k);
                if (k+1<data.size())
                    add(t>>uint_bits, k+1);
    //note this was public, so I could do both at the same time when needed
    //operator /= and %= both just call this
    //I have never needed to divide by a biguint yet.
    biguint& div(unsigned int rhs, unsigned int & mod) 
        long long carry = 0
        for each element i from data length to zero
            carry = (carry << uint_bits) | data[i]
            data[i] = carry/rhs;
            carry %= rhs
        mod = carry
    //I have never needed to shift by a biguint yet
    biguint& operator<<=(unsigned int rhs) 
        resize to have enough room, always at least 1 bigger
        const unsigned int bigshift = rhs/uint_bits;
        const unsigned int lilshift = rhs%uint_bits;
        const unsigned int carry_shift = (uint_bits-lilshift)%32;
        for each element i from bigshift to zero
             t = data[i-bigshift] << lilshift;
             t |= data[i-bigshift-1] >> carry_shift;
             data[i] = t;
        if bigshift < data.size
            data[bigshift] = data[0] << lilshift
        zero each element i from 0 to bigshift
    std::ofstream& operator<<(std::ofstream& out, biguint num)
        unsigned int mod
        vector reverse
        do 
            num.div(10,mod);
            push back mod onto reverse
        while num greater than 0
        print out elements of reverse in reverse
    std::ifstream& operator>>(std::ifstream& in, biguint num)
        char next
        do
            in.get(next) 
        while next is whitespace
        num = 0
        do 
            num = num * 10 + next
        while in.get(next) and next is digit
    //these are handy for initializing to known values.
    //I also have constructors that simply call these
    biguint& assign(const char* rhs, unsigned int base)
        for each char c in rhs
            data *= base
            add(baselut[c], 0)
    biguint& assign(const char* rhs, std::integral_constant<unsigned int, 64> base)
        for each char c in rhs
            data *= base
            add(base64lut[c], 0)
    //despite being 3 times as much, code, the hex version is _way_ faster.
    biguint& assign(const char* rhs, std::integral_constant<unsigned int, 16>) 
        if first two characters are "0x" skip them
        unsigned int len = strlen(rhs);
        grow(len/4+1);
        zero out data
        const unsigned int hex_per_int = uint_bits/4;
        if (len > hex_per_int*data.size()) { //calculate where first digit goes
            rhs += len-hex_per_int*data.size();
            len = hex_per_int*data.size();
        }
        for(unsigned int i=len; i --> 0; ) { //place each digit directly in it's place
            unsigned int t = (unsigned int)(baselut[*(rhs++)]) << (i%hex_per_int)*4u;
            data[i/hex_per_int] |= t;
        }
    

    I also made specializations for multiplication, divide, modulo, shifts and others for std::integral_constant<unsigned int, Value>, which made massive improvements to my serializing and deserializing functions amongst others.

    0 讨论(0)
提交回复
热议问题