Как проверить случайность (пример - перетасовка)

Во-первых, этот вопрос вырвался из этого вопроса. Я сделал это, потому что я думаю, что эта часть больше, чем часть более длинного вопроса. Если это оскорбляет, прошу простить меня.

Предположим, что у вас есть алгоритм, который генерирует случайность. Теперь как вы его протестируете? Или быть более прямым - предположим, у вас есть алгоритм, который перетасовывает колоду карт, как вы проверяете, что это совершенно случайный алгоритм?

Чтобы добавить некоторую теорию к проблеме - Палубу карт можно перетасовать в 52! (52 факториала) разными способами. Возьмите колоду карт, перетасуйте ее вручную и запишите порядок всех карт. Какова вероятность того, что вы получили бы именно эту перетасовку? Ответ: 1/52!.

Какова вероятность того, что после перетасовки вы получите A, K, Q, J... каждого костюма в последовательности? Ответ 1/52!

Итак, просто перетасовывая один раз и глядя на результат, вы не получите абсолютно никакой информации о вашей случайности. Два раза, и у вас есть дополнительная информация, еще три...

Как бы вы черным ящиком испытали алгоритм перетасовки для случайности?

Ответ 1

Статистика. Фактическим стандартом для тестирования RNG является Diehard suite. Альтернативно, Ent-программа предоставляет тесты, которые проще интерпретировать, но менее всеобъемлющие.

Что касается алгоритмов перетасовки, используйте известный алгоритм, например Fisher-Yates (a.k.a "Knuth Shuffle" ). Перемешивание будет равномерно случайным, если базовый RNG будет равномерно случайным. Если вы используете Java, этот алгоритм доступен в стандартной библиотеке (см. Collections.shuffle).

Вероятно, это не важно для большинства приложений, но имейте в виду, что большинство ГСЧ не обеспечивают достаточных степеней свободы для создания каждой возможной перестановки колоды с 52 карточками (пояснил здесь).

Ответ 2

Вот один простой пример, который вы можете выполнить. Он использует генерируемые случайные числа для оценки 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;
}

Ответ 3

Во-первых, невозможно точно знать, является ли определенный конечный результат "действительно случайным", поскольку, как вы указываете, возможен любой выход.

Что можно сделать, это сделать последовательность выходов и проверить различные измерения этой последовательности на более вероятную. Вы можете получить своего рода оценку доверия, что алгоритм генерации делает хорошую работу.

Например, вы можете проверить вывод 10 разных тасов. Назначьте номер 0-51 каждой карте и возьмите среднее значение карты в позиции 6 во время перетасовки. Сходящееся среднее значение составляет 25,5, поэтому вы будете удивлены, увидев здесь значение 1. Вы можете использовать центральную предельную теорему, чтобы получить оценку того, насколько вероятна каждая средняя для данной позиции.

Но мы не должны останавливаться здесь! Поскольку этот алгоритм можно одурачить системой, которая только чередуется между двумя тасованиями, которые предназначены для получения точного среднего значения 25,5 в каждой позиции. Как мы можем сделать лучше?

Мы ожидаем равномерного распределения (равного правдоподобия для любой данной карты) в каждой позиции, в разных тасованиях. Таким образом, среди 10 тасований мы можем попытаться проверить, что выбор "выглядит единообразным". Это в основном просто сокращенная версия исходной проблемы. Вы можете проверить, что стандартное отклонение выглядит разумным, что минимальный разумный и максимальное значение. Вы также можете проверить, что другие значения, такие как ближайшие две карты (по нашим назначенным номерам), также имеют смысл.

Но мы также не можем просто добавлять различные измерения, такие как бесконечность, поскольку, учитывая достаточную статистику, любая конкретная тасовка по какой-то причине окажется маловероятной (например, это одна из очень немногих тасований, в которой карты X, Y, Z отображаются в порядке). Поэтому большой вопрос: какой правильный набор измерений взять? Здесь я должен признать, что я не знаю лучшего ответа. Однако, если у вас есть определенное приложение, вы можете выбрать хороший набор свойств/измерений для тестирования и работать с ними - это похоже на то, как криптографы обрабатывают вещи.

