gpt4 book ai didi

c++ - 在类中使用 std::chrono::high_resolution_clock 播种 std::mt19937 的正确方法是什么?

转载 作者:太空狗 更新时间:2023-10-29 23:10:37 26 4
gpt4 key购买 nike

首先,大家好!这是我在这里的第一个问题,所以我希望我没有搞砸。在写这里之前我在谷歌上搜索了很多。我刚接触编码和 C++,我正在自学。

考虑到我被告知只为任何随机引擎播种一次是一个很好的做法(我在这里可能是错的),使用 std::mt19937< 的正确/最佳/更有效的方法是什么 来自类内的随机标准库,由来自 chrono 标准库的 std::chrono::high_resolution_clock::now().time_since_epoch().count() 播种?

我想使用那个 chrono 值,因为它变化得非常快,并且会生成一个令人毛骨悚然的数字。我从未考虑过 std::random_device 因为我认为它有点阴暗。我可能又错了。

编辑:大部分时间我都在使用 C4Droid IDE 的 Android 手机上编码和学习,因为我没有太多空闲时间坐在合适的计算机上,所以这就是我认为的原因std::random_device 并不可靠。

在我知道类是什么之前,我已经成功地完成了它,但我现在正在学习类并做了很多试验和错误(到处放 static_casts,尝试 const,static 等,因为代码总是出错) 完成此操作:

class Deck
{
private:
std::array<Card, 52> m_card;
const int m_seed {static_cast<int>(std::chrono::high_resolution_clock::now().time_since_epoch().count())};

std::mt19937 m_rng {m_seed};

int rng(int min, int max)
{
std::uniform_int_distribution<> rng{min, max};
return rng(m_rng);
}

void swapCard(Card &a, Card &b)
{
Card temp {a};
a = b;
b = temp;
}

public:

Deck()
{
int index{0};
for (int iii {0}; iii < Card::CS_MAX; ++iii)
{
for (int jjj {0}; jjj < Card::CR_MAX; ++jjj)
{
m_card[index] = Card(static_cast<Card::CardSuit>(iii), static_cast<Card::CardRank>(jjj));
++index;
}
}
}

void printDeck() const
{
for (int iii {0}; iii < 52; ++iii)
{
m_card[iii].printCard();
if (((iii + 1) % 13 == 0) && iii != 0)
std::cout << '\n';
else
std::cout << ' ';
}
}

void shuffleDeck(int xTimes = 1)
{
for (int iii {0}; iii < xTimes; ++iii)
{
for (int jjj {0}; jjj < 52; ++jjj)
{
swapCard(m_card[jjj], m_card[rng(0, 51)]);
}
}
}

};

这行得通,但我不知道这是否是正确的做法。另外,有人告诉我可以将永不更改的变量设为静态,以便在该类的所有对象之间共享,但我不能将 m_seed 设为静态...

我很确定有一种更有效的方法可以做到这一点。你们能帮忙吗?

最佳答案

I was told that it is a good practice to only seed any Random Engine once

这听起来是个不错的建议。我想补充一点,您最好每个线程只有一个生成器,因为实例化和播种需要时间,而且标准生成器不是线程安全的。

I think std::random_device is not really reliable

它应该能够告诉您它是否通过其 entropy() 函数。零熵意味着它的熵池是空的或者根本不存在。在后一种情况下,您将从中获得伪随机数。

What is the proper way ...

通过阅读评论中的链接和其他一些提示,这是我目前收集的内容:

  • 创建 SeedSequence根据生成器的需要创建尽可能多的种子值的类。如果 std::random_device 中的熵为零,请尽可能将其与其他来源结合起来。我认为间隔一段时间的散列 time_point 样本可以与 rd() 结合使用,因为输入值中的 1 个更改位理想情况下应该更改散列值中的一半位。
  • 创建一个共享生成器,在(新)线程请求时自动实例化和播种,因为生成器不是线程安全的。
  • 创建从生成器继承的分发模板,以便一个线程中的所有分发共享同一个生成器。
  • 不要过多地实例化分布。如果您经常使用同一个发行版,请保留它。

这是在代码中添加注释的尝试:

#include <iostream>
#include <chrono>
#include <climits>
#include <functional>
#include <iterator>
#include <random>
#include <thread>
#include <type_traits>
#include <utility>

