How can I generate a random BigInteger within a certain range?

后端 未结 6 2411
春和景丽
春和景丽 2021-02-13 06:52

Consider this method that works well:

public static bool mightBePrime(int N) {
    BigInteger a = rGen.Next (1, N-1);
    return modExp (a, N - 1, N) == 1;
}


        
相关标签:
6条回答
  • 2021-02-13 07:24

    The naive implementation will fail on average 64 times before finding a valid BigInteger within the specified range.

    On the worst case, my implementation will retry on average only 0.5 times (read as: 50% of the times it will find a result on the first try).

    Also, unlike with modular arithmetic, my implementation maintains a uniform distribution.

    Explanation

    We must generate a random BigInteger between min and max.

    1. If min > max, we swap min with max
    2. To simplify the implementation we shift our range from [min, max] to [0, max-min], this way we won't have to deal with the sign bit
    3. We count how many bytes max contains (bytes.Length)
    4. From the most significant bit, we count how many bits are 0 (zeroBits)
    5. We generate a random sequence of bytes.Length bytes
    6. We know that for our sequence to be < max, at least zeroBits bits from the most significant bit must be 0, so we use a zeroBitMask to set them with a single bit-to-bit & operation over the most significant byte, this will save a lot of time by reducing the change of generating a number out of our range
    7. We check if the number we generated is > max, and if so we try again
    8. We unshift the range back from [0, max-min] to [min, max] by adding min to our result

    And we have our number.

    0 讨论(0)
  • 2021-02-13 07:25

    Create byte array and convert to BigInteger:

    public BigInteger random_generate(BigInteger maxValue)
    {
        Random random = new Random();
        byte[] maxValue_array = maxValue.ToByteArray();
        byte[] randomValue_array = new byte[maxValue_array.Count()];
        bool on_limit = true; //make sure randomValue won't greater than maxValue
        for (int generate_byte = maxValue_array.Count() - 1; generate_byte >= 0; generate_byte--)
        {
            byte random_byte = 0;
            if (on_limit)
            {
                random_byte = (byte)random.Next(maxValue_array[generate_byte]);
                if (random_byte != (byte)random.Next(maxValue_array[generate_byte]))
                {
                    on_limit = false;
                }
            }
            else
            {
                random_byte = (byte)random.Next(256);
            }
            randomValue_array[generate_byte] = random_byte;
        }
        return new BigInteger(randomValue_array);
    }
    

    If the maxValue is too small, than the random will generate the same value. So you can set the random outside the function:

    static void Main(string[] args)
    {
        Random random = new Random();
        BigInteger i = random_generate(10, random); //10 is just a example
    }
    
    public BigInteger random_generate(BigInteger maxValue, Random random)
    {
        byte[] maxValue_array = maxValue.ToByteArray();
        //...rest of the code...
    }
    
    0 讨论(0)
  • 2021-02-13 07:31

    Paul suggested in a comment that I generate a number using random bytes, then throw it away if it's too big. Here's what I came up with (Marcel's answer + Paul's advice):

    public static BigInteger RandomIntegerBelow(BigInteger N) {
        byte[] bytes = N.ToByteArray ();
        BigInteger R;
    
        do {
            random.NextBytes (bytes);
            bytes [bytes.Length - 1] &= (byte)0x7F; //force sign bit to positive
            R = new BigInteger (bytes);
        } while (R >= N);
    
        return R;
    }
    

    http://amirshenouda.wordpress.com/2012/06/29/implementing-rsa-c/ helped a little too.

    0 讨论(0)
  • 2021-02-13 07:35

    The following Range method will return an IEnumerable<BigInteger> within the range you specify. A simple Extension method will return a random element within the IEnumerable.

    public static IEnumerable<BigInteger> Range(BigInteger from, BigInteger to)
    {
        for(BigInteger i = from; i < to; i++) yield return i;
    }
    
    public static class Extensions
    {
        public static BigInteger RandomElement(this IEnumerable<BigInteger> enumerable, Random rand)
        {
            int index = rand.Next(0, enumerable.Count());
            return enumerable.ElementAt(index);
        }
    }
    

    usage:

    Random rnd = new Random();
    var big = Range(new BigInteger(10000000000000000), new BigInteger(10000000000000020)).RandomElement(rnd);
    

    // returns random values and in this case it was 10000000000000003

    0 讨论(0)
  • 2021-02-13 07:36

    Here's an alternate way to generate numbers within range without throwing away values and allowing BigIntegers for min and max.

    public BigInteger RandomBigInteger(BigInteger min, BigInteger max)
        {
            Random rnd = new Random();
            string numeratorString, denominatorString;
            double fraction = rnd.NextDouble();
            BigInteger inRange;
    
            //Maintain all 17 digits of precision, 
            //but remove the leading zero and the decimal point;
            numeratorString = fraction.ToString("G17").Remove(0, 2);  
    
            //Use the length instead of 17 in case the random
            //fraction ends with one or more zeros
            denominatorString = string.Format("1E{0}", numeratorString.Length); 
    
            inRange = (max - min) * BigInteger.Parse(numeratorString) /
               BigInteger.Parse(denominatorString, 
               System.Globalization.NumberStyles.AllowExponent) 
               + min;
            return inRange;
        }
    

    For generality you may want to specify precision as well. This seems to work.

        public BigInteger RandomBigIntegerInRange(BigInteger min, BigInteger max, int precision)
        {
            Random rnd = new Random();
            string numeratorString, denominatorString;
            double fraction = rnd.NextDouble();
            BigInteger inRange;
    
            numeratorString = GenerateNumeratorWithSpecifiedPrecision(precision);
            denominatorString = string.Format("1E{0}", numeratorString.Length); 
    
            inRange = (max - min) * BigInteger.Parse(numeratorString) / BigInteger.Parse(denominatorString, System.Globalization.NumberStyles.AllowExponent) + min;
            return inRange;
        }
    
        private string GenerateNumeratorWithSpecifiedPrecision(int precision)
        {
            Random rnd = new Random();
            string answer = string.Empty;
    
            while(answer.Length < precision)
            {
                answer += rnd.NextDouble().ToString("G17").Remove(0, 2);                
            }
            if (answer.Length > precision) //Most likely
            {
                answer = answer.Substring(0, precision);
            }
            return answer;
        } 
    
    0 讨论(0)
  • 2021-02-13 07:45

    Use the Random-Class

    public BigInteger getRandom(int length){
        Random random = new Random();
        byte[] data = new byte[length];
        random.NextBytes(data);
        return new BigInteger(data);
    }
    
    0 讨论(0)
提交回复
热议问题