Ответ 4

Там много теории по проверке случайности. Для очень простого теста на алгоритме перетасовки карт вы можете сделать много перетасовки, а затем запустить тест на квадрат с квадратом, чтобы вероятность того, что каждая карта, поднявшаяся в любом положении, была одинаковой. Но это не проверяет, что последовательные карты не коррелированы, поэтому вы также захотите провести тесты на этом.

Том 2 из Knuth Art of Computer Programming дает ряд тестов, которые вы можете использовать в разделах 3.3.2 (Эмпирические тесты) и 3.3.4 (Спектральный тест) и теории, лежащей за ними.

Ответ 5

Перемешайте много, а затем запишите результаты (если im читает это правильно). Я помню, что видел сравнения "генераторов случайных чисел". Они просто проверяют его снова и снова, а затем отображают результаты.

Если он действительно случайный, график будет в основном четным.

Ответ 6

Единственный способ проверить случайность - написать программу, которая пытается построить прогностическую модель для тестируемых данных, а затем использовать эту модель для прогнозирования будущих данных, а затем показывая, что неопределенность или энтропия, его предсказаний с течением времени имеют тенденцию к максимальному (т.е. равномерному распределению). Конечно, вы всегда будете сомневаться в том, захватила ли ваша модель весь необходимый контекст; с учетом модели всегда будет возможно построить вторую модель, которая генерирует неслучайные данные, которые выглядят случайными для первого. Но до тех пор, пока вы согласитесь, что орбита Плутона оказывает незначительное влияние на результаты алгоритма перетасовки, вы должны уметь убедиться, что ее результаты приемлемо случайны.

Конечно, если вы это сделаете, вы можете также использовать свою модель в целом, чтобы фактически создать нужные данные. И если вы это сделаете, вы вернетесь на квадратный.

Ответ 7

Я не полностью понимаю ваш вопрос. Вы говорите

Предположим, что у вас есть алгоритм, который генерирует случайность. Теперь, как вы его протестируете?

Что ты имеешь в виду? Если вы предполагаете, что можете генерировать случайность, нет необходимости ее проверять.

Как только у вас есть хороший генератор случайных чисел, создание произвольной перестановки легко (например, вызовите свои карты 1-52). Создайте 52 случайных числа, назначая каждому по карте, а затем сортируйте в соответствии с вашими 52 рандомами). Вы не собираетесь уничтожать случайность вашего хорошего RNG, создавая свою перестановку.

Трудный вопрос: можете ли вы доверять своему RNG. Ниже приведен пример ссылки на людей, обсуждающих эту проблему в определенном контексте.

Ответ 8

Тестирование 52! возможности, конечно, невозможны. Вместо этого попробуйте перетасовать на меньшем количестве карточек, например 3, 5 и 10. Затем вы можете протестировать миллиарды тасований и использовать гистограмму и статистический тест хи-квадрат, чтобы доказать, что каждая перестановка приближается к "четному" числу раз.

Ответ 9

Пока нет кода, поэтому скопируйте тестовую часть из моего ответа в исходный вопрос.

  // ...
  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 - это целая отрасль науки.

Ответ 10

Обдумав это сам, я бы сделал что-то вроде:

Настройка (псевдо-код)

// 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.

Итак, я бы вычислил, насколько далеко от этого числа мы.

Ответ 11

Просто просмотрите свой результат по сравнению с вашим поставленным перед случайностью. Вот пример того, что я сделал.

 public void testShuffleRemainingDeck()
{
    System.out.println("ShuffleRemainingDeck"); 
    Deck instance = new Deck(true);             //create new deck
    System.out.println(instance.toString());    //print unshuffled deck.
    instance.ShuffleRemainingDeck();            //shuffle the deck.
    System.out.println(instance.toString());    //print shuffled deck.
                                                //now visually compare the outputs.
}

Ответ 12

Для быстрого теста вы всегда можете попробовать сжать его. Как только он не сжимается, вы можете перейти на другие тесты.

Я пробовал умереть, но он отказывается работать на тасование. Все тесты терпят неудачу. Он также очень неустойчив, он не позволит вам указать диапазон значений, которые вы хотите, или что-то в этом роде.