Spec-Zone.ru › C++

std::random_shuffle, std::shuffle

Определено в заголовке <algorithm>
template< class RandomIt >
void random_shuffle( RandomIt first, RandomIt last );
(1) (устарело в C++14)
(удалено в C++17)
(2)
template< class RandomIt, class RandomFunc >
void random_shuffle( RandomIt first, RandomIt last, RandomFunc& r );
(до C++11)
template< class RandomIt, class RandomFunc >
void random_shuffle( RandomIt first, RandomIt last, RandomFunc&& r );
(с C++11)
(устарело в C++14)
(удалено в C++17)
template< class RandomIt, class URBG >
void shuffle( RandomIt first, RandomIt last, URBG&& g );
(3) (с C++11)

Переупорядочивает элементы в заданном диапазоне [first, last) таким образом, что каждая возможная перестановка этих элементов имеет равную вероятность появления.

1) Источник случайности определяется реализацией, но часто используется функция std::rand.
2) Источником случайности является объект-функция r.
3) Источником случайности является объект g.

Параметры

first, last - диапазон элементов, которые нужно случайным образом переупорядочить
r - объект-функция, возвращающая случайно выбранное значение типа, преобразуемого в std::iterator_traits<RandomIt>::difference_type в интервале [​0​, n) при вызове как r(n)
g - генератор случайных битов равномерного распределения тип результата которого преобразуется в std::iterator_traits<RandomIt>::difference_type
Требования к типу
-RandomIt должен удовлетворять требованиям ValueSwappable и LegacyRandomAccessIterator.
-std::remove_reference_t<URBG> должен удовлетворять требованиям генератора случайных битов равномерного распределения.

Возвращаемое значение

(нет)

Сложность

Линейна относительно расстояния между first и last.

Примечания

Обратите внимание, что реализация не диктуется стандартом, поэтому, даже если вы используете точно такие же RandomFunc или URBG (генератор случайных чисел равномерного распределения), вы можете получить разные результаты с различными реализациями стандартной библиотеки.

Причина удаления std::random_shuffle в C++17 заключается в том, что версия только для итераторов обычно зависит от std::rand, которая теперь также рассматривается для устаревания. (std::rand следует заменить классами из заголовка <random>, поскольку std::rand считается вредным.) Кроме того, версия только для итераторов std::random_shuffle обычно зависит от глобального состояния. Алгоритм перемешивания std::shuffle является предпочтительной заменой, так как он использует URBG в качестве своего 3-го параметра.

Возможная реализация

См. также реализации в libstdc++ и libc++.

random_shuffle (1)
template<class RandomIt>
void random_shuffle(RandomIt first, RandomIt last)
{
    typedef typename std::iterator_traits<RandomIt>::difference_type diff_t;
 
    for (diff_t i = last - first - 1; i > 0; --i)
    {
        using std::swap;
        swap(first[i], first[std::rand() % (i + 1)]);
        // rand() % (i + 1) is not actually correct, because the generated number is
        // not uniformly distributed for most values of i. The correct code would be
        // a variation of the C++11 std::uniform_int_distribution implementation.
    }
}
random_shuffle (2)
template<class RandomIt, class RandomFunc>
void random_shuffle(RandomIt first, RandomIt last, RandomFunc&& r)
{
    typedef typename std::iterator_traits<RandomIt>::difference_type diff_t;
 
    for (diff_t i = last - first - 1; i > 0; --i)
    {
        using std::swap;
        swap(first[i], first[r(i + 1)]);
    }
}
shuffle (3)
template<class RandomIt, class URBG>
void shuffle(RandomIt first, RandomIt last, URBG&& g)
{
    typedef typename std::iterator_traits<RandomIt>::difference_type diff_t;
    typedef std::uniform_int_distribution<diff_t> distr_t;
    typedef typename distr_t::param_type param_t;
 
    distr_t D;
    for (diff_t i = last - first - 1; i > 0; --i)
    {
        using std::swap;
        swap(first[i], first[D(g, param_t(0, i))]);
    }
}

Пример

Случайным образом перемешивает последовательность [1, 10] целых чисел:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <random>
#include <vector>
 
int main()
{
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
 
    std::random_device rd;
    std::mt19937 g(rd());
 
    std::shuffle(v.begin(), v.end(), g);
 
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << '\n';
}

Возможный вывод:

8 6 10 4 2 3 7 1 9 5

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применяется к Поведение, как опубликовано Корректное поведение
LWG 395 C++98 источник случайности перегрузки (1) не был указан, и
std::rand не мог быть источником из-за требования библиотеки C
он определяется реализацией,
и использование std::rand разрешено
LWG 552
(N2423)
C++98 r не требовалось быть источником
случайности перегрузки (2)[1]
требовалось
  1. Перегрузка (3) имеет такую же ошибку, но эта часть решения не применима к C++98.

См. также

next_permutation
генерирует следующее большее лексикографическое сочетание элементов
(шаблон функции)
prev_permutation
генерирует следующее меньшее лексикографическое сочетание элементов
(шаблон функции)
ranges::shuffle
(C++20)
случайным образом переупорядочивает элементы в диапазоне
(неблокирующая)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/algorithm/random_shuffle

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API