1 /** 2 * ae.utils.random 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.utils.random; 15 16 import std.algorithm.comparison; 17 import std.algorithm.mutation; 18 import std.array; 19 import std.random; 20 import std.range.primitives; 21 22 /// Like `randomShuffle`, but returns results incrementally 23 /// (still copies the input, but calls `gen` only as needed). 24 /// Like `randomCover`, but much faster 25 /// (O(n) instead of O(n^2), though less space-efficient. 26 auto incrementalRandomShuffle(Range, RandomGen)(Range range, ref RandomGen gen) 27 if (isInputRange!Range && isUniformRNG!RandomGen) 28 { 29 alias E = ElementType!Range; 30 static struct IncrementalRandomShuffle 31 { 32 private: 33 E[] arr; 34 size_t i; 35 RandomGen* gen; 36 37 this(Range range, RandomGen* gen) 38 { 39 this.arr = range.array; 40 this.gen = gen; 41 prime(); 42 } 43 44 void prime() 45 { 46 import std.algorithm.mutation : swapAt; 47 arr.swapAt(i, i + uniform(0, arr.length - i, gen)); 48 } 49 50 public: 51 @property bool empty() const { return i == arr.length; } 52 ref E front() { return arr[i]; } 53 void popFront() 54 { 55 i++; 56 if (!empty) 57 prime(); 58 } 59 } 60 61 return IncrementalRandomShuffle(move(range), &gen); 62 } 63 64 auto incrementalRandomShuffle(Range)(Range range) 65 if (isInputRange!Range) 66 { return incrementalRandomShuffle(range, rndGen); } 67 68 unittest 69 { 70 auto shuffled = [1, 2].incrementalRandomShuffle; 71 assert(shuffled.equal([1, 2]) || shuffled.equal([2, 1])); 72 }