确定C#随机实例的种子

出于兴趣,我正在玩C#附带的Random类。 我试图预测未来。 因为它是伪随机的,所以必须有某种方法来预测数字。 到目前为止,我能想到的唯一方法是蛮力(获取所有可能的种子并找到其中随机数的模式),但我相信这将是太多的处理能力,因为种子可以是-2,147,483,647到2,147,483,647。

到目前为止,我已经确定:

new Random() == new Random(Environment.TickCount) 

并且这两个类将以相同的顺序提供相同的随机数。

是否有可能确定Random类实例的种子? 如果你可以在实例化Random类时知道Environment.TickCount,可以预测随机数,但我不知道是否可以完成。

因为它是伪随机的,所以必须有某种方法来预测数字。

确实; 如果您知道内部状态(特别是inextinextpSeedArray – 总共58个int值), 并且您知道将按照确切顺序请求的确切操作(例如,请求Next()Next()NextBytes()与请求Next()NextBytes()Next() )非常不同 – 那么你可以做出完全准确的前向猜测。 这是播种PRNG的全部内容 – 它允许可重复的随机性,这在您需要能够重播事件的许多场景中都很有用。

所以:我认为你不可能找回原始种子 ,但是为了预测未来 (而不是过去)你不需要种子 – 你只需要58个int值。

然而! 随机性重要的任何事情都应该使用加密随机提供程序 – 而这些不可重复或可猜测。

例如:

 static class Program { static Random Clone(this Random source) { var clone = new Random(); var type = typeof(Random); var field = type.GetField("inext", BindingFlags.Instance | BindingFlags.NonPublic); field.SetValue(clone, field.GetValue(source)); field = type.GetField("inextp", BindingFlags.Instance | BindingFlags.NonPublic); field.SetValue(clone, field.GetValue(source)); field = type.GetField("SeedArray", BindingFlags.Instance | BindingFlags.NonPublic); int[] arr = (int[])field.GetValue(source); field.SetValue(clone, arr.Clone()); return clone; } static void Main() { Random rand = new Random(); var clone = rand.Clone(); Console.WriteLine("My predictions:"); Console.WriteLine(clone.Next()); Console.WriteLine(clone.Next()); Console.WriteLine(clone.Next()); Console.WriteLine("Actual:"); Console.WriteLine(rand.Next()); Console.WriteLine(rand.Next()); Console.WriteLine(rand.Next()); } } 

Random的实现不会将种子存储在任何地方 – 它使用种子数组。 所以不幸的是,没有办法确定种子。

Random的构造函数接受种子看起来像这样:

 public Random(int Seed) { this.SeedArray = new int[0x38]; int num4 = (Seed == 0x80000000) ? 0x7fffffff : Math.Abs(Seed); int num2 = 0x9a4ec86 - num4; this.SeedArray[0x37] = num2; int num3 = 0x1; for (int i = 0x1; i < 0x37; i++) { int index = (0x15 * i) % 0x37; this.SeedArray[index] = num3; num3 = num2 - num3; if (num3 < 0x0) { num3 += 0x7fffffff; } num2 = this.SeedArray[index]; } for (int j = 0x1; j < 0x5; j++) { for (int k = 0x1; k < 0x38; k++) { this.SeedArray[k] -= this.SeedArray[0x1 + ((k + 0x1e) % 0x37)]; if (this.SeedArray[k] < 0x0) { this.SeedArray[k] += 0x7fffffff; } } } this.inext = 0x0; this.inextp = 0x15; Seed = 0x1; } 

您可以看到它不会将种子存储在任何位置。

(关于这一点的一个奇怪的事情是,由于一些奇怪的原因,它在末尾为Seed变量赋值1 - 但这并不是真正相关的;它只是有点奇怪。)

您可以为Random编写自己的包装类,它保留种子值以供以后检索。 实施只会记住Seed

 public class MyRandom: Random { public MyRandom() : this(Environment.TickCount) { } public MyRandom(int seed) : base(seed) { this.seed = seed; } public int Seed { get { return seed; } } private readonly int seed; }