How to efficiently convert a few bytes into an integer between a range?

后端 未结 4 2133
离开以前
离开以前 2021-01-19 17:45

I\'m writing something that reads bytes (just a List) from a remote random number generation source that is extremely slow. For that and my personal

4条回答
  •  情歌与酒
    2021-01-19 18:02

    Your random source gives you 8 random bits per call. For an integer in the range [min,max] you would need ceil(log2(max-min+1)) bits.

    Assume that you can get random bytes from the source using some function:

    bool RandomBuf(BYTE* pBuf , size_t nLen); // fill buffer with nLen random bytes
    

    Now you can use the following function to generate a random value in a given range:

    // --------------------------------------------------------------------------
    // produce a uniformly-distributed integral value in range [nMin, nMax]
    // T is char/BYTE/short/WORD/int/UINT/LONGLONG/ULONGLONG
    template  T RandU(T nMin, T nMax)
    {
        static_assert(std::numeric_limits::is_integer, "RandU: integral type expected");
    
        if (nMin>nMax)
            std::swap(nMin, nMax);
    
        if (0 == (T)(nMax-nMin+1)) // all range of type T
        {
            T nR;
            return RandomBuf((BYTE*)&nR, sizeof(T)) ? *(T*)&nR : nMin;
        }
    
        ULONGLONG nRange    = (ULONGLONG)nMax-(ULONGLONG)nMin+1        ; // number of discrete values
        UINT      nRangeBits= (UINT)ceil(log((double)nRange) / log(2.)); // bits for storing nRange discrete values
        ULONGLONG nR                                                   ;
    
        do
        {
            if (!RandomBuf((BYTE*)&nR, sizeof(nR)))
                return nMin;
    
            nR= nR>>((sizeof(nR)<<3) - nRangeBits); // keep nRangeBits random bits
        }
        while (nR >= nRange);                       // ensure value in range [0..nRange-1]
    
        return nMin + (T)nR;                        // [nMin..nMax]
    }
    

    Since you are always getting a multiple of 8 bits, you can save extra bits between calls (for example you may need only 9 bits out of 16 bits). It requires some bit-manipulations, and it is up to you do decide if it is worth the effort.

    You can save even more, if you'll use 'half bits': Let's assume that you want to generate numbers in the range [1..5]. You'll need log2(5)=2.32 bits for each random value. Using 32 random bits you can actually generate floor(32/2.32)= 13 random values in this range, though it requires some additional effort.

提交回复
热议问题