Common
A library of common classes.
RandomSeed.cs
2using System;
3using System.Collections.Generic;
4using System.Linq;
5using System.Runtime.Serialization;
6
8{
17 [DataContract(Namespace = Constants.DataContractNamespace)]
18 public class RandomSeed
19 {
23 public static RandomSeed Current { get; } = new RandomSeed();
24
28 [DataMember(Order = 1)]
29 public int Seed { get; private set; }
30
34 [DataMember(Order = 2)]
35 private int Position1 { get; set; }
36
40 [DataMember(Order = 3)]
41 private int Position2 { get; set; }
42
46 [DataMember(Order = 4)]
47 private int[] Seeds { get; set; } = new int[56];
48
52 public RandomSeed()
53 {
54 SetSeed(Environment.TickCount);
55 }
56
61 public RandomSeed(int seed)
62 {
63 SetSeed(seed);
64 }
65
70 private RandomSeed(RandomSeed other)
71 {
72 Seed = other.Seed;
73 Position1 = other.Position1;
74 Position2 = other.Position2;
75 Seeds = new int[other.Seeds.Length];
76 Array.Copy(other.Seeds, Seeds, other.Seeds.Length);
77 }
78
80 public override string ToString()
81 {
82 return $"RandomSeed(Seed = {Seed})";
83 }
84
89 {
90 return new RandomSeed(this);
91 }
92
97 private static int Mod(int value)
98 {
99 if (value < 0)
100 return value + int.MaxValue;
101 return value;
102 }
103
108 private static int WrapIndex(int value)
109 {
110 if (value > 55)
111 return 1;
112 return value;
113 }
114
119 public void SetSeed(int seed)
120 {
121 Seed = seed;
122 Position1 = 0;
123 Position2 = 21;
124 var mj = seed == int.MinValue ? int.MaxValue : Math.Abs(seed);
125 mj = 161803398 - mj;
126 Seeds[55] = mj;
127 var mk = 1;
128
129 // Populate random seed array.
130 for (int i = 1; i < 55; i++)
131 {
132 var index = 21 * i % 55;
133 Seeds[index] = mk;
134 mk = Mod(mj - mk);
135 mj = Seeds[index];
136 }
137
138 // Generate random numbers to reduce seed bias.
139 for (int k = 1; k < 5; k++)
140 {
141 for (int i = 1; i < 56; i++)
142 {
143 Seeds[i] = Mod(Seeds[i] - Seeds[1 + (i + 30) % 55]);
144 }
145 }
146 }
147
152 public int Next()
153 {
154 var i = WrapIndex(Position1 + 1);
155 var j = WrapIndex(Position2 + 1);
156 var next = Mod(Seeds[i] - Seeds[j]);
157
158 if (next == int.MaxValue)
159 next--;
160
161 Seeds[i] = next;
162 Position1 = i;
163 Position2 = j;
164 return next;
165 }
166
171 public int Next(int maxValue)
172 {
173 return (int)(NextDouble() * maxValue);
174 }
175
181 public int Next(int minValue, int maxValue)
182 {
183 var delta = maxValue - (long)minValue;
184 var t = delta <= int.MaxValue ? NextDouble() : NextLargeDouble();
185 return (int)((long)(t * delta) + minValue);
186 }
187
191 public float NextFloat()
192 {
193 return (float)NextDouble();
194 }
195
200 public float NextFloat(float maxValue)
201 {
202 return (float)NextDouble(maxValue);
203 }
204
210 public float NextFloat(float minValue, float maxValue)
211 {
212 return (float)NextDouble(minValue, maxValue);
213 }
214
218 public double NextDouble()
219 {
220 return Next() / (double)int.MaxValue;
221 }
222
227 public double NextDouble(double maxValue)
228 {
229 return NextDouble() * maxValue;
230 }
231
237 public double NextDouble(double minValue, double maxValue)
238 {
239 var x = NextDouble();
240 return x * maxValue + (1 - x) * minValue;
241 }
242
246 public (float x, float y) FloatInsideUnitCircle()
247 {
248 var (x, y) = DoubleInsideUnitCircle();
249 return ((float)x, (float)y);
250 }
251
255 public (double x, double y) DoubleInsideUnitCircle()
256 {
257 var radius = Math.Sqrt(NextDouble());
258 var angle = NextDouble(2 * Math.PI);
259 var x = radius * Math.Cos(angle);
260 var y = radius * Math.Sin(angle);
261 return (x, y);
262 }
263
267 private double NextLargeDouble()
268 {
269 var next = Next();
270
271 if (Next() % 2 == 0)
272 next = -next;
273
274 double result = next;
275 result += int.MaxValue - 1;
276 result /= 2.0 * int.MaxValue - 1;
277 return result;
278 }
279
284 public void Shuffle<T>(IList<T> list)
285 {
286 for (int i = 0; i < list.Count - 1; i++)
287 {
288 var j = Next(i, list.Count);
289 (list[i], list[j]) = (list[j], list[i]);
290 }
291 }
292
297 public List<T> Shuffled<T>(IEnumerable<T> collection)
298 {
299 var copy = new List<T>(collection);
300 Shuffle(copy);
301 return copy;
302 }
303
308 private int DrawIndex(IList<double> totals)
309 {
310 if (totals.Count > 0)
311 {
312 var value = NextDouble(totals[totals.Count - 1]);
313
314 for (int i = 0; i < totals.Count; i++)
315 {
316 if (value <= totals[i] && totals[i] > 0)
317 return i;
318 }
319 }
320
321 return -1;
322 }
323
328 public int DrawWeightedIndex(IList<double> weights)
329 {
330 if (weights.Count > 0)
331 {
332 var totals = new List<double>(weights.Count);
333 return DrawWeightedIndex(weights, totals);
334 }
335
336 return -1;
337 }
338
343 public int DrawWeightedIndex(IList<float> weights)
344 {
345 if (weights.Count > 0)
346 {
347 var totals = new List<double>(weights.Count);
348 return DrawWeightedIndex(weights, totals);
349 }
350
351 return -1;
352 }
353
360 public int DrawWeightedIndex(IList<double> weights, List<double> totals)
361 {
362 Maths.CumSum(weights, totals);
363 return DrawIndex(totals);
364 }
365
372 public int DrawWeightedIndex(IList<float> weights, List<double> totals)
373 {
374 Maths.CumSum(weights, totals);
375 return DrawIndex(totals);
376 }
377
386 public List<int> DrawWeightedIndexes(IList<double> weights, int count, bool withReplacement)
387 {
388 if (withReplacement)
389 return DrawWeightedIndexesWithReplacement(weights, count);
390
391 return DrawWeightedIndexesWithoutReplacement(weights, count);
392 }
393
399 private List<int> DrawWeightedIndexesWithReplacement(IList<double> weights, int count)
400 {
401 var result = new List<int>(count);
402
403 if (weights.Count > 0)
404 {
405 var totals = Maths.CumSum(weights);
406
407 for (int i = 0; i < count; i++)
408 {
409 var index = DrawIndex(totals);
410
411 if ((uint)index < (uint)weights.Count)
412 result.Add(index);
413 }
414 }
415
416 return result;
417 }
418
424 private List<int> DrawWeightedIndexesWithoutReplacement(IList<double> weights, int count)
425 {
426 count = Math.Min(count, weights.Count);
427 var result = new List<int>(count);
428
429 if (weights.Count > 0)
430 {
431 var weightsCopy = weights.ToArray();
432 var totals = new List<double>(weightsCopy.Length);
433
434 for (int i = 0; i < count; i++)
435 {
436 var index = DrawWeightedIndex(weightsCopy, totals);
437
438 if ((uint)index < (uint)weightsCopy.Length)
439 {
440 weightsCopy[index] = 0;
441 result.Add(index);
442 }
443 }
444 }
445
446 return result;
447 }
448
457 public List<int> DrawWeightedIndexes(IList<float> weights, int count, bool withReplacement)
458 {
459 if (withReplacement)
460 return DrawWeightedIndexesWithReplacement(weights, count);
461
462 return DrawWeightedIndexesWithoutReplacement(weights, count);
463 }
464
470 private List<int> DrawWeightedIndexesWithReplacement(IList<float> weights, int count)
471 {
472 var result = new List<int>(count);
473
474 if (weights.Count > 0)
475 {
476 var totals = Maths.CumSum(weights);
477
478 for (int i = 0; i < count; i++)
479 {
480 var index = DrawIndex(totals);
481
482 if ((uint)index < (uint)weights.Count)
483 result.Add(index);
484 }
485 }
486
487 return result;
488 }
489
495 private List<int> DrawWeightedIndexesWithoutReplacement(IList<float> weights, int count)
496 {
497 count = Math.Min(count, weights.Count);
498 var result = new List<int>(count);
499
500 if (weights.Count > 0)
501 {
502 var weightsCopy = weights.ToArray();
503 var totals = new List<double>(weightsCopy.Length);
504
505 for (int i = 0; i < count; i++)
506 {
507 var index = DrawWeightedIndex(weightsCopy, totals);
508
509 if ((uint)index < (uint)weightsCopy.Length)
510 {
511 weightsCopy[index] = 0;
512 result.Add(index);
513 }
514 }
515 }
516
517 return result;
518 }
519
524 public bool ChanceSatisfied(double chance)
525 {
526 return (NextDouble() <= chance && chance > 0) || chance >= 1;
527 }
528 }
529}
Contains extra math operations.
Definition: Maths.cs:10
static double[] CumSum(IList< double > values)
Returns the cumulative sums of the list.
Definition: Maths.cs:15
A class for performing pseudo-random number generation.
Definition: RandomSeed.cs:19
List< int > DrawWeightedIndexesWithReplacement(IList< float > weights, int count)
Draws a quantity of random weighted indexes from a list with replacement.
Definition: RandomSeed.cs:470
int DrawIndex(IList< double > totals)
Draws an index from the list of cumulative weights.
Definition: RandomSeed.cs:308
int Next(int minValue, int maxValue)
Returns a random value on the interval [minValue, maxValue).
Definition: RandomSeed.cs:181
RandomSeed(RandomSeed other)
Initializes a copy of a random seed.
Definition: RandomSeed.cs:70
List< T > Shuffled< T >(IEnumerable< T > collection)
Returns a new shuffled copy of the collection.
Definition: RandomSeed.cs:297
double NextDouble(double maxValue)
Returns a random double on the interval [0, maxValue).
Definition: RandomSeed.cs:227
RandomSeed(int seed)
Initializes a new random seed.
Definition: RandomSeed.cs:61
double NextDouble()
Returns a random double on the interval [0, 1).
Definition: RandomSeed.cs:218
double NextDouble(double minValue, double maxValue)
Returns a random double on the interval [minValue, maxValue).
Definition: RandomSeed.cs:237
static int Mod(int value)
Returns the positive modulo of a value with respect to int.MaxValue.
Definition: RandomSeed.cs:97
int Seed
The random seed.
Definition: RandomSeed.cs:29
int[] Seeds
An array of previous seeds.
Definition: RandomSeed.cs:47
int DrawWeightedIndex(IList< float > weights)
Draws a random weighted index from a list.
Definition: RandomSeed.cs:343
float NextFloat(float minValue, float maxValue)
Returns a random float on the interval [minValue, maxValue).
Definition: RandomSeed.cs:210
void SetSeed(int seed)
Sets the random seed and initializes the randomizer.
Definition: RandomSeed.cs:119
int DrawWeightedIndex(IList< float > weights, List< double > totals)
Draws a random weighted index from a list. The specified totals buffer allows for minimal garbage gen...
Definition: RandomSeed.cs:372
List< int > DrawWeightedIndexesWithoutReplacement(IList< double > weights, int count)
Draws a quantity of random weighted indexes from a list without replacement.
Definition: RandomSeed.cs:424
int Next(int maxValue)
Returns a random value on the interval [0, maxValue).
Definition: RandomSeed.cs:171
bool ChanceSatisfied(double chance)
Draws a random number and returns true if it satisfies the specified probability.
Definition: RandomSeed.cs:524
int Position2
The second position of the randomizer.
Definition: RandomSeed.cs:41
override string ToString()
Definition: RandomSeed.cs:80
int Next()
Returns a random integer on the interval [0, int.MaxValue).
Definition: RandomSeed.cs:152
float NextFloat()
Returns a random float on the interval [0, 1).
Definition: RandomSeed.cs:191
List< int > DrawWeightedIndexesWithoutReplacement(IList< float > weights, int count)
Draws a quantity of random weighted indexes from a list without replacement.
Definition: RandomSeed.cs:495
List< int > DrawWeightedIndexes(IList< float > weights, int count, bool withReplacement)
Draws a quantity of random weighted indexes from a list. Based on the draw weights and whether indexe...
Definition: RandomSeed.cs:457
static int WrapIndex(int value)
Wraps an index if it exceeds the top array bounds.
Definition: RandomSeed.cs:108
float x
Returns a random floating precision point inside the unit circle.
Definition: RandomSeed.cs:246
RandomSeed Copy()
Returns a copy of the object.
Definition: RandomSeed.cs:88
List< int > DrawWeightedIndexesWithReplacement(IList< double > weights, int count)
Draws a quantity of random weighted indexes from a list with replacement.
Definition: RandomSeed.cs:399
static RandomSeed Current
The current global random number generator.
Definition: RandomSeed.cs:23
int Position1
The first position of the randomizer.
Definition: RandomSeed.cs:35
RandomSeed()
Initializes a new random seed based on the current system ticks.
Definition: RandomSeed.cs:52
List< int > DrawWeightedIndexes(IList< double > weights, int count, bool withReplacement)
Draws a quantity of random weighted indexes from a list. Based on the draw weights and whether indexe...
Definition: RandomSeed.cs:386
int DrawWeightedIndex(IList< double > weights, List< double > totals)
Draws a random weighted index from a list. The specified totals buffer allows for minimal garbage gen...
Definition: RandomSeed.cs:360
void Shuffle< T >(IList< T > list)
Shuffles the specified list in place.
Definition: RandomSeed.cs:284
int DrawWeightedIndex(IList< double > weights)
Draws a random weighted index from a list.
Definition: RandomSeed.cs:328
double x
Returns a random double precision point inside the unit circle.
Definition: RandomSeed.cs:255
float NextFloat(float maxValue)
Returns a random float on the interval [0, maxValue).
Definition: RandomSeed.cs:200
double NextLargeDouble()
Returns a random high resolution double on the interval [0, 1).
Definition: RandomSeed.cs:267