如何测试随机性(例如 - 改组)

时间:2008-09-11 12:27:22

标签: math theory random puzzle

首先,这个问题从this问题中删除了。我这样做是因为我认为这部分比一个较长问题的一部分要大。如果它冒犯了,请原谅我。

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

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

在洗牌之后,你在每个套装中获得A,K,Q,J ......的几率是多少?回答1/52!

所以,只需改组一次并查看结果就可以完全没有关于你的改组算法随机性的信息。两次,你有更多的信息,还有三个......

你如何用黑盒测试随机性的混洗算法?

11 个答案:

答案 0 :(得分:27)

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

对于改组算法,使用众所周知的算法,例如Fisher-Yates(a.k.a“Knuth Shuffle”)。只要下面的RNG是均匀随机的,shuffle将是均匀随机的。如果您使用的是Java,则此算法可在标准库中使用(请参阅Collections.shuffle)。

对于大多数应用来说,这可能并不重要,但请注意,大多数RNG没有提供足够的自由度来产生52张牌的每种可能的排列(解释here)。

答案 1 :(得分:6)

这是您可以执行的一项简单检查。它使用生成的随机数来估计Pi。它不是随机性的证据,但是不好的RNG通常不能很好地完成(它们会返回2.5或3.8而不是3.14)。

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

您可以检查的其他内容是输出的standard deviation。均匀分布的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;
}

答案 2 :(得分:5)

首先,我们无法确定某个有限输出是否“真正随机”,因为正如您所指出的那样any output is possible

可以做的是采取一系列输出并检查该序列的各种测量结果。你可以得出一种置信度得分,生成算法做得很好。

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

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

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

但我们也不能只是无限地添加各种测量,因为如果给出足够的统计数据,任何特定的随机播放都会因某些原因而显得不太可能(例如,这是卡片X,Y, Z出现在顺序中)。所以最大的问题是:哪种测量方法正确?在这里,我不得不承认我不知道最好的答案。但是,如果你有一个特定的应用程序,你可以选择一组好的属性/测量来测试,并使用它们 - 这似乎是密码学家处理事情的方式。

答案 3 :(得分:4)

关于测试随机性的理论很多。对于卡洗牌算法的一个非常简单的测试,你可以做很多洗牌,然后进行卡方检验,每张卡在任何位置出现的概率是均匀的。但这并不能测试连续的卡片是否相关,所以你也想对它进行测试。

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

答案 4 :(得分:2)

随机播放,然后记录结果(如果我正确读取)。我记得看过“随机数发生器”的比较。他们只是一遍又一遍地测试它,然后绘制结果图。

如果它是真正随机的,那么图表将基本上是偶数。

答案 5 :(得分:2)

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

当然,如果你这样做,你也可以使用你的模型生成来实际创建你想要的数据。如果你这样做,那么你就回到原点。

答案 6 :(得分:0)

我没有完全听从你的问题。你说

  

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

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

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

难题是您是否可以信任您的RNG。 Here's一个示例链接,指向在特定环境中讨论该问题的人。

答案 7 :(得分:0)

测试52!可能性当然是不可能的。相反,尝试在较少数量的牌上进行随机播放,例如3,5和10.然后你可以测试数十亿次洗牌,并使用直方图和卡方统计测试来证明每个排列都是“均匀”的数字时间。

答案 8 :(得分:0)

到目前为止没有代码,因此我将my answer的测试部分复制粘贴到原始问题。

  // ...
  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随机性是整个科学的一个分支。

答案 9 :(得分:0)

要进行快速测试,您可以随时尝试压缩它。一旦它不压缩,那么你可以进行其他测试。

我已经尝试过顽固分子,但它拒绝为洗牌工作。所有测试都失败了它也非常笨拙,它不会让你指定你想要的值范围或类似的东西。

答案 10 :(得分:-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.

所以我会计算出这个数字的距离。