Considérez cette méthode qui fonctionne bien:
public static bool mightBePrime(int N) { BigInteger a = rGen.Next (1, N-1); return modExp (a, N - 1, N) == 1; }
Maintenant, afin de satisfaire à une exigence de la classe que je suis en train de suivre, mightBePrime
doit accepter un BigInteger
N, mais cela signifie que j’ai besoin d’une méthode différente pour générer mon BigInteger aléatoire a.
Ma première idée était de faire quelque chose comme BigInteger a = (N-1) * rGen.NextDouble ()
, mais un BigInteger ne peut pas être multiplié par un double.
Comment générer un BigInteger aléatoire compris entre 1 et N-1 où N est un BigInteger?
Paul a suggéré dans un commentaire que je génère un nombre en utilisant des octets aléatoires, puis que je le jette s’il est trop gros. Voici ce que j’ai trouvé (réponse de Marcel + conseil de Paul):
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/ a également aidé un peu.
Utilisez la classe aléatoire
public BigInteger getRandom(int length){ Random random = new Random(); byte[] data = new byte[length]; random.NextBytes(data); return new BigInteger(data); }
L’implémentation naïve échouera en moyenne 64 fois avant de trouver un BigInteger
valide dans la plage spécifiée.
Dans le pire des cas, mon implémentation ne réessayera en moyenne que 0,5 fois (comme suit: 50% des fois, elle trouvera un résultat du premier coup).
De plus, contrairement à l’arithmétique modulaire, mon implémentation maintient une dissortingbution uniforme .
Nous devons générer un BigInteger
aléatoire entre min
et max
.
min > max
, nous échangeons min
avec max
[min, max]
à [0, max-min]
. Ainsi, nous n’aurons pas à traiter le bit de signe. max
contient ( bytes.Length
) zeroBits
) bytes.Length
octets < max
, au moins zeroBits
bit du bit le plus significatif doit être 0, nous utilisons donc un zeroBitMask
pour les définir avec une seule opération bit à bit &
opération sur l' octet le plus significatif . gagner beaucoup de temps en réduisant le changement de génération d'un nombre hors de notre gamme > max
, et si nous essayons à nouveau [0, max-min]
à [min, max]
en ajoutant min
à notre résultat Et nous avons notre numéro. 😊
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 dissortingbution 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]); }
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)
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
Créer un tableau d’octets et convertir en 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); }
Si maxValue est trop petit, l’aléatoire générera la même valeur. Donc, vous pouvez définir le hasard en dehors de la fonction:
static void Main(ssortingng[] 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... }
Voici un autre moyen de générer des nombres dans la plage sans perdre de valeurs et de permettre aux BigIntegers de min et max.
public BigInteger RandomBigInteger(BigInteger min, BigInteger max) { Random rnd = new Random(); ssortingng numeratorSsortingng, denominatorSsortingng; double fraction = rnd.NextDouble(); BigInteger inRange; //Maintain all 17 digits of precision, //but remove the leading zero and the decimal point; numeratorSsortingng = fraction.ToSsortingng("G17").Remove(0, 2); //Use the length instead of 17 in case the random //fraction ends with one or more zeros denominatorSsortingng = ssortingng.Format("1E{0}", numeratorSsortingng.Length); inRange = (max - min) * BigInteger.Parse(numeratorSsortingng) / BigInteger.Parse(denominatorSsortingng, System.Globalization.NumberStyles.AllowExponent) + min; return inRange; }
Pour la généralité, vous pouvez également spécifier la précision. Cela semble fonctionner.
public BigInteger RandomBigIntegerInRange(BigInteger min, BigInteger max, int precision) { Random rnd = new Random(); ssortingng numeratorSsortingng, denominatorSsortingng; double fraction = rnd.NextDouble(); BigInteger inRange; numeratorSsortingng = GenerateNumeratorWithSpecifiedPrecision(precision); denominatorSsortingng = ssortingng.Format("1E{0}", numeratorSsortingng.Length); inRange = (max - min) * BigInteger.Parse(numeratorSsortingng) / BigInteger.Parse(denominatorSsortingng, System.Globalization.NumberStyles.AllowExponent) + min; return inRange; } private ssortingng GenerateNumeratorWithSpecifiedPrecision(int precision) { Random rnd = new Random(); ssortingng answer = ssortingng.Empty; while(answer.Length < precision) { answer += rnd.NextDouble().ToString("G17").Remove(0, 2); } if (answer.Length > precision) //Most likely { answer = answer.Subssortingng(0, precision); } return answer; }
La méthode Range
suivante renverra un IEnumerable
dans la plage que vous spécifiez. Une méthode d’extension simple renverra un élément aléatoire dans IEnumerable.
public static IEnumerable 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 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);
// retourne des valeurs aléatoires et dans ce cas, il était 10000000000000003