【问题标题】:Is there a way to grab the actual state of System.Random?有没有办法获取 System.Random 的实际状态?
【发布时间】:2012-01-01 14:18:32
【问题描述】:

我希望能够获得 System.Random 的实际状态或种子或任何内容,以便我可以关闭应用程序,当用户重新启动它时,它只是用存储的应用程序“重新播种”它并像以前一样继续从未关闭。

有可能吗?

使用 Jon 的想法,我想出了这个来测试它;

static void Main(string[] args)
{
    var obj = new Random();
    IFormatter formatter = new BinaryFormatter();
    Stream stream = new FileStream("c:\\test.txt", FileMode.Create, FileAccess.Write, FileShare.None);
    formatter.Serialize(stream, obj);
    stream.Close();
    for (var i = 0; i < 10; i++)
        Console.WriteLine(obj.Next().ToString());

    Console.WriteLine();

    formatter = new BinaryFormatter();
    stream = new FileStream("c:\\test.txt", FileMode.Open, FileAccess.Read, FileShare.Read);
    obj = (Random)formatter.Deserialize(stream);
    stream.Close();
    for (var i = 0; i < 10; i++)
        Console.WriteLine(obj.Next().ToString());

    Console.Read();
}

【问题讨论】:

  • 我创建了一个新的库来处理这个问题:github.com/RafalSzefler/NPrng 它提供了几个不同的带有序列化器的 prng 算法。

标签: c# .net random


【解决方案1】:

它是可序列化的,所以你可能会发现你可以使用BinaryFormatter 并保存字节数组...

示例代码:

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public class Program
{
    public static void Main(String[] args)
    {
        Random rng = new Random();
        Console.WriteLine("Values before saving...");
        ShowValues(rng);

        BinaryFormatter formatter = new BinaryFormatter(); 
        MemoryStream stream = new MemoryStream();
        formatter.Serialize(stream, rng);

        Console.WriteLine("Values after saving...");
        ShowValues(rng);

        stream.Position = 0; // Rewind ready for reading
        Random restored = (Random) formatter.Deserialize(stream);

        Console.WriteLine("Values after restoring...");
        ShowValues(restored);       
    }

    static void ShowValues(Random rng)
    {
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine(rng.Next(100));
        }
    }
}

样品运行的结果很有希望:

Values before saving...
25
73
58
6
33
Values after saving...
71
7
87
3
77
Values after restoring...
71
7
87
3
77

诚然,我并不热衷于内置序列化,但如果这是为了相当快速和肮脏的 hack,那应该没问题...

【讨论】:

  • 我会在我这边试试,我会同时留意这里
  • 谢谢 :-),工作得很好,我没想到序列化 :-(
  • 通过查看生成的文件,它似乎存储了之前生成的全部或部分随机数。这可能会变大。你知道如何解决这个问题吗?
  • 我希望它一直是那个大小 - 它只是对象的状态,需要记住类名、程序集名称等的开销。
  • @Fredou:一点也不奇怪——这只是随机数生成器状态的快照。如果大小改变了会很奇怪!
【解决方案2】:

我不是 C# 人,所以我没有资源查看源代码,但种子必须存储在类中。从其文档来看,该类不是密封的,因此您应该能够创建一个子类。在这个子类中,您可以创建一个函数来返回当前种子。在您的应用关闭时,您可以保存种子(文件?数据库?具有良好内存的猴子?),然后在您启动新应用时重新加载它。

这有一个额外的好处,即允许您从任何以前保存的点恢复,用于备份或其他目的。

【讨论】:

  • 您有理由相信种子是该类的公共(或受保护)API 的一部分吗?如果种子是私有实现细节,继承将无济于事,派生类将无权访问。
  • @Anthony 我说我没有源代码。我想我可以明确地说我没有确凿的证据来支持这一点,所以不,我没有。对我来说,包含在受保护的 API 中似乎是合乎逻辑的。
  • 我会调查这个,因为我不喜欢我在序列化保存的文件中看到的内容
  • @Fredou 通过一些调查,您可能可以结合我的方法和 Jon 的方法:使用序列化机制提取种子而不是子类化,保存并正常加载。
  • 这取决于您所说的“种子”是什么意思。通常,种子是指用于初始化生成器的值。此值极不可能保留在任何 prng 中。原因很简单,它不是必需的,它仅用于初始化然后在每次迭代中混合的状态。但是你不需要种子,你只需要当前状态。由于这些算法是确定性的,因此它们不能依赖于其他任何东西。
【解决方案3】:

这是我在静态实用程序类中提出的:

//* Used for Getting and setting System.Random state *//
private static System.Reflection.FieldInfo[] randomFields;
private static System.Reflection.FieldInfo[] RandomFields { get { if (randomFields == null) {
            randomFields = new System.Reflection.FieldInfo[3];
            var t = typeof(System.Random);
            randomFields[0] = t.GetField("SeedArray", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            randomFields[1] = t.GetField("inext", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            randomFields[2] = t.GetField("inextp", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        }
        return randomFields;
    } }
/// <summary>
/// Gets <see cref="System.Random"/> current state array and indexes with Reflection.
/// </summary>
/// <param name="rand"></param>
/// <returns></returns>
public static int[] GetSeedArray(this System.Random rand) {
    var state = new int[58];
    ((int[])RandomFields[0].GetValue(rand)).CopyTo(state, 0);
    state[56] = (int)RandomFields[1].GetValue(rand);
    state[57] = (int)RandomFields[2].GetValue(rand);
    return state;
}

/// <summary>
/// Restores saved <see cref="System.Random"/> state and indexes with Reflection. Use with caution.
/// </summary>
/// <param name="rand"></param>
/// <param name="seedArray"></param>
public static void SetSeedArray(this System.Random rand, int[] seedArray) {
    if (seedArray.Length != 56 + 2) return;

    Array.Copy(seedArray, ((int[])RandomFields[0].GetValue(rand)), 56);
    RandomFields[1].SetValue(rand, seedArray[56]);
    RandomFields[2].SetValue(rand, seedArray[57]);
}

【讨论】:

    猜你喜欢
    • 2015-10-23
    • 1970-01-01
    • 1970-01-01
    • 2011-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-18
    相关资源
    最近更新 更多