std::random_shuffle, std::shuffle
From cppreference.com
| ヘッダー <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 です。
以下のいずれかの条件が満たされている場合、動作は未定義です。
- r の戻り値の型が std::iterator_traits<RandomIt>::difference_type に変換可能でない。
- 型 std::iterator_traits<RandomIt>::difference_type の正の値 n が与えられた場合、r(n) の結果が区間
[0,n)のランダムに選ばれた値でない。
3) ランダム性のソースはオブジェクト g です。
型
T を std::remove_reference_t<URBG> とすると、以下のいずれかの条件が満たされている場合、動作は未定義です。-
Tが UniformRandomBitGenerator でない。
|
(C++20まで) |
もし *first の型が Swappable でない(C++11まで)RandomIt が ValueSwappable でない(C++11以降)、動作は未定義です。
目次 |
[編集] パラメータ
| first, last | - | ランダムにシャッフルする要素の 範囲 を定義するイテレータのペア。 |
| r | - | ランダムに選ばれた値を返す関数オブジェクト。 |
| g | - | ランダムに選ばれた値を返すジェネレータオブジェクト。 |
| 型要件 | ||
-RandomItはLegacyRandomAccessIteratorの要件を満たす必要がある。 | ||
[編集] 計算量
ちょうど std::distance(first, last) - 1 回の交換。
[編集] 実装例
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))]); } } |
[編集] 注意
実装は標準によって規定されていないため、まったく同じ RandomFunc または URBG (Uniform Random Number Generator) を使用しても、標準ライブラリの実装が異なれば異なる結果が得られる可能性があることに注意してください。
C++17 で std::random_shuffle が削除された理由は、イテレータのみのバージョンが通常 std::rand に依存しているためです。std::rand は、非推奨の議論にも上がっています。(std::rand は、std::rand は有害と見なされるため、<random> ヘッダーのクラスに置き換えるべきです。) さらに、イテレータのみの std::random_shuffle バージョンは通常、グローバル状態に依存します。std::shuffle のシャッフルアルゴリズムは、3 番目のパラメータとして URBG を使用するため、推奨される代替手段です。
[編集] 例
整数 [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] |
必要 |
- ↑ オーバーロード (3) も同様の不具合がありますが、その部分の解決策は C++98 には適用されません。
[編集] 関連項目
| 要素の範囲の次に大きい辞書順の順列を生成する (関数テンプレート) | |
| 要素の範囲の次に小さい辞書順の順列を生成する (関数テンプレート) | |
| (C++20) |
範囲内の要素をランダムに並べ替える (アルゴリズム関数オブジェクト) |