7.3. Случайное перемешивание данных

We use cookies. Read the Privacy and Cookie Policy

7.3. Случайное перемешивание данных

Проблема

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

Решение

Используйте стандартный алгоритм random_shuffle, определенный в <algorithm>. random_shuffle принимает два итератора произвольного доступа и (необязательно) функтор генератора случайных чисел и реорганизует случайным образом элементы заданного диапазона. Пример 7.3 показывает, как это делается.

Пример 7.3. Случайное перемешивание последовательностей

#include <iostream>

#include <vector>

#include <algorithm>

#include <iterator>

#include "utils.h" // Для printContainer(): см. 7.10

using namespace std;

int main() {

 vector<int> v;

 back_insert_iterator<std::vector<int> > p = back_inserter(v);

 for (int i = 0; i < 10; ++i) *p = i;

 printContainer(v, true);

 random_shuffle(v.begin(), v.end());

 printContainer(v, true);

}

Вывод должен выглядеть примерно так.

-----

0123456789

-----

8192057346

Обсуждение

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

void random_shuffle(RndIter first, RndIter last);

void random_shuffle(RndIter first, RndIter last, RandFunc& rand);

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

Этот генератор случайных чисел должен быть функтором с единственным аргументом, возвращающим единственное значение, и оба они должны преобразовываться в iterator_traits<RndIter>::difference_type. В большинстве случаев для этого подойдет целое число. Например, вот мой псевдогенератор случайных чисел.

struct RanNumGenFtor {

 size_t operator()(size_t n) const {

  return(rand() % n);

 }

} rnd;

random_shuffle(v.begin(), vend(), rnd);

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