BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bdlb_pcgrandomgenerator.h
Go to the documentation of this file.
1/// @file bdlb_pcgrandomgenerator.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlb_pcgrandomgenerator.h -*-C++-*-
8#ifndef INCLUDED_BDLB_PCGRANDOMGENERATOR
9#define INCLUDED_BDLB_PCGRANDOMGENERATOR
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bdlb_pcgrandomgenerator bdlb_pcgrandomgenerator
15/// @brief Provide a class to generate random numbers using the PCG algorithm.
16/// @addtogroup bdl
17/// @{
18/// @addtogroup bdlb
19/// @{
20/// @addtogroup bdlb_pcgrandomgenerator
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bdlb_pcgrandomgenerator-purpose"> Purpose</a>
25/// * <a href="#bdlb_pcgrandomgenerator-classes"> Classes </a>
26/// * <a href="#bdlb_pcgrandomgenerator-description"> Description </a>
27/// * <a href="#bdlb_pcgrandomgenerator-usage"> Usage </a>
28/// * <a href="#bdlb_pcgrandomgenerator-example-1-simulating-a-pair-of-dice"> Example 1: Simulating a Pair of Dice </a>
29/// * <a href="#bdlb_pcgrandomgenerator-example-2-using-a-stream-selector-to-guarantee-uncorrelated-sequences"> Example 2: Using a Stream Selector to Guarantee Uncorrelated Sequences </a>
30///
31/// # Purpose {#bdlb_pcgrandomgenerator-purpose}
32/// Provide a class to generate random numbers using the PCG algorithm.
33///
34/// # Classes {#bdlb_pcgrandomgenerator-classes}
35///
36/// - bdlb::PcgRandomGenerator: PCG-based random number generator (RNG)
37///
38/// @see bdlb_random
39///
40/// # Description {#bdlb_pcgrandomgenerator-description}
41/// This component provides a single mechanism class,
42/// `bdlb::PcgRandomGenerator`, that is used to generate random numbers
43/// employing the PCG algorithm, a high-performance, high-quality RNG. PCG
44/// stands for "permuted congruential generator" (see http://www.pcg-random.org
45/// for details). The PCG technique employs the concepts of permutation
46/// functions on tuples and a base linear congruential generator. The PCG
47/// algorithm is seeded with two values: its initial state and a "stream
48/// selector." The stream selector is intended to address a potential hazard of
49/// multiple instances of a random number generator having unintended
50/// correlation between their outputs. For example, if we allow them to have
51/// the same internal state (e.g., mistakenly seeding them with the current time
52/// in seconds), they will output the exact same sequence of numbers. Employing
53/// a stream selector enables the same initial state to generate unique
54/// sequences. Free operators `==` and `!=` provide the operational definition
55/// of value. Refer to O'Neill (2014) at
56/// https://www.pcg-random.org/pdf/hmc-cs-2014-0905.pdf for details.
57///
58///
59/// ## Usage {#bdlb_pcgrandomgenerator-usage}
60///
61///
62/// This section illustrates intended use of this component.
63///
64/// ### Example 1: Simulating a Pair of Dice {#bdlb_pcgrandomgenerator-example-1-simulating-a-pair-of-dice}
65///
66///
67/// This example shows how one might use `bdlb::PcgRandomGenerator` to create
68/// and use a class to simulate the roll of a single die in a game, such as
69/// craps, that uses dice.
70///
71/// First, we define the `Die` class itself:
72/// @code
73/// // =========
74/// // class Die
75/// // =========
76///
77/// class Die {
78///
79/// // DATA
80/// bdlb::PcgRandomGenerator d_pcg;
81/// // used to generate next role of this die
82///
83/// public:
84/// // CREATORS
85///
86/// /// Create an object used to simulate a single die, using the
87/// /// specified `initialState` and `streamSelector`.
88/// Die(bsl::uint64_t initialState, bsl::uint64_t streamSelector);
89///
90/// // MANIPULATORS
91///
92/// /// Return the next pseudo-random value in the range `[1 .. 6]`,
93/// /// based on the sequence of values established by the
94/// /// `initialState` and `streamSelector` values supplied at
95/// /// construction.
96/// int roll();
97/// };
98///
99/// // ---------
100/// // class Die
101/// // ---------
102///
103/// // CREATORS
104/// inline
105/// Die::Die(bsl::uint64_t initialState, bsl::uint64_t streamSelector)
106/// : d_pcg(initialState, streamSelector)
107/// {
108/// }
109///
110/// // MANIPULATORS
111/// int Die::roll()
112/// {
113/// int result;
114///
115/// do {
116/// result = d_pcg.generate() & 7;
117/// } while (result > 5);
118///
119/// return result + 1;
120/// }
121/// @endcode
122/// Now, we can use our `Die` class to get the random numbers needed to
123/// simulate a game of craps. Note that the game of craps requires two dice.
124///
125/// We can instantiate a single `Die` and role it twice:
126/// @code
127/// void rollOneDieTwice()
128/// {
129/// Die a(123, 456);
130///
131/// int d1 = a.roll();
132/// int d2 = a.roll();
133///
134/// cout << "d1 = " << d1 << ", d2 = " << d2 << endl; // d1 = 3, d2 = 5
135/// }
136/// @endcode
137/// Alternatively, we could create two instances of `Die`, with separate initial
138/// states/sequences, and role each one once:
139/// @code
140/// void rollTwoDice()
141/// {
142/// Die a(123, 123);
143/// Die b(456, 456);
144///
145/// int d1 = a.roll();
146/// int d2 = b.roll();
147///
148/// cout << "d1 = " << d1 << ", d2 = " << d2 << endl; // d1 = 3, d2 = 1
149/// }
150/// @endcode
151/// Note that the specification of separate seeds is important to produce a
152/// proper distribution for our game. If we had shared the seed value each die
153/// would always produce the same sequence of values as the other.
154/// @code
155/// void shareStateAndSequence()
156/// {
157/// Die a(123, 456); // BAD IDEA
158/// Die b(123, 456); // BAD IDEA
159///
160/// int d1 = a.roll();
161/// int d2 = b.roll();
162/// assert(d2 == d1);
163/// }
164/// @endcode
165/// ### Example 2: Using a Stream Selector to Guarantee Uncorrelated Sequences {#bdlb_pcgrandomgenerator-example-2-using-a-stream-selector-to-guarantee-uncorrelated-sequences}
166///
167///
168/// This example illustrates how a stream selector can be used to guarantee
169/// that two distinct instances of `PcgRandomGenerator` produce uncorrelated
170/// sequences.
171///
172/// First, we use `bdlb::RandomDevice` to choose the initial states for the
173/// generators using a source of true randomness:
174/// @code
175/// uint64_t state1;
176/// int rc = bdlb::RandomDevice::getRandomBytes(
177/// reinterpret_cast<unsigned char *>(&state1), sizeof(state1));
178/// (void)rc; // error handling omitted
179///
180/// uint64_t state2;
181/// int rc = bdlb::RandomDevice::getRandomBytes(
182/// reinterpret_cast<unsigned char *>(&state2), sizeof(state2));
183/// (void)rc; // error handling omitted
184/// @endcode
185/// Then we select two distinct stream selectors for the generators, which, due
186/// to the PCG algorithmic properties, will guarantee that the sequences will be
187/// uncorrelated even if the initial state is exactly the same:
188/// @code
189/// const uint64_t streamSelector1 = 1;
190/// const uint64_t streamSelector2 = 2;
191/// @endcode
192/// Finally, we initialize the generators with their respective initial states
193/// and stream selectors and check that they produce distinct sequences of
194/// random numbers. The check is guaranteed to pass even in the rare, but
195/// possible case of `state1 == state2`:
196/// @code
197/// bdlb::PcgRandomGenerator rng1(state1, streamSelector1);
198/// bdlb::PcgRandomGenerator rng2(state2, streamSelector2);
199///
200/// const int NUM_VALUES = 1000;
201/// bsl::vector<bsl::uint32_t> sequence1(NUM_VALUES);
202/// bsl::vector<bsl::uint32_t> sequence2(NUM_VALUES);
203///
204/// for (int i = 0; i < NUM_VALUES; ++i) {
205/// sequence1[i] = rng1.generate();
206/// sequence2[i] = rng2.generate();
207/// }
208///
209/// assert(sequence1 != sequence2);
210/// @endcode
211/// @}
212/** @} */
213/** @} */
214
215/** @addtogroup bdl
216 * @{
217 */
218/** @addtogroup bdlb
219 * @{
220 */
221/** @addtogroup bdlb_pcgrandomgenerator
222 * @{
223 */
224
225#include <bdlscm_version.h>
226
227#include <bsls_keyword.h>
228
229#include <bsl_cstdint.h>
230
231
232namespace bdlb {
233
234 // ========================
235 // class PcgRandomGenerator
236 // ========================
237
238/// This class implements a random number generator (RNG) based on the PCG
239/// algorithm.
240///
241/// See @ref bdlb_pcgrandomgenerator
243
244 // DATA
245 bsl::uint64_t d_state; // the RNG state
246 bsl::uint64_t d_streamSelector; // selected sequence (stream)
247
248 // FRIENDS
249 friend bool operator==(const PcgRandomGenerator& lhs,
250 const PcgRandomGenerator& rhs);
251
252 public:
253 // CREATORS
254
256 /// Create a `PcgRandomGenerator` object and seed it with the optionally
257 /// specified `initState` and `streamSelector`. If `initState` and
258 /// `streamSelector` are not specified, 0 is used for both. The
259 /// highest-order bit of `streamSelector` is ignored. Note that
260 /// invoking different instances created with the identical `initState`
261 /// and `streamSelector` will result in the same sequence of random
262 /// numbers from subsequent invocations of `generate()`.
263 PcgRandomGenerator(bsl::uint64_t initState, bsl::uint64_t streamSelector);
264
265 /// Create a `PcgRandomGenerator` object having the same value as the
266 /// specified `original` object. Note that this newly created object
267 /// will generate the same sequence of numbers as `original`.
268 PcgRandomGenerator(const PcgRandomGenerator& original) = default;
269
270 // MANIPULATORS
271
272 /// Assign to this object the value of the specified `rhs` object, and
273 /// return a non-`const` reference to this object. Note that the object
274 /// after assignment will generate the same sequence of numbers as `rhs`.
276
277 /// Return the next random number in the sequence generated by this
278 /// object.
279 bsl::uint32_t generate();
280
281 /// Seed this generator with the specified new `initState` and
282 /// `streamSelector`. Note that the sequence of random numbers produced
283 /// from subsequent invocations of `generate()` will be the same as that
284 /// produced by an object created by
285 /// `PcgRandomGenerator(initState, streamSelector)`.
286 void seed(bsl::uint64_t initState, bsl::uint64_t streamSelector);
287};
288
289// FREE OPERATORS
290
291/// Return `true` if the specified `lhs` and `rhs` objects have the same
292/// value, and `false` otherwise. Two `PcgRandomGenerator` objects have the
293/// same value if they would produce the same sequence of random numbers
294/// from subsequent invocations of `generate()`.
295bool operator==(const PcgRandomGenerator& lhs, const PcgRandomGenerator& rhs);
296
297/// Return `true` if the specified `lhs` and `rhs` objects do not have the
298/// same value, and `false` otherwise. Two `PcgRandomGenerator` objects do
299/// not have the same value if they would not produce the same sequence of
300/// random numbers from subsequent invocations of `generate()`.
301bool operator!=(const PcgRandomGenerator& lhs, const PcgRandomGenerator& rhs);
302
303// ============================================================================
304// INLINE DEFINITIONS
305// ============================================================================
306
307 // ------------------------
308 // class PcgRandomGenerator
309 // ------------------------
310
311// CREATORS
312inline
317
318inline
320 bsl::uint64_t streamSelector)
321{
322 seed(initState, streamSelector);
323}
324
325// MANIPULATORS
326inline
328{
329 static const bsl::uint64_t k_MULTIPLIER = 6364136223846793005ULL;
330
331 bsl::uint64_t oldstate = d_state;
332
333 // Advance the internal state
334 d_state = oldstate * k_MULTIPLIER + d_streamSelector;
335
336 // Perform the output function
337 bsl::uint32_t xorshifted =
338 static_cast<bsl::uint32_t>(((oldstate >> 18u) ^ oldstate) >> 27u);
339 bsl::uint32_t rot = static_cast<bsl::uint32_t>(oldstate >> 59u);
340 return (xorshifted >> rot) | (xorshifted << ((0u - rot) & 31u));
341}
342
343inline
344void PcgRandomGenerator::seed(bsl::uint64_t initState,
345 bsl::uint64_t streamSelector)
346{
347 d_streamSelector = (streamSelector << 1u) | 1u;
348
349 d_state = 0U;
350 generate();
351 d_state += initState;
352 generate();
353}
354
355} // close package namespace
356
357// FREE OPERATORS
358inline
359bool bdlb::operator==(const PcgRandomGenerator& lhs,
360 const PcgRandomGenerator& rhs)
361{
362 return lhs.d_state == rhs.d_state &&
363 lhs.d_streamSelector == rhs.d_streamSelector;
364}
365
366inline
367bool bdlb::operator!=(const PcgRandomGenerator& lhs,
368 const PcgRandomGenerator& rhs)
369{
370 return !(lhs == rhs);
371}
372
373
374
375#endif
376
377// ----------------------------------------------------------------------------
378// Copyright 2020 Bloomberg Finance L.P.
379//
380// Licensed under the Apache License, Version 2.0 (the "License");
381// you may not use this file except in compliance with the License.
382// You may obtain a copy of the License at
383//
384// http://www.apache.org/licenses/LICENSE-2.0
385//
386// Unless required by applicable law or agreed to in writing, software
387// distributed under the License is distributed on an "AS IS" BASIS,
388// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
389// See the License for the specific language governing permissions and
390// limitations under the License.
391// ----------------------------- END-OF-FILE ----------------------------------
392
393/** @} */
394/** @} */
395/** @} */
Definition bdlb_pcgrandomgenerator.h:242
void seed(bsl::uint64_t initState, bsl::uint64_t streamSelector)
Definition bdlb_pcgrandomgenerator.h:344
PcgRandomGenerator(const PcgRandomGenerator &original)=default
friend bool operator==(const PcgRandomGenerator &lhs, const PcgRandomGenerator &rhs)
PcgRandomGenerator()
Definition bdlb_pcgrandomgenerator.h:313
PcgRandomGenerator & operator=(const PcgRandomGenerator &rhs)=default
bsl::uint32_t generate()
Definition bdlb_pcgrandomgenerator.h:327
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bdlb_algorithmworkaroundutil.h:74
bool operator!=(const BigEndianInt16 &lhs, const BigEndianInt16 &rhs)
bool operator==(const BigEndianInt16 &lhs, const BigEndianInt16 &rhs)