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)。当您准备好生成新号码时,您需要:

  1. 从 table.

  2. 中转储太旧的值
  3. 生成一个 table 中没有的随机数。这可以通过以下任一方式完成:

    • 正在生成一个数字,检查它是否在 table 中,如果是则重试。

    • 从较小的范围生成一个数字,并将其映射到较大的范围,该范围已从中删除 table 中存在的数字。假设 table 有 10 个条目,而您的完整范围是 1-50。然后您将生成 1-40 范围内的数字,因为 table 中的 10 个数字是禁止输入的。然后你需要做映射。

  4. (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! :)

一个简单的方法:记录号码的选择和时间。

所以每次要显示一个数字:

  1. 检查记录并删除任何超过 15 分钟的条目。
  2. 在 1 到 10 之间选择一个数字。
  3. 查看记录以查看所选号码是否在最近十五分钟内被使用过。如果是,则返回第一步。
  4. 由于选择的号码在过去15分钟内没有被使用过,所以显示该号码。
  5. 为所选号码和当前时间的记录添加一个新条目。

请注意,显然,如果您在不到 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";
  }
}