Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/common/RandHelper.h
193674 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2005-2026 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file RandHelper.h
15
/// @author Daniel Krajzewicz
16
/// @author Michael Behrisch
17
/// @author Jakob Erdmann
18
/// @date Fri, 29.04.2005
19
///
20
//
21
/****************************************************************************/
22
#pragma once
23
#include <config.h>
24
25
#include <cassert>
26
#include <cstring>
27
#include <vector>
28
#include <map>
29
#include <random>
30
#include <sstream>
31
#include <iostream>
32
#include <algorithm>
33
34
// TODO make this configurable
35
#define SAVE_ONLY_COUNT 1000000
36
37
// ===========================================================================
38
// helper function
39
// ===========================================================================
40
41
#ifdef __clang__
42
__attribute__((no_sanitize("unsigned-integer-overflow")))
43
#endif
44
inline uint64_t splitmix64(const uint64_t seed) {
45
uint64_t z = (seed + 0x9e3779b97f4a7c15);
46
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
47
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
48
return z ^ (z >> 31);
49
}
50
51
52
// ===========================================================================
53
// class declaration
54
// ===========================================================================
55
56
class OptionsCont;
57
58
// ===========================================================================
59
// class definitions
60
// ===========================================================================
61
/**
62
* @class XoShiRo256PlusPlus
63
* @brief A random number generator as proposed here https://prng.di.unimi.it/xoshiro256plusplus.c
64
*/
65
class XoShiRo256PlusPlus {
66
public:
67
inline void seed(const uint64_t seed) {
68
this->state[0] = splitmix64(splitmix64(seed));
69
this->state[1] = splitmix64(this->state[0]);
70
this->state[2] = splitmix64(this->state[1]);
71
this->state[3] = splitmix64(this->state[2]);
72
}
73
74
inline uint64_t operator()() {
75
const uint64_t result = rotl64(this->state[0] + this->state[3], 23) + this->state[0];
76
const uint64_t t = this->state[1] << 17;
77
this->state[2] ^= this->state[0];
78
this->state[3] ^= this->state[1];
79
this->state[1] ^= this->state[2];
80
this->state[0] ^= this->state[3];
81
this->state[2] ^= t;
82
this->state[3] = rotl64(this->state[3], 45);
83
return result;
84
}
85
86
void discard(unsigned long long skip) {
87
while (skip-- > 0) {
88
(*this)();
89
}
90
}
91
92
friend std::ostream& operator<<(std::ostream& os, const XoShiRo256PlusPlus& r) {
93
os << r.state[0] << r.state[1] << r.state[2] << r.state[3];
94
return os;
95
}
96
97
friend std::istream& operator>>(std::istream& is, XoShiRo256PlusPlus& r) {
98
is >> r.state[0] >> r.state[1] >> r.state[2] >> r.state[3];
99
return is;
100
}
101
102
103
private:
104
static inline uint64_t rotl64(const uint64_t x, const int k) {
105
return (x << k) | (x >> (64 - k));
106
}
107
108
uint64_t state[4];
109
110
};
111
112
113
//class SumoRNG : public XoShiRo256PlusPlus {
114
class SumoRNG : public std::mt19937 {
115
public:
116
SumoRNG(const std::string& _id) : id(_id) {}
117
118
void setSeed(int _seed) {
119
origSeed = _seed;
120
seed(_seed);
121
}
122
123
unsigned long long int count = 0;
124
int origSeed = default_seed;
125
std::string id;
126
};
127
128
129
/**
130
* @class RandHelper
131
* @brief Utility functions for using a global, resetable random number generator
132
*/
133
class RandHelper {
134
135
public:
136
/// @brief Initialises the given options container with random number options
137
static void insertRandOptions(OptionsCont& oc);
138
139
/// @brief Initialises the random number generator with hardware randomness or seed
140
static void initRand(SumoRNG* which = nullptr, const bool random = false, const int seed = 23423);
141
142
/// @brief Reads the given random number options and initialises the random number generator in accordance
143
static void initRandGlobal(SumoRNG* which = nullptr);
144
145
static int getSeed(SumoRNG* which = nullptr);
146
147
/// @brief Returns a random real number in [0, 1)
148
static double rand(SumoRNG* rng = nullptr);
149
150
/// @brief Returns a random real number in [0, maxV)
151
static inline double rand(double maxV, SumoRNG* rng = nullptr) {
152
return maxV * rand(rng);
153
}
154
155
/// @brief Returns a random real number in [minV, maxV)
156
static inline double rand(double minV, double maxV, SumoRNG* rng = nullptr) {
157
return minV + (maxV - minV) * rand(rng);
158
}
159
160
/// @brief Returns a random integer in [0, maxV-1]
161
static inline int rand(int maxV, SumoRNG* rng = nullptr) {
162
if (rng == nullptr) {
163
rng = &myRandomNumberGenerator;
164
}
165
unsigned int usedBits = maxV - 1;
166
usedBits |= usedBits >> 1;
167
usedBits |= usedBits >> 2;
168
usedBits |= usedBits >> 4;
169
usedBits |= usedBits >> 8;
170
usedBits |= usedBits >> 16;
171
172
// Draw numbers until one is found in [0, maxV-1]
173
int result;
174
do {
175
result = (*rng)() & usedBits;
176
rng->count++;
177
} while (result >= maxV);
178
return result;
179
}
180
181
/// @brief Returns a random integer in [minV, maxV-1]
182
static inline int rand(int minV, int maxV, SumoRNG* rng = nullptr) {
183
return minV + rand(maxV - minV, rng);
184
}
185
186
/// @brief Returns a random 64 bit integer in [0, maxV-1]
187
static inline long long int rand(long long int maxV, SumoRNG* rng = nullptr) {
188
if (maxV <= std::numeric_limits<int>::max()) {
189
return rand((int)maxV, rng);
190
}
191
if (rng == nullptr) {
192
rng = &myRandomNumberGenerator;
193
}
194
unsigned long long int usedBits = maxV - 1;
195
usedBits |= usedBits >> 1;
196
usedBits |= usedBits >> 2;
197
usedBits |= usedBits >> 4;
198
usedBits |= usedBits >> 8;
199
usedBits |= usedBits >> 16;
200
usedBits |= usedBits >> 32;
201
202
// Draw numbers until one is found in [0, maxV-1]
203
long long int result;
204
do {
205
result = (((unsigned long long int)(*rng)() << 32) | (*rng)()) & usedBits; // toss unused bits to shorten search
206
rng->count += 2;
207
} while (result >= maxV);
208
return result;
209
}
210
211
/// @brief Returns a random 64 bit integer in [minV, maxV-1]
212
static inline long long int rand(long long int minV, long long int maxV, SumoRNG* rng = nullptr) {
213
return minV + rand(maxV - minV, rng);
214
}
215
216
/// @brief Access to a random number from a normal distribution
217
static double randNorm(double mean, double variance, SumoRNG* rng = nullptr);
218
219
/// @brief Access to a random number from an exponential distribution
220
static double randExp(double rate, SumoRNG* rng = nullptr);
221
222
/// @brief Returns a random element from the given vector
223
template<class T>
224
static inline const T&
225
getRandomFrom(const std::vector<T>& v, SumoRNG* rng = nullptr) {
226
assert(v.size() > 0);
227
return v[rand((int)v.size(), rng)];
228
}
229
230
/// @brief save rng state to string
231
static std::string saveState(SumoRNG* rng = nullptr) {
232
if (rng == nullptr) {
233
rng = &myRandomNumberGenerator;
234
}
235
std::ostringstream oss;
236
oss << rng->count;
237
if (rng->count >= SAVE_ONLY_COUNT) {
238
oss << " " << (*rng);
239
}
240
return oss.str();
241
}
242
243
/// @brief load rng state from string
244
static void loadState(const std::string& state, SumoRNG* rng = nullptr) {
245
if (rng == nullptr) {
246
rng = &myRandomNumberGenerator;
247
}
248
std::istringstream iss(state);
249
iss >> rng->count;
250
if (rng->count < SAVE_ONLY_COUNT) {
251
rng->discard(rng->count);
252
} else {
253
iss >> (*rng);
254
}
255
}
256
257
template<class T>
258
static void shuffle(std::vector<T>& v, SumoRNG* rng = nullptr) {
259
for (int i = (int)(v.size() - 1); i > 0; --i) {
260
std::swap(*(v.begin() + i), *(v.begin() + rand(i, rng)));
261
}
262
}
263
264
static long long int count() {
265
return myRandomNumberGenerator.count;
266
}
267
268
/// @brief return a value scrambled value from [0, 1]
269
static double randHash(long long int x) {
270
const uint64_t h = splitmix64(x) >> 11;
271
return (double)h / (double(1ULL << 53));
272
}
273
274
/// @brief string hashing adapted from https://en.wikipedia.org/wiki/MurmurHash
275
#ifdef __clang__
276
__attribute__((no_sanitize("integer"))) // left-shift and unsigned-integer-overflow
277
#endif
278
static uint32_t murmur3_32(const std::string& key2, int seed) {
279
const uint8_t* key = reinterpret_cast<const uint8_t*>(key2.data());
280
const size_t len = key2.size();
281
uint32_t h = seed;
282
uint32_t k;
283
/* Read in groups of 4. */
284
for (size_t i = len >> 2; i; i--) {
285
memcpy(&k, key, sizeof(uint32_t));
286
key += sizeof(uint32_t);
287
h ^= murmur_32_scramble(k);
288
h = (h << 13) | (h >> 19);
289
h = h * 5 + 0xe6546b64;
290
}
291
/* Read the rest. */
292
k = 0;
293
for (size_t i = len & 3; i; i--) {
294
k <<= 8;
295
k |= key[i - 1];
296
}
297
h ^= murmur_32_scramble(k);
298
/* Finalize. */
299
h ^= len;
300
h ^= h >> 16;
301
h *= 0x85ebca6b;
302
h ^= h >> 13;
303
h *= 0xc2b2ae35;
304
h ^= h >> 16;
305
return h;
306
}
307
308
309
protected:
310
/// @brief the default random number generator to use
311
static SumoRNG myRandomNumberGenerator;
312
313
/// @brief helper function for murmur_32_scramble from https://en.wikipedia.org/wiki/MurmurHash
314
#ifdef __clang__
315
__attribute__((no_sanitize("integer"))) // left-shift and unsigned-integer-overflow
316
#endif
317
static inline uint32_t murmur_32_scramble(uint32_t k) {
318
k *= 0xcc9e2d51;
319
k = (k << 15) | (k >> 17);
320
k *= 0x1b873593;
321
return k;
322
}
323
324
};
325
326