C++ & Qt:不要在 x 分钟内选择一个随机数
C++ & Qt: Don't pick a random number for x minutes
假设我有 10 个数字,从 1 到 10,并且 rand() 选择了数字 3。该数字将显示在我的屏幕上,但在它显示后的 15 分钟内不会显示已选。我该怎么做?
我知道 srand 应该用于更好的随机结果,但这不是我想要实现的。我想要一个特定的数字显示一次,但之后它要等到 15 分钟后才会出现。
我读过一些关于睡眠的文章,但我还没有弄清楚它是如何工作的,以及它是否适合我的问题。
您需要有一个 table 可以存储对 (time, number)
。当您准备好生成新号码时,您需要:
从 table.
中转储太旧的值
生成一个 table 中没有的随机数。这可以通过以下任一方式完成:
正在生成一个数字,检查它是否在 table 中,如果是则重试。
从较小的范围生成一个数字,并将其映射到较大的范围,该范围已从中删除 table 中存在的数字。假设 table 有 10 个条目,而您的完整范围是 1-50。然后您将生成 1-40 范围内的数字,因为 table 中的 10 个数字是禁止输入的。然后你需要做映射。
将 (current time, number from #2 above)
添加到 table。
类似下面的内容可能是一个起点:
class Generator {
QMap<QDateTime, int> m_map;
QSet<int> m_set;
public:
int generate();
}
int Generator::generate() {
// Remove stale values
auto cutoff = QDateTime::currentDateTime().addSecs(-15*60);
auto it = m_map.begin(); // The map is sorted in ascending time order
while (it != m_map.end() && it.key() < cutoff) {
m_set.remove(it.value());
it = m_map.erase(it);
}
// Ensure that we have some values left to find
Q_ASSERT(m_set.size() < 10);
// Generate a new value
int result;
while (true) {
result = 1 + rand() % 10; // use C++11 random number generator instead!
if (!m_set.contains(result)) break;
}
// Add it back
m_set.insert(result);
m_map.insert(QDateTime::currentDateTime(), result);
return result;
}
// Look Ma, no timers! :)
一个简单的方法:记录号码的选择和时间。
所以每次要显示一个数字:
- 检查记录并删除任何超过 15 分钟的条目。
- 在 1 到 10 之间选择一个数字。
- 查看记录以查看所选号码是否在最近十五分钟内被使用过。如果是,则返回第一步。
- 由于选择的号码在过去15分钟内没有被使用过,所以显示该号码。
- 为所选号码和当前时间的记录添加一个新条目。
请注意,显然,如果您在不到 15 分钟的时间内选出 9 个号码,并且在这 15 分钟内也即将选出第 10 个号码,则结果根本不是随机的。关键是,在这种约束下,随着在 15 分钟内选择更多数字 window,结果随机性降低,直到没有随机性。
此外,在 15 分钟内选择了 10 个号码后 window,没有剩余号码,因此尝试查找未被选择的号码的循环将一直旋转,直到获得一些记录删除。然后它将不得不选择任何可用的数字。这可能意味着每 15 分钟将重复使用相同的数字序列。
srand()
不是用于生成比 rand()
更好的随机数据的函数。这两个函数协同工作:srand()
设置用于生成随机序列的种子值,而 rand()
是实际生成值的值。 srand()
不会产生更好的值,也不会做任何事情使 rand()
产生 'better' 值。它只允许您 select 一个 不同的 值序列。
如果您想要更好的东西,请查看 C++ <random>
header 或者 third-party library.
C++ <random>
header 的真正好处是分布:不必自己弄清楚如何生成随机数(通常使用 %
,这几乎总是一个坏主意),你可以简单地使用例如std::uniform_int_distribution<>(1, 10)
这将正确生成 1 到 10 之间的数字。
#include <iostream>
#include <random>
int main() {
// prepare a reasonable source of randomness
std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r()};
std::mt19937 pRNG(seed);
// produce 50 random values between one and ten
std::uniform_int_distribution<int> one_to_ten(1, 10);
for (int i = 0; i < 50; ++i) {
std::cout << one_to_ten(pRNG) << "\n";
}
}
假设我有 10 个数字,从 1 到 10,并且 rand() 选择了数字 3。该数字将显示在我的屏幕上,但在它显示后的 15 分钟内不会显示已选。我该怎么做?
我知道 srand 应该用于更好的随机结果,但这不是我想要实现的。我想要一个特定的数字显示一次,但之后它要等到 15 分钟后才会出现。 我读过一些关于睡眠的文章,但我还没有弄清楚它是如何工作的,以及它是否适合我的问题。
您需要有一个 table 可以存储对 (time, number)
。当您准备好生成新号码时,您需要:
从 table.
中转储太旧的值
生成一个 table 中没有的随机数。这可以通过以下任一方式完成:
正在生成一个数字,检查它是否在 table 中,如果是则重试。
从较小的范围生成一个数字,并将其映射到较大的范围,该范围已从中删除 table 中存在的数字。假设 table 有 10 个条目,而您的完整范围是 1-50。然后您将生成 1-40 范围内的数字,因为 table 中的 10 个数字是禁止输入的。然后你需要做映射。
将
(current time, number from #2 above)
添加到 table。
类似下面的内容可能是一个起点:
class Generator {
QMap<QDateTime, int> m_map;
QSet<int> m_set;
public:
int generate();
}
int Generator::generate() {
// Remove stale values
auto cutoff = QDateTime::currentDateTime().addSecs(-15*60);
auto it = m_map.begin(); // The map is sorted in ascending time order
while (it != m_map.end() && it.key() < cutoff) {
m_set.remove(it.value());
it = m_map.erase(it);
}
// Ensure that we have some values left to find
Q_ASSERT(m_set.size() < 10);
// Generate a new value
int result;
while (true) {
result = 1 + rand() % 10; // use C++11 random number generator instead!
if (!m_set.contains(result)) break;
}
// Add it back
m_set.insert(result);
m_map.insert(QDateTime::currentDateTime(), result);
return result;
}
// Look Ma, no timers! :)
一个简单的方法:记录号码的选择和时间。
所以每次要显示一个数字:
- 检查记录并删除任何超过 15 分钟的条目。
- 在 1 到 10 之间选择一个数字。
- 查看记录以查看所选号码是否在最近十五分钟内被使用过。如果是,则返回第一步。
- 由于选择的号码在过去15分钟内没有被使用过,所以显示该号码。
- 为所选号码和当前时间的记录添加一个新条目。
请注意,显然,如果您在不到 15 分钟的时间内选出 9 个号码,并且在这 15 分钟内也即将选出第 10 个号码,则结果根本不是随机的。关键是,在这种约束下,随着在 15 分钟内选择更多数字 window,结果随机性降低,直到没有随机性。
此外,在 15 分钟内选择了 10 个号码后 window,没有剩余号码,因此尝试查找未被选择的号码的循环将一直旋转,直到获得一些记录删除。然后它将不得不选择任何可用的数字。这可能意味着每 15 分钟将重复使用相同的数字序列。
srand()
不是用于生成比 rand()
更好的随机数据的函数。这两个函数协同工作:srand()
设置用于生成随机序列的种子值,而 rand()
是实际生成值的值。 srand()
不会产生更好的值,也不会做任何事情使 rand()
产生 'better' 值。它只允许您 select 一个 不同的 值序列。
如果您想要更好的东西,请查看 C++ <random>
header 或者 third-party library.
C++ <random>
header 的真正好处是分布:不必自己弄清楚如何生成随机数(通常使用 %
,这几乎总是一个坏主意),你可以简单地使用例如std::uniform_int_distribution<>(1, 10)
这将正确生成 1 到 10 之间的数字。
#include <iostream>
#include <random>
int main() {
// prepare a reasonable source of randomness
std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r()};
std::mt19937 pRNG(seed);
// produce 50 random values between one and ten
std::uniform_int_distribution<int> one_to_ten(1, 10);
for (int i = 0; i < 50; ++i) {
std::cout << one_to_ten(pRNG) << "\n";
}
}