root/trunk/rangeextra/randomcover.d

Revision 573, 4.7 kB (checked in by dsimcha, 3 years ago)

Fix bug.

Line 
1 /**
2 An improved RandomCover struct that fixes bug 2865, adds sampling with
3 replacement, and uses a BitArray instead of a bool[] to track what's already
4 been sampled when sampling without replacement.
5
6 Authors:
7
8 $(WEB erdani.org, Andrei Alexandrescu) (Original RandomCover)
9 David Simcha (Modifications described above)
10
11 License:  Phobos License
12
13 Credits:
14
15 The entire random number library architecture is derived from the
16 excellent $(WEB
17 open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf, C++0X) random
18 number facility proposed by Jens Maurer and contributed to by
19 researchers at the Fermi laboratory.
20
21 Macros:
22
23 WIKI = Phobos/StdRandom
24 */
25
26 import std.random, std.range, std.bitmanip;
27
28 version(unittest) {
29     import std.conv, std.algorithm;
30 }
31
32 /**For RandomCover.*/
33 enum Replacement {
34     /**Sample the range with replacement.  This means the RandomCover will
35      * be an infinite range and elements may be repeated.*/
36     With,
37
38     /**Sample the range without replacement.  This means that the RandomCover
39      * will be a finite range and elements will not be repeated.*/
40     Without
41 }
42
43 ///
44 struct RandomCover(Range, Random, Replacement replace)
45 if(replace == Replacement.Without && isRandomAccessRange!(Range))
46 {
47     private Range _input;
48     private Random _rnd;
49     private BitArray _chosen;
50     private uint _current;
51     private uint _alreadyChosen;
52
53     this(Range input, Random rnd)
54     {
55         _input = input;
56         _rnd = rnd;
57         _chosen.length = _input.length;
58         popFront;
59     }
60
61     auto opSlice()
62     {
63         return this;
64     }
65
66     ref ElementType!(Range) front()
67     {
68         return _input[_current];
69     }
70
71     void popFront()
72     {
73         if (_alreadyChosen >= _input.length)
74         {
75             // No more elements
76             ++_alreadyChosen; // means we're done
77             return;
78         }
79         uint k = _input.length - _alreadyChosen;
80         uint i;
81         foreach (e; _input)
82         {
83             if (_chosen[i]) { ++i; continue; }
84             // Roll a dice with k faces
85             auto chooseMe = uniform(0, k, _rnd) == 0;
86             assert(k > 1 || chooseMe);
87             if (chooseMe)
88             {
89                 _chosen[i] = true;
90                 _current = i;
91                 ++_alreadyChosen;
92                 return;
93             }
94             --k;
95             ++i;
96         }
97         assert(false);
98     }
99
100     bool empty() { return _alreadyChosen > _input.length; }
101 }
102
103 ///
104 struct RandomCover(Range, Random, Replacement replace)
105 if(replace == Replacement.With && isRandomAccessRange!(Range))
106 {
107     private Range _input;
108     private Random _rnd;
109     private uint _current;
110     enum bool empty = false;
111
112     this(Range input, Random rnd)
113     {
114         _input = input;
115         _rnd = rnd;
116         popFront;
117     }
118
119     auto opSlice()
120     {
121         return this;
122     }
123
124     ref ElementType!(Range) front()
125     {
126         // Front should return the same thing every time it's called until
127         // popFront is called again, so don't do the random selection here.
128         return _input[_current];
129     }
130
131     void popFront()
132     {
133         _current = uniform(0U, _input.length, _rnd);
134     }
135
136 }
137
138 /**
139 Covers a given range $(D r) in a random manner.  If replace ==
140 Replacement.Without (default), goes through each
141 element of $(D r) once and only once, just in a random order. If
142 replace == Replacement.With, RandomCover becomes an infinite range that
143 randomly selects an element from $(D r), with uniform probability, each time
144 popFront() is called.  $(D r) must be a random access range with length.
145
146 Examples:
147 ----
148 int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
149 auto rnd = Random(unpredictableSeed);
150 foreach (e; randomCover(a, rnd))
151 {
152     writeln(e);
153 }
154
155 // Create a bootstrap sample of 100 elements of a by sampling with
156 // replacement.
157 auto bootstrapSample = take(100, randomCover!(Replacement.With)(a));
158 ----
159 */
160 RandomCover!(Range, Random, replace)
161 randomCover(Replacement replace = Replacement.Without, Range, Random)
162 (Range r, Random rnd)
163 if(isRandomAccessRange!(Range))
164 {
165     return typeof(return)(r, rnd);
166 }
167
168 unittest
169 {
170     int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
171     auto rnd = Random(unpredictableSeed);
172     auto rc = randomCover(a, rnd);
173     int[] b = new int[9];
174     uint i;
175     foreach (e; rc)
176     {
177         //writeln(e);
178         b[i++] = e;
179     }
180     sort(b);
181     assert(a == b, text(b));
182
183     // Other than statistical tests for randomness, can't test the
184     // with replacement version well.  Just make sure it instantiates
185     // and compiles.
186     foreach(e; take(100, randomCover!(Replacement.With)(a, rnd))) {}
187 }
Note: See TracBrowser for help on using the browser.