如何在一定范围内生成随机BigInteger?

时间:2013-06-28 05:25:32

标签: c# .net

考虑这种效果很好的方法:

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

现在,为了满足我正在上课的要求,mightBePrime必须接受BigInteger N,但这意味着我需要一种不同的方式来生成我的随机BigInteger a。

我的第一个想法是做BigInteger a = (N-1) * rGen.NextDouble ()之类的事情,但BigInteger不能乘以双倍。

如何在1和N-1之间生成随机BigInteger,其中N是BigInteger?

7 个答案:

答案 0 :(得分:8)

保罗在评论中建议我使用随机字节生成一个数字,如果它太大则扔掉它。这就是我想出的结果(马塞尔的回答+保罗的建议):

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/也有所帮助。

答案 1 :(得分:6)

使用Random-Class

public BigInteger getRandom(int length){
    Random random = new Random();
    byte[] data = new byte[length];
    random.NextBytes(data);
    return new BigInteger(data);
}

答案 2 :(得分:3)

在找到指定范围内的有效BigInteger之前,天真的实现平均会失败64次。

在最糟糕的情况下,我的实施将平均只重试0.5次(读作:第一次尝试时会找到结果的50%)。

此外,与模块化算术不同,我的实现保持均匀分布

解释

我们必须在BigIntegermin之间生成随机max

  1. 如果min > max,我们会将minmax
  2. 交换
  3. 为了简化实施,我们将范围从[min, max]转移到[0, max-min],这样我们就不必处​​理符号位了
  4. 我们计算max包含(bytes.Length
  5. 的字节数
  6. 从最重要的位开始,我们计算有多少位为0(zeroBits
  7. 我们生成bytes.Length字节
  8. 的随机序列
  9. 我们知道,对于我们的序列< max最高位中的至少zeroBits位必须为0,因此我们使用zeroBitMask通过最高有效字节上的单个位到&操作来设置它们,这将通过减少生成超出范围的数字的变化来节省大量时间< / LI>
  10. 我们检查我们生成的号码是> max,如果是,我们再试一次
  11. 我们通过在结果中添加[0, max-min],将范围从[min, max]移至min
  12. 我们有号码。

    实施

    public static BigInteger RandomInRange(RandomNumberGenerator rng, BigInteger min, BigInteger max)
    {
        if (min > max)
        {
            var buff = min;
            min = max;
            max = buff;
        }
    
        // offset to set min = 0
        BigInteger offset = -min;
        min = 0;
        max += offset;
    
        var value = randomInRangeFromZeroToPositive(rng, max) - offset;
        return value;
    }
    
    private static BigInteger randomInRangeFromZeroToPositive(RandomNumberGenerator rng, BigInteger max)
    {
        BigInteger value;
        var bytes = max.ToByteArray();
    
        // count how many bits of the most significant byte are 0
        // NOTE: sign bit is always 0 because `max` must always be positive
        byte zeroBitsMask = 0b00000000;
    
        var mostSignificantByte = bytes[bytes.Length - 1];
    
        // we try to set to 0 as many bits as there are in the most significant byte, starting from the left (most significant bits first)
        // NOTE: `i` starts from 7 because the sign bit is always 0
        for (var i = 7; i >= 0; i--)
        {
            // we keep iterating until we find the most significant non-0 bit
            if ((mostSignificantByte & (0b1 << i)) != 0)
            {
                var zeroBits = 7 - i;
                zeroBitsMask = (byte)(0b11111111 >> zeroBits);
                break;
            }
        }
    
        do
        {
            rng.GetBytes(bytes);
    
            // set most significant bits to 0 (because `value > max` if any of these bits is 1)
            bytes[bytes.Length - 1] &= zeroBitsMask;
    
            value = new BigInteger(bytes);
    
            // `value > max` 50% of the times, in which case the fastest way to keep the distribution uniform is to try again
        } while (value > max);
    
        return value;
    }
    

    测试

    using (var rng = RandomNumberGenerator.Create())
    {
        BigInteger min = 0;
        BigInteger max = 5;
    
        var attempts = 10000000;
        var count = new int[(int)max + 1];
    
        var sw = Stopwatch.StartNew();
    
        for (var i = 0; i < attempts; i++)
        {
            var v = BigIntegerUtils.RandomInRange(rng, min, max);
            count[(int)v]++;
        }
    
        var time = sw.Elapsed;
        Console.WriteLine("Generated {0} big integers from {1} to {2} in {3}", attempts, min, max, time);
        Console.WriteLine("On average: {0} ms/integer or {1} integers/second", time.TotalMilliseconds / attempts, attempts / time.TotalSeconds);
    
        for (var i = 0; i <= max; i++)
            Console.WriteLine("{0} generated {1}% of the times ({2} times)", i, count[i] * 100d / attempts, count[i]);
    }
    

    我的i7-6500U上的测试输出:

    Generated 10000000 big integers from 0 to 5 in 00:00:09.5413677
    On average: 0.00095413677 ms/integer or 1048067.77334449 integers/second
    0 generated 16.66633% of the times (1666633 times)
    1 generated 16.6717% of the times (1667170 times)
    2 generated 16.66373% of the times (1666373 times)
    3 generated 16.6666% of the times (1666660 times)
    4 generated 16.68271% of the times (1668271 times)
    5 generated 16.64893% of the times (1664893 times)
    

    我的i7-6500U上的另一个测试输出

    Generated 10000000 big integers from 0 to 10^100 in 00:00:17.5036570
    On average: 0.0017503657 ms/integer or 571309.184132207 integers/second
    

答案 3 :(得分:0)

创建字节数组并转换为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);
}

