【问题标题】:C# random number generatorC# 随机数生成器
【发布时间】:2010-09-22 16:08:28
【问题描述】:

我正在寻找一个随机数,它总是为给定的种子生成相同的“随机”数。种子由 x + (y 定义,其中 x 和 y 是高度图上的位置。

我可以每次使用我的种子创建一个新的 System.Random 实例,但这会带来很大的 GC 压力。特别是因为这会被调用很多次。

编辑: “很多”意味着一百万次。

感谢所有回答的人!我知道我不清楚,但我在这里了解到哈希函数正是我想要的。

【问题讨论】:

  • 这不是很随机,是吗?
  • 你会有多少种不同的组合?您可以将 Random 实例存储在以种子为键的字典中重复使用。
  • 正如您所指出的,System.Random 已经为给定的种子生成了相同的序列。您是否正在寻找具有 mutable 种子的 RNG,以便您可以简单地“重置”它?多少次是“很多”次,您是否对此进行了分析以查看它是否真的会导致较差的 GC 性能?
  • @driis:这行不通,因为一旦在其中一个实例上调用Next,种子就会改变。
  • 完全正确,如果您只希望每个种子的 一个 值而不是序列,那么它根本不是真正的随机(甚至是伪随机),您可能会很好地使用其他一些函数,比如哈希码。事实上,重读这个问题,我有一个明显的印象,这个问题试图重新发明哈希码。

标签: c# random


【解决方案1】:

我可以每次使用我的种子创建一个新的 System.Random 实例

这样做。

但那是很大的 GC 压力。特别是因为这会被调用很多次。

你打了多少次?它是否可验证地表现不佳?请注意,GC 已针对处理大量生命周期短的小对象进行了优化。它应该很容易解决这个问题。

而且,还有什么替代方案可以使用种子但不创建某个对象的新实例?事实上,这听起来更像是一个设计糟糕的类。

【讨论】:

  • 另一种选择是一个对象,您可以只传递一个种子值而不需要构造函数。
  • @Robert:是的,我认为这是“糟糕的设计”:它需要一个相当好的理由来使对象可重置而不是依赖new:可重置对象使得推理变得更加困难关于状态。
  • @Konrad:我认为你总是使用不可变对象。我们中的一些人仍然使用使用可变对象的传统 OOP 进行编程,我们不认为这是糟糕的设计,尤其是在我们不使用多线程的情况下。
  • 最好的办法是一个简单的函数,比如 int Random(int seed)
  • @Hannesh:不,这将是一件非常糟糕的事情,因为它显然总是返回相同的值——根本不是随机的。
【解决方案2】:

由于hash function 显然更接近您想要的,请考虑以下变体:

int Hash(int n) {
    const int prime = 1031;
    return (((n & 0xFFFF) * prime % 0xFFFF)) ^ (n >> 16);
}

在将最低有效两个字节与素数相乘后,将最低有效两个字节与四字节数的最高有效两个字节进行异或运算。因此,结果在 0

这应该稍微“打乱”输入数字,可靠地为相同的输入生成相同的值,并且看起来“随机”。现在,我还没有对分布进行随机分析,如果有统计学家看过它,他可能会直接陷入过敏性休克。 (事实上​​,我真的是在头脑中写下了这个实现。)

如果您需要一些不太成熟的东西,请考虑使用已建立的校验和(例如 CRC32)。

【讨论】:

  • 一开始我很失望,因为写 n = 1 - 10 的哈希值给出了一组数字,其中每个数字都比上一个大 1031。对更高的数字效果更好(显然),但在将值输入回函数时我得到了令人满意的结果(即 n = Hash(Hash(n)) )。
  • 如果统计学家看它,他可能只会认为函数不是真正随机的,结果也不是真正的正态分布。但这些都不是问题的重点,所以你不必担心人员伤亡。 ;-)
  • @Hannesh:您可以使用 MD5 哈希,将其截断为四个字节,然后将这些字节转换为数字。或者以相同的方式使用任何其他哈希。
  • MD5 散列非常昂贵,它只需要散列到人类无法真正看到模式的程度。我对 Konrads 哈希生成器的结果很满意。
【解决方案3】:

有关 C# 源代码,请参阅 Simple Random Number Generation。状态只是两个无符号整数,因此很容易在调用之间跟上。并且生成器通过了质量标准测试。

【讨论】:

  • 看起来不错。两个无符号整数正是我所拥有的!
【解决方案4】:

如果存储Dictionary<int, int> 来提供给定种子的新Random 对象返回的第一个值呢?

class RandomSource
{
    Dictionary<int, int> _dictionary = new Dictionary<int, int>();

    public int GetValue(int seed)
    {
        int value;
        if (!_dictionary.TryGetValue(seed, out value))
        {
            value = _dictionary[seed] = new Random(seed).Next();
        }

        return value;
    }
}

这会引发 GC 压力,即构造一个新的 Random 实例第一次你想要一个特定种子的值,但是使用相同种子的每个后续调用都会取而代之检索一个缓存的值。

【讨论】:

    【解决方案5】:

    我认为“随机数生成器”实际上并不是您想要的。只需创建另一个地图并使用随机值预先填充它。如果您当前的高度图是 W x H,最简单的解决方案是创建一个 W x H 二维数组,并使用 System.Random 用随机值填充每个元素。然后,您可以在需要时查找特定 (x, y) 坐标的预填充随机值。

    或者,如果您当前的高度图实际上存储了某种数据结构,您可以修改它以存储除了高度值之外的随机值。

    这样做的一个附带好处是,稍后,如果您需要,您可以对整个“随机”地图执行操作,以确保它具有某些属性。例如,根据上下文(这是针对游戏的吗?),您稍后可能会发现您希望消除地图上的随机性。如果您按照我的描述预先计算和存储值,这将是微不足道的。

    【讨论】:

    • 地图需要无限连续,如果我重新访问一个区域,一定不能改变。因为它需要无限连续,所以我需要一个函数来表示 (x, y) 处的高度。我已经解决了大部分问题,现在我需要的只是随机数。
    【解决方案6】:

    CSharpCity 为多个随机数生成器提供源。您必须进行试验,看看这些对性能的影响是否比 System.Random 小。

    ExtremeOptimization 提供了一个包含多个生成器的库。他们还讨论了生成器的质量和速度,并与 System.Random 进行了比较。

    最后,GC 压力是什么意思?您真的是指内存压力,这是我见过的唯一使用它的上下文吗? GC 的工作是非常有效地处理对象 gob 的创建和销毁。我担心你会陷入过早的优化诱惑。也许您可以创建一个测试应用程序,提供一些冷硬的数字。

    【讨论】:

    • 如果按照预期的方式使用 .NET 的生成器,我完全没问题,但我会重新创建 System.Random 的实例一百万次,因为我想设置种子.但事实证明,我真正想要的是一个哈希函数。
    猜你喜欢
    • 2011-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-24
    • 1970-01-01
    • 2016-08-15
    • 2017-04-10
    • 1970-01-01
    相关资源
    最近更新 更多