【问题标题】:C++ How to generate 10,000 UNIQUE random integers to store in a BST?C++ 如何生成 10,000 个唯一随机整数以存储在 BST 中?
【发布时间】:2019-03-24 19:37:45
【问题描述】:

我正在尝试生成 10,000 个 1 到 20,000 范围内的唯一随机整数以存储在 BST 中,但不确定执行此操作的最佳方法。

我看到了一些关于如何使用数组或向量的好建议,但不适用于 BST。我有一个contains 方法,但我不相信它会在这种情况下工作,因为它用于搜索并返回关于找到所需数字需要多少次尝试的结果。下面是我得到的最接近的,但它不喜欢我的 == 运算符。使用数组并将数组存储在 BST 中会更好吗?或者有没有更好的方法来使用下面的代码,以便在生成数字时将它们直接存储在树中?

for (int i = 0; i < 10000; i++) 
{
    int random = rand() % 20000;
    tree1Ptr->add(random);
    for (int j = 0; j < i; j++) {
        if (tree1Ptr[j]==random) i--;
        }
    }

【问题讨论】:

  • en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle 唯一的数字几乎按照定义不是随机的。
  • 这对我来说似乎是两个不同的问题。插入二叉搜索树并创建不在树中的数字似乎无关。你使用什么类型的容器应该不会有什么不同。
  • 无论集合是否包含 x,collection.contains(x) 方法不应该返回一个二进制值吗?您的变体没有什么意义。
  • " 它不喜欢我的 == 运算符" 是那个还是 operator[] 上的 tree1Ptr 是什么?

标签: c++ random binary-search-tree unique random-seed


【解决方案1】:

您的代码中有几个问题。但是,让我们直接进入痛点。

主要问题是什么?

从您的代码中,很明显tree1Ptr 是一个指针。原则上应该指向树的一个节点,它有两个指针,一个指向左节点,一个指向右节点。

所以在你的代码中,你应该有:

tree1Ptr = new Node;   // or whatever the type of your node is called

然而,在你的内部循环中,你只是把它当作一个数组来使用:

for (int i = 0; i < 10000; i++) 
{
    int random = rand() % 20000;
    tree1Ptr->add(random);
    for (int j = 0; j < i; j++) {
        if (tree1Ptr[j]==random)  //<============ OUCH !!
            i--;
    }
}

编译器不会抱怨,因为它是有效的语法:您可以在指针上使用数组索引。但是你要确保你不会越界(所以在这里,j 保持

其他说明

顺便说一下,在内部循环中,你只想说如果找到数字就必须重试。如果号码已经找到,您可以break 内循环,以免继续。

您还应该为随机数生成器播种,以避免始终以相同的顺序运行程序。

如何解决?

您确实需要加深对 BST 的理解。导航节点需要与当前节点中的值进行比较,并根据结果,使用左指针或右指针继续迭代,而不使用索引。但是在这里解释太长了。所以也许你应该找一个教程,比如this one

【讨论】:

    【解决方案2】:

    对于很多独特的“随机”数字,我通常使用Format Preserving Encryption。由于加密是一对一的,因此只要输入是唯一的,就可以保证输出的唯一性。不同的加密密钥将生成一组不同的输出,即输入的不同排列。只需加密 0, 1, 2, 3, 4, ... 并保证输出是唯一的。

    您需要 [1 .. 20,000] 范围内的数字。不幸的是,20,000 需要 21 位,并且大多数加密方案都有偶数位:在您的情况下为 22 位。这意味着您将需要骑自行车步行;如果数字太大,则重新加密输出,直到获得所需范围内的数字。由于您的输入最多只能达到 10,000,而您将在 20,000 以上骑自行车行走,您仍然可以避免重复。

    我所知道的唯一允许 22 位块大小的标准密码是 Hasty Pudding 密码。或者,编写自己的简单Feistel cipher 也很容易。如果您不想要加密安全,四轮就足够了。对于加密级别的安全性,您需要使用 NIST 批准的 AES/FFX。

    【讨论】:

      【解决方案3】:

      您可以通过两种方式从序列中选择随机唯一数字,而无需检查之前选择的数字(即已经在您的 BST 中)。

      使用 random_shuffle

      一个简单的方法是打乱一个 1 ... 20,000 的排序数组,然后简单地挑选前 10,000 个项目:

      #include <algorithm>
      #include <vector>
      
      std::vector<int> values(20000);
      for (int i = 0; i < 20000; ++i) {
        values[i] = i+1;
      }
      std::random_shuffle(values.begin(), values.end());
      
      for (int i = 0; i < 10000; ++i) {
        // Insert values[i] into your BST
      }
      

      如果要挑选的随机数 (10,000) 的大小与总数 (20,000) 的大小相当,则此方法效果很好,因为随机洗牌的复杂性在更大的结果集上分摊。

      使用 uniform_int_distribution

      如果要选择的随机数的大小远小于总数的大小,则可以使用另一种方法:

      #include <chrono>
      #include <random>
      #include <vector>
      
      // Use timed seed so every run produces different random picks.
      std::default_random_engine reng(
          std::chrono::steady_clock::now().time_since_epoch().count());
      
      int num_pick  = 1000;   // # of random numbers remained to pick
      int num_total = 20000;  // Total # of numbers to pick from
      
      int cur_value = 1;  // Current prospective number to be picked
      while (num_pick > 0) {
        // Probability to pick `cur_value` is num_pick / (num_total-cur_value+1)
        std::uniform_int_distribution<int> distrib(0, num_total-cur_value);
      
        if (distrib(reng) < num_pick) {
          bst.insert(cur_value);  // insert `cur_value` to your BST
          --num_pick;
        }
        ++cur_value;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多