Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/common/RandHelper.h
169678 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2005-2025 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 <vector>
27
#include <map>
28
#include <random>
29
#include <sstream>
30
#include <iostream>
31
#include <algorithm>
32
33
// TODO make this configurable
34
#define SAVE_ONLY_COUNT 1000000
35
36
// ===========================================================================
37
// class declaration
38
// ===========================================================================
39
40
class OptionsCont;
41
42
// ===========================================================================
43
// class definitions
44
// ===========================================================================
45
/**
46
* @class XoShiRo256PlusPlus
47
* @brief A random number generator as proposed here https://prng.di.unimi.it/xoshiro256plusplus.c
48
*/
49
class XoShiRo256PlusPlus {
50
public:
51
inline void seed(const uint64_t seed) {
52
this->state[0] = splitmix64(splitmix64(seed));
53
this->state[1] = splitmix64(this->state[0]);
54
this->state[2] = splitmix64(this->state[1]);
55
this->state[3] = splitmix64(this->state[2]);
56
}
57
58
inline uint64_t operator()() {
59
const uint64_t result = rotl64(this->state[0] + this->state[3], 23) + this->state[0];
60
const uint64_t t = this->state[1] << 17;
61
this->state[2] ^= this->state[0];
62
this->state[3] ^= this->state[1];
63
this->state[1] ^= this->state[2];
64
this->state[0] ^= this->state[3];
65
this->state[2] ^= t;
66
this->state[3] = rotl64(this->state[3], 45);
67
return result;
68
}
69
70
void discard(unsigned long long skip) {
71
while (skip-- > 0) {
72
(*this)();
73
}
74
}
75
76
friend std::ostream& operator<<(std::ostream& os, const XoShiRo256PlusPlus& r) {
77
os << r.state[0] << r.state[1] << r.state[2] << r.state[3];
78
return os;
79
}
80
81
friend std::istream& operator>>(std::istream& is, XoShiRo256PlusPlus& r) {
82
is >> r.state[0] >> r.state[1] >> r.state[2] >> r.state[3];
83
return is;
84
}
85
86
87
private:
88
static inline uint64_t rotl64(const uint64_t x, const int k) {
89
return (x << k) | (x >> (64 - k));
90
}
91
92
static inline uint64_t splitmix64(const uint64_t seed) {
93
uint64_t z = (seed + 0x9e3779b97f4a7c15);
94
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
95
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
96
return z ^ (z >> 31);
97
}
98
99
uint64_t state[4];
100
101
};
102
103
104
//class SumoRNG : public XoShiRo256PlusPlus {
105
class SumoRNG : public std::mt19937 {
106
public:
107
SumoRNG(const std::string& _id) : id(_id) {}
108
109
unsigned long long int count = 0;
110
std::string id;
111
};
112
113
114
/**
115
* @class RandHelper
116
* @brief Utility functions for using a global, resetable random number generator
117
*/
118
class RandHelper {
119
120
public:
121
/// @brief Initialises the given options container with random number options
122
static void insertRandOptions(OptionsCont& oc);
123
124
/// @brief Initialises the random number generator with hardware randomness or seed
125
static void initRand(SumoRNG* which = nullptr, const bool random = false, const int seed = 23423);
126
127
/// @brief Reads the given random number options and initialises the random number generator in accordance
128
static void initRandGlobal(SumoRNG* which = nullptr);
129
130
/// @brief Returns a random real number in [0, 1)
131
static double rand(SumoRNG* rng = nullptr);
132
133
/// @brief Returns a random real number in [0, maxV)
134
static inline double rand(double maxV, SumoRNG* rng = nullptr) {
135
return maxV * rand(rng);
136
}
137
138
/// @brief Returns a random real number in [minV, maxV)
139
static inline double rand(double minV, double maxV, SumoRNG* rng = nullptr) {
140
return minV + (maxV - minV) * rand(rng);
141
}
142
143
/// @brief Returns a random integer in [0, maxV-1]
144
static inline int rand(int maxV, SumoRNG* rng = nullptr) {
145
if (rng == nullptr) {
146
rng = &myRandomNumberGenerator;
147
}
148
unsigned int usedBits = maxV - 1;
149
usedBits |= usedBits >> 1;
150
usedBits |= usedBits >> 2;
151
usedBits |= usedBits >> 4;
152
usedBits |= usedBits >> 8;
153
usedBits |= usedBits >> 16;
154
155
// Draw numbers until one is found in [0, maxV-1]
156
int result;
157
do {
158
result = (*rng)() & usedBits;
159
rng->count++;
160
} while (result >= maxV);
161
return result;
162
}
163
164
/// @brief Returns a random integer in [minV, maxV-1]
165
static inline int rand(int minV, int maxV, SumoRNG* rng = nullptr) {
166
return minV + rand(maxV - minV, rng);
167
}
168
169
/// @brief Returns a random 64 bit integer in [0, maxV-1]
170
static inline long long int rand(long long int maxV, SumoRNG* rng = nullptr) {
171
if (maxV <= std::numeric_limits<int>::max()) {
172
return rand((int)maxV, rng);
173
}
174
if (rng == nullptr) {
175
rng = &myRandomNumberGenerator;
176
}
177
unsigned long long int usedBits = maxV - 1;
178
usedBits |= usedBits >> 1;
179
usedBits |= usedBits >> 2;
180
usedBits |= usedBits >> 4;
181
usedBits |= usedBits >> 8;
182
usedBits |= usedBits >> 16;
183
usedBits |= usedBits >> 32;
184
185
// Draw numbers until one is found in [0, maxV-1]
186
long long int result;
187
do {
188
result = (((unsigned long long int)(*rng)() << 32) | (*rng)()) & usedBits; // toss unused bits to shorten search
189
rng->count += 2;
190
} while (result >= maxV);
191
return result;
192
}
193
194
/// @brief Returns a random 64 bit integer in [minV, maxV-1]
195
static inline long long int rand(long long int minV, long long int maxV, SumoRNG* rng = nullptr) {
196
return minV + rand(maxV - minV, rng);
197
}
198
199
/// @brief Access to a random number from a normal distribution
200
static double randNorm(double mean, double variance, SumoRNG* rng = nullptr);
201
202
/// @brief Access to a random number from an exponential distribution
203
static double randExp(double rate, SumoRNG* rng = nullptr);
204
205
/// @brief Returns a random element from the given vector
206
template<class T>
207
static inline const T&
208
getRandomFrom(const std::vector<T>& v, SumoRNG* rng = nullptr) {
209
assert(v.size() > 0);
210
return v[rand((int)v.size(), rng)];
211
}
212
213
/// @brief save rng state to string
214
static std::string saveState(SumoRNG* rng = nullptr) {
215
if (rng == nullptr) {
216
rng = &myRandomNumberGenerator;
217
}
218
std::ostringstream oss;
219
oss << rng->count;
220
if (rng->count >= SAVE_ONLY_COUNT) {
221
oss << " " << (*rng);
222
}
223
return oss.str();
224
}
225
226
/// @brief load rng state from string
227
static void loadState(const std::string& state, SumoRNG* rng = nullptr) {
228
if (rng == nullptr) {
229
rng = &myRandomNumberGenerator;
230
}
231
std::istringstream iss(state);
232
iss >> rng->count;
233
if (rng->count < SAVE_ONLY_COUNT) {
234
rng->discard(rng->count);
235
} else {
236
iss >> (*rng);
237
}
238
}
239
240
template<class T>
241
static void shuffle(std::vector<T>& v, SumoRNG* rng = nullptr) {
242
for (int i = (int)(v.size() - 1); i > 0; --i) {
243
std::swap(*(v.begin() + i), *(v.begin() + rand(i, rng)));
244
}
245
}
246
247
static long long int count() {
248
return myRandomNumberGenerator.count;
249
}
250
251
protected:
252
/// @brief the default random number generator to use
253
static SumoRNG myRandomNumberGenerator;
254
255
};
256
257