//----------------------------------------------------------------------------------
// sexmex - A hash function kindly borrowed from Pelle Evensens yet to be published
// work: http://mostlymangling.blogspot.com/
//
// g++ 8.3.1: std::hash<Integer-type> lets the value through as-is (identity)
// so I'll use this to create proper hash values instead.
template<typename Out = size_t, typename In>
inline std::enable_if_t<sizeof(In) * CHAR_BIT <= 64 &&
std::numeric_limits<Out>::is_integer &&
std::numeric_limits<In>::is_integer,
Out>
sexmex(In v) {
uint64_t v2 = static_cast<uint64_t>(v); // cast away signedness
v2 ^= (v2 >> 20) ^ (v2 >> 37) ^ (v2 >> 51);
v2 *= 0xA54FF53A5F1D36F1ULL; // Fractional part of sqrt(7)
v2 ^= (v2 >> 20) ^ (v2 >> 37) ^ (v2 >> 51);
v2 *= 0x510E527FADE682D1ULL; // Fractional part of sqrt(11)
v2 ^= (v2 >> 20) ^ (v2 >> 37) ^ (v2 >> 51);
// Discard the high bits if Out is < 64 bits. This particular hash function
// has not shown any weaknesses in the lower bits in any widely known test
// suites yet.
return static_cast<Out>(v2);
}
//----------------------------------------------------------------------------------
class seeder {
public:
using result_type = std::uint_least32_t;

// function called by the generator on construction to fill its internal state
template<class RandomIt>
void generate(RandomIt Begin, RandomIt End) const noexcept {
using seed_t = std::remove_reference_t<decltype(*Begin)>;
std::random_device rd{};

if(rd.entropy() == 0.) { // check entropy
// zero entropy, add some
constexpr auto min = std::chrono::high_resolution_clock::duration::min();
std::vector<seed_t> food_for_generator(
static_cast<size_t>(std::distance(Begin, End)));

for(int stiring = 0; stiring < 10; ++stiring) {
for(auto& food : food_for_generator) {
// sleep a little to ensure a new clock count each iteration
std::this_thread::sleep_for(min);
std::this_thread::sleep_for(min);
auto cc = std::chrono::high_resolution_clock::now()
.time_since_epoch()
.count();
food ^= sexmex<seed_t>(cc);
food ^= sexmex<seed_t>(rd());
}
stir_buffer(food_for_generator);
}

// seed the generator
for(auto f = food_for_generator.begin(); Begin != End; ++f, ++Begin)
*Begin = *f;

} else {
// we got entropy, use random_device almost as-is but make sure
// values from rd() becomes seed_t's number of bits and unbiased
// via sexmex.
//
// seed the generator
for(; Begin != End; ++Begin) *Begin = sexmex<seed_t>(rd());
}
}

private:
template<typename SeedType>
inline void stir_buffer(std::vector<SeedType>& buf) const noexcept {
for(size_t i = 0; i < buf.size() * 2; ++i) {
buf[i % buf.size()] += static_cast<SeedType>(
sexmex(buf[(i + buf.size() - 1) % buf.size()] + i));
}
}
};
//----------------------------------------------------------------------------------
struct shared_generator {
// we want one instance shared between all instances of uniform_dist per thread
static thread_local seeder ss;
static thread_local std::mt19937 generator;
};

thread_local seeder shared_generator::ss{};
thread_local std::mt19937 shared_generator::generator(ss);
//----------------------------------------------------------------------------------
// a distribution template for uniform distributions, both int and real
template<typename T>
class uniform_dist : shared_generator {
public:
uniform_dist(T low, T high) : distribution(low, high) {}

// make instances callable
inline T operator()() { return distribution(generator); }

private:
template<class D>
using dist_t =
std::conditional_t<std::is_integral_v<D>, std::uniform_int_distribution<D>,
std::uniform_real_distribution<D>>;

dist_t<T> distribution;
};
//----------------------------------------------------------------------------------
void thread_func() {
uniform_dist<int> something(0, 10);
for(int i = 0; i < 10; ++i) std::cout << something() << "\n";
}

int main() {
// all distributions sharing the same generator:
uniform_dist<size_t> card_picker(0, 51);
uniform_dist<int16_t> other(-32768, 32767);
uniform_dist<float> fd(-1000.f, 1000.f);
uniform_dist<double> dd(-1., 1.);
for(int i = 0; i < 10; ++i) std::cout << card_picker() << "\n";
std::cout << "--\n";
for(int i = 0; i < 10; ++i) std::cout << other() << "\n";
std::cout << "--\n";
for(int i = 0; i < 10; ++i) std::cout << fd() << "\n";
std::cout << "--\n";
for(int i = 0; i < 10; ++i) std::cout << dd() << "\n";
// in the thread function, a new generator will be created and seeded.
std::thread t(thread_func);
t.join();
}

关于c++ - 在类中使用 std::chrono::high_resolution_clock 播种 std::mt19937 的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56242943/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com