44

首先,这个问题是从这个问题中删除的。我这样做是因为我认为这部分比一个较长问题的子部分更大。如有冒犯,请见谅。

假设您有一个生成随机性的算法。现在你如何测试它?或者更直接地说——假设你有一个洗牌的算法,你如何测试它是一个完全随机的算法?

为这个问题添加一些理论 - 一副纸牌可以在 52 中洗牌!(52阶乘)不同的方式。拿一副牌,用手洗牌,写下所有牌的顺序。你得到那个洗牌的概率是多少?答案:1 / 52!。

洗牌后,你依次得到每种花色的 A、K、Q、J ……的机会是多少?回答 1 / 52!

因此,只需洗牌一次并查看结果,您绝对不会获得任何关于洗牌算法随机性的信息。两次,你有更多的信息,三个甚至更多......

您将如何黑盒测试洗牌算法的随机性?

4

11 回答 11

30

统计数据。测试 RNG 的事实标准是Diehard 套件(最初可在http://stat.fsu.edu/pub/diehard获得)。或者,Ent 程序提供更易于解释但不太全面的测试。

至于改组算法,请使用众所周知的算法,例如Fisher-Yates(又名“Knuth Shuffle”)。只要底层 RNG 是一致随机的,洗牌将是一致随机的。如果您使用的是 Java,则此算法在标准库中可用(请参阅Collections.shuffle)。

对于大多数应用程序来说这可能无关紧要,但请注意,大多数 RNG 没有提供足够的自由度来生成 52 张牌的所有可能排列(在此处解释)。

于 2008-09-11T12:45:04.717 回答
7

这是您可以执行的一项简单检查。它使用生成的随机数来估计 Pi。这不是随机性的证明,但糟糕的 RNG 通常在这方面表现不佳(它们会返回 2.5 或 3.8 而不是 ~3.14)。

理想情况下,这只是您将运行以检查随机性的众多测试之一。

您可以检查的其他内容是输出的标准偏差。在 0..n 范围内均匀分布的一组值的预期标准偏差接近 n/sqrt(12)。

/**
 * This is a rudimentary check to ensure that the output of a given RNG
 * is approximately uniformly distributed.  If the RNG output is not
 * uniformly distributed, this method will return a poor estimate for the
 * value of pi.
 * @param rng The RNG to test.
 * @param iterations The number of random points to generate for use in the
 * calculation.  This value needs to be sufficiently large in order to
 * produce a reasonably accurate result (assuming the RNG is uniform).
 * Less than 10,000 is not particularly useful.  100,000 should be sufficient.
 * @return An approximation of pi generated using the provided RNG.
 */
public static double calculateMonteCarloValueForPi(Random rng,
                                                   int iterations)
{
    // Assumes a quadrant of a circle of radius 1, bounded by a box with
    // sides of length 1.  The area of the square is therefore 1 square unit
    // and the area of the quadrant is (pi * r^2) / 4.
    int totalInsideQuadrant = 0;
    // Generate the specified number of random points and count how many fall
    // within the quadrant and how many do not.  We expect the number of points
    // in the quadrant (expressed as a fraction of the total number of points)
    // to be pi/4.  Therefore pi = 4 * ratio.
    for (int i = 0; i < iterations; i++)
    {
        double x = rng.nextDouble();
        double y = rng.nextDouble();
        if (isInQuadrant(x, y))
        {
            ++totalInsideQuadrant;
        }
    }
    // From these figures we can deduce an approximate value for Pi.
    return 4 * ((double) totalInsideQuadrant / iterations);
}

/**
 * Uses Pythagoras' theorem to determine whether the specified coordinates
 * fall within the area of the quadrant of a circle of radius 1 that is
 * centered on the origin.
 * @param x The x-coordinate of the point (must be between 0 and 1).
 * @param y The y-coordinate of the point (must be between 0 and 1).
 * @return True if the point is within the quadrant, false otherwise.
 */
private static boolean isInQuadrant(double x, double y)
{
    double distance = Math.sqrt((x * x) + (y * y));
    return distance <= 1;
}
于 2008-09-11T13:50:23.937 回答
6

首先,不可能确定某个有限输出是否“真正随机”,因为正如您所指出的,任何输出都是可能的

可以做的是获取一系列输出,并根据更可能的情况检查该序列的各种测量值。您可以得出一种生成算法做得很好的置信度分数。

例如,您可以检查 10 种不同 shuffle 的输出。为每张牌分配一个 0-51 的数字,并在洗牌过程中取第 6 位牌的平均值。收敛平均值为 25.5,因此您会惊讶地看到这里的值为 1。您可以使用中心极限定理来估计给定位置的每个平均值的可能性。

但我们不应该止步于此!因为这个算法可能会被一个只在两个随机播放之间交替的系统所愚弄,这两个随机播放被设计为在每个位置给出 25.5 的精确平均值。我们怎样才能做得更好?

我们期望在每个位置,在不同的洗牌中都有一个均匀的分布(任何给定卡片的可能性相等)。因此,在 10 次洗牌中,我们可以尝试验证选择“看起来一致”。这基本上只是原始问题的简化版本。您可以检查标准偏差是否合理、最小值是否合理以及最大值是否合理。您还可以检查其他值,例如最接近的两张卡片(通过我们分配的数字)是否也有意义。

但是我们也不能像这样无限地添加各种测量值,因为如果有足够的统计数据,任何特定的洗牌都将由于某种原因显得极不可能(例如,这是极少数出现 X、Y、Z 牌的洗牌之一命令)。所以最大的问题是:哪一组测量是正确的?在这里我不得不承认我不知道最好的答案。但是,如果您考虑到某个应用程序,您可以选择一组好的属性/测量值来测试,并使用它们——这似乎是密码学家处理事情的方式。

于 2008-09-11T12:49:16.313 回答
4

有很多关于测试随机性的理论。对于洗牌算法的一个非常简单的测试,你可以做很多洗牌,然后运行卡方检验,每张牌出现在任何位置的概率是一致的。但这并不能测试连续卡不相关,因此您还需要对此进行测试。

Knuth 的计算机编程艺术第 2 卷提供了许多测试,您可以在第 3.3.2 节(经验测试)和 3.3.4(频谱测试)及其背后的理论中使用这些测试。

于 2008-09-11T12:51:45.217 回答
3

测试随机性的唯一方法是编写一个程序,尝试为正在测试的数据建立一个预测模型,然后使用该模型尝试预测未来的数据,然后显示其预测的不确定性或熵随着时间的推移趋于最大值(即均匀分布)。当然,您总是不确定您的模型是否已捕获所有必要的上下文;给定一个模型,总是有可能构建第二个模型,该模型生成的非随机数据在第一个模型看来是随机的。但是只要你接受冥王星的轨道对洗牌算法的结果影响不大,那么你应该能够满足自己,它的结果是可以接受的随机性。

当然,如果你这样做,你也可以使用你的模型来生成你想要的数据。如果你这样做,那么你又回到了第一方。

于 2008-09-11T12:32:52.827 回答
2

洗牌很多,然后记录结果(如果我没看错的话)。我记得看到过“随机数生成器”的比较。他们只是一遍又一遍地测试它,然后绘制结果。

如果它真的是随机的,那么图形将大部分是均匀的。

于 2008-09-11T12:29:58.717 回答
0

我没有完全关注你的问题。你说

假设您有一个生成随机性的算法。现在你如何测试它?

你什么意思?如果您假设可以生成随机性,则无需对其进行测试。

一旦你有一个好的随机数生成器,创建一个随机排列就很容易(例如,调用你的卡片 1-52。生成 52 个随机数,将每个随机数按顺序分配给一张卡片,然后根据你的 52 个随机数进行排序)。您不会通过生成排列来破坏好 RNG 的随机性。

困难的问题是您是否可以信任您的RNG。 这是一个示例链接,供人们在特定上下文中讨论该问题。

于 2008-09-11T12:47:01.450 回答
0

测试52!可能性当然是不可能的。相反,尝试在较小数量的牌上洗牌,比如 3、5 和 10。然后你可以测试数十亿次洗牌,并使用直方图和卡方统计检验来证明每个排列都是一个“偶数”数次。

于 2008-09-11T12:57:31.160 回答
0

到目前为止还没有代码,因此我从对原始问题的回答中复制粘贴了一个测试部分。

  // ...
  int main() {
    typedef std::map<std::pair<size_t, Deck::value_type>, size_t> Map;
    Map freqs;    
    Deck d;
    const size_t ntests = 100000;

    // compute frequencies of events: card at position
    for (size_t i = 0; i < ntests; ++i) {
      d.shuffle();
      size_t pos = 0;
      for(Deck::const_iterator j = d.begin(); j != d.end(); ++j, ++pos) 
        ++freqs[std::make_pair(pos, *j)]; 
    }

    // if Deck.shuffle() is correct then all frequencies must be similar
    for (Map::const_iterator j = freqs.begin(); j != freqs.end(); ++j)
      std::cout << "pos=" << j->first.first << " card=" << j->first.second 
                << " freq=" << j->second << std::endl;    
  }

此代码不测试底层伪随机数生成器的随机性。测试 PRNG 随机性是一个完整的科学分支。

于 2008-09-11T13:18:55.900 回答
0

为了快速测试,您可以随时尝试压缩它。一旦它不压缩,您就可以进行其他测试。

我已经更努力地尝试了,但它拒绝为洗牌工作。所有的测试都失败了。它也很乏味,它不会让你指定你想要的值的范围或类似的东西。

于 2016-10-05T20:10:03.817 回答
-1

自己考虑一下,我会做的是:

设置(伪代码)

// A card has a Number 0-51 and a position 0-51
int[][] StatMatrix = new int[52][52]; // Assume all are set to 0 as starting values
ShuffleCards();
ForEach (card in Cards) {
   StatMatrix[Card.Position][Card.Number]++;
}

这给了我们一个 52x52 的矩阵,表示一张牌在某个位置结束的次数。重复多次(我会从 1000 开始,但比我更擅长统计的人可能会给出更好的数字)。

分析矩阵

如果我们有完美的随机性并且无限次地进行洗牌,那么对于每张牌和每个位置,牌在该位置结束的次数与任何其他牌相同。用不同的方式说同样的话:

statMatrix[position][card] / numberOfShuffle = 1/52.

所以我会计算我们离这个数字有多远。

于 2008-09-11T14:43:50.733 回答