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;
}
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.
We must generate a random BigInteger
between min
and max
.
min > max
, we swap min
with max
[min, max]
to [0, max-min]
, this way we won't have to deal with the sign bitmax
contains (bytes.Length
)zeroBits
)bytes.Length
bytes< 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> max
, and if so we try again[0, max-min]
to [min, max]
by adding min
to our resultAnd we have our number.
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...
}
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.
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
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;
}
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);
}