如果maxValue太小,则random会生成相同的值。 所以你可以在函数外面设置随机:

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...
}

答案 4 :(得分:0)

这是另一种在范围内生成数字而不会丢弃值并允许BigIntegers达到最小值和最大值的方法。

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;
    }

一般而言,您可能还需要指定精度。这似乎有效。

    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;
    } 

答案 5 :(得分:0)

这是 NextBigInteger 类的 Random 扩展方法。它基于优秀的 Fabio Iotti 的 implementation,为了简洁而修改。

/// <summary>
/// Returns a random BigInteger that is within a specified range.
/// The lower bound is inclusive, and the upper bound is exclusive.
/// </summary>
public static BigInteger NextBigInteger(this Random random,
    BigInteger minValue, BigInteger maxValue)
{
    if (minValue > maxValue) throw new ArgumentException();
    if (minValue == maxValue) return minValue;
    BigInteger zeroBasedUpperBound = maxValue - 1 - minValue; // Inclusive
    Debug.Assert(zeroBasedUpperBound.Sign >= 0);
    byte[] bytes = zeroBasedUpperBound.ToByteArray();
    Debug.Assert(bytes.Length > 0);
    Debug.Assert((bytes[bytes.Length - 1] & 0b10000000) == 0);

    // Search for the most significant non-zero bit
    byte lastByteMask = 0b11111111;
    for (byte mask = 0b10000000; mask > 0; mask >>= 1, lastByteMask >>= 1)
    {
        if ((bytes[bytes.Length - 1] & mask) == mask) break; // We found it
    }

    while (true)
    {
        random.NextBytes(bytes);
        bytes[bytes.Length - 1] &= lastByteMask;
        var result = new BigInteger(bytes);
        Debug.Assert(result.Sign >= 0);
        if (result <= zeroBasedUpperBound) return result + minValue;
    }
}

为了返回理想范围内的值而丢弃的 BigInteger 个实例的百分比平均为 30%(最佳情况 0%,最坏情况 50%)。

随机数的分布是均匀的。

用法示例:

Random random = new();
BigInteger value = random.NextBigInteger(BigInteger.Zero, new BigInteger(1000));

注意: BigInteger.ToByteArray is well documented 返回的字节结构(在备注部分),所以应该是相当安全的假设 BigIntegerbyte[] 表示在 .NET 平台的未来版本中不会改变。如果发生这种情况,上面的 NextBigInteger 实现可能会以令人讨厌的方式失败,例如进入无限循环或生成错误范围内的数字。我添加了一些调试断言,这些断言永远不会因当前表示而失败,但对无效条件检查的覆盖范围绝不是彻底的。

答案 6 :(得分:-1)

以下Range方法将在您指定的范围内返回IEnumerable<BigInteger>。 一个简单的扩展方法将在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);
    }
}

<强>用法:

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

//返回随机值,在本例中为10000000000000003