Symphony Of Empires
disc_dist.hpp
Go to the documentation of this file.
1 // Eng3D - General purpouse game engine
2 // Copyright (C) 2021, Eng3D contributors
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <https://www.gnu.org/licenses/>.
16 //
17 // ----------------------------------------------------------------------------
18 // Name:
19 // disc_dist.hpp
20 //
21 // Abstract:
22 // Does some important stuff.
23 // ----------------------------------------------------------------------------
24 
25 #pragma once
26 
27 #include <numeric>
28 #include <algorithm>
29 #include <cstddef>
30 #include <cassert>
31 #include <vector>
32 #include "eng3d/rand.hpp"
33 
38 template<typename T>
40  std::vector<T> _items;
41  std::vector<float> alias;
42  std::vector<float> prob;
43  Eng3D::Rand _rand;
44 public:
45  DiscreteDistribution(std::vector<T> items, std::vector<float> probabilities)
46  : _items{ items },
47  _rand{ 0 }
48  {
49  assert(!items.empty() && !probabilities.empty());
50  // Scale each probabilty
51  const auto total = std::accumulate(probabilities.begin(), probabilities.end(), 0.f);
52  assert(total != 0.f);
53  const auto scale = probabilities.size() / total;
54  for(auto& p : probabilities) p *= scale;
55 
56  // Fill two work tables with probabilities larger/smaller than 1
57  std::vector<std::pair<float, size_t>> small;
58  small.reserve(probabilities.size());
59  std::vector<std::pair<float, size_t>> big;
60  big.reserve(probabilities.size());
61  for(size_t i = 0; i < probabilities.size(); i++) {
62  if(probabilities[i] < 1.0f) small.emplace_back(probabilities[i], i);
63  else big.emplace_back(probabilities[i], i);
64  }
65 
66  // Remove from the bigger one and place on the smaller ones
67  alias.resize(probabilities.size(), 0);
68  prob.resize(probabilities.size(), 0);
69  while(!small.empty() && !big.empty()) {
70  const auto& [less_prob, less_index] = small.back();
71  const auto& [greater_prob, greater_index] = big.back();
72  big.pop_back();
73  prob[less_index] = less_prob;
74  alias[less_index] = greater_index;
75  small.pop_back();
76  auto final_prob = (greater_prob + less_prob) - 1;
77  if(final_prob < 1.f) small.emplace_back(greater_prob, greater_index);
78  else big.emplace_back(greater_prob, greater_index);
79  }
80 
81  for(const auto& [_, index] : big)
82  prob[index] = 1.f;
83  big.clear();
84 
85  for(const auto& [_, index] : small)
86  prob[index] = 1.f;
87  small.clear();
88  }
89  ~DiscreteDistribution() = default;
90 
92  T get_item() {
93  auto index = _rand() % _items.size();
94  auto r = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
95  if(prob[index] < r) index = alias[index];
96  return _items[index];
97  }
98 };
Uses the Aiias method to generate a lookup table of with different probabilties.
Definition: disc_dist.hpp:39
~DiscreteDistribution()=default
DiscreteDistribution(std::vector< T > items, std::vector< float > probabilities)
Definition: disc_dist.hpp:45
T get_item()
Get a random item with a certian probabilty, thread safe.
Definition: disc_dist.hpp:92
Thread safe random number generator.
Definition: rand.hpp:34