Skip to content

Commit 88df5ea

Browse files
committed
Add RandomNumberGenerator::rand_weighted method
1 parent bb6b06c commit 88df5ea

File tree

5 files changed

+48
-0
lines changed

5 files changed

+48
-0
lines changed

core/math/random_number_generator.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ void RandomNumberGenerator::_bind_methods() {
4242
ClassDB::bind_method(D_METHOD("randfn", "mean", "deviation"), &RandomNumberGenerator::randfn, DEFVAL(0.0), DEFVAL(1.0));
4343
ClassDB::bind_method(D_METHOD("randf_range", "from", "to"), &RandomNumberGenerator::randf_range);
4444
ClassDB::bind_method(D_METHOD("randi_range", "from", "to"), &RandomNumberGenerator::randi_range);
45+
ClassDB::bind_method(D_METHOD("rand_weighted", "weights"), &RandomNumberGenerator::rand_weighted);
4546
ClassDB::bind_method(D_METHOD("randomize"), &RandomNumberGenerator::randomize);
4647

4748
ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed");

core/math/random_number_generator.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ class RandomNumberGenerator : public RefCounted {
5757
_FORCE_INLINE_ real_t randfn(real_t p_mean = 0.0, real_t p_deviation = 1.0) { return randbase.randfn(p_mean, p_deviation); }
5858
_FORCE_INLINE_ int randi_range(int p_from, int p_to) { return randbase.random(p_from, p_to); }
5959

60+
_FORCE_INLINE_ int rand_weighted(const Vector<float> &p_weights) { return randbase.rand_weighted(p_weights); }
61+
6062
RandomNumberGenerator() { randbase.randomize(); }
6163
};
6264

core/math/random_pcg.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "random_pcg.h"
3232

3333
#include "core/os/os.h"
34+
#include "core/templates/vector.h"
3435

3536
RandomPCG::RandomPCG(uint64_t p_seed, uint64_t p_inc) :
3637
pcg(),
@@ -42,6 +43,26 @@ void RandomPCG::randomize() {
4243
seed(((uint64_t)OS::get_singleton()->get_unix_time() + OS::get_singleton()->get_ticks_usec()) * pcg.state + PCG_DEFAULT_INC_64);
4344
}
4445

46+
int RandomPCG::rand_weighted(const Vector<float> &p_weights) {
47+
ERR_FAIL_COND_V_MSG(p_weights.is_empty(), -1, "Weights array is empty.");
48+
int64_t weights_size = p_weights.size();
49+
const float *weights = p_weights.ptr();
50+
float weights_sum = 0.0;
51+
for (int64_t i = 0; i < weights_size; ++i) {
52+
weights_sum += weights[i];
53+
}
54+
55+
float remaining_distance = Math::randf() * weights_sum;
56+
for (int64_t i = 0; i < weights_size; ++i) {
57+
remaining_distance -= weights[i];
58+
if (remaining_distance < 0) {
59+
return i;
60+
}
61+
}
62+
63+
return -1;
64+
}
65+
4566
double RandomPCG::random(double p_from, double p_to) {
4667
return randd() * (p_to - p_from) + p_from;
4768
}

core/math/random_pcg.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ static int __bsr_clz32(uint32_t x) {
5959
#define LDEXPF(s, e) ldexp(s, e)
6060
#endif
6161

62+
template <class T>
63+
class Vector;
64+
6265
class RandomPCG {
6366
pcg32_random_t pcg;
6467
uint64_t current_seed = 0; // The seed the current generator state started from.
@@ -87,6 +90,8 @@ class RandomPCG {
8790
return pcg32_boundedrand_r(&pcg, bounds);
8891
}
8992

93+
int rand_weighted(const Vector<float> &p_weights);
94+
9095
// Obtaining floating point numbers in [0, 1] range with "good enough" uniformity.
9196
// These functions sample the output of rand() as the fraction part of an infinite binary number,
9297
// with some tricks applied to reduce ops and branching:

doc/classes/RandomNumberGenerator.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,25 @@
1717
<link title="Random number generation">$DOCS_URL/tutorials/math/random_number_generation.html</link>
1818
</tutorials>
1919
<methods>
20+
<method name="rand_weighted">
21+
<return type="int" />
22+
<param index="0" name="weights" type="PackedFloat32Array" />
23+
<description>
24+
Returns a random index with non-uniform weights. Prints an error and returns [code]-1[/code] if the array is empty.
25+
[codeblocks]
26+
[gdscript]
27+
var rnd = RandomNumberGenerator.new()
28+
29+
var my_array = ["one", "two", "three, "four"]
30+
var weights = PackedFloat32Array([0.5, 1, 1, 2])
31+
32+
# Prints one of the four elements in `my_array`.
33+
# It is more likely to print "four", and less likely to print "two".
34+
print(my_array[rng.rand_weighted(weights)])
35+
[/gdscript]
36+
[/codeblocks]
37+
</description>
38+
</method>
2039
<method name="randf">
2140
<return type="float" />
2241
<description>

0 commit comments

Comments
 (0)