【问题标题】:Random number generator that fills an interval填充区间的随机数生成器
【发布时间】:2011-01-17 20:28:25
【问题描述】:

你将如何实现一个随机数生成器,给定一个区间,(随机)生成该区间内的所有数字,而不需要任何重复?

它应该消耗尽可能少的时间和内存。

刚刚发明的 C#-ruby-ish 伪代码中的示例:

interval = new Interval(0,9)
rg = new RandomGenerator(interval);
count = interval.Count // equals 10
count.times.do{
    print rg.GetNext() + " "
}

这应该输出类似:

1 4 3 2 7 5 0 9 8 6 

【问题讨论】:

    标签: algorithm language-agnostic random


    【解决方案1】:

    用区间填充一个数组,然后打乱它。

    洗牌 N 个元素的数组的标准方法是在 0 和 N-1 之间选择一个随机数(比如 R),然后将 item[R] 与 item[N] 交换。然后从 N 中减去 1,然后重复直到 N =1。

    【讨论】:

    【解决方案2】:

    一个建议,但它占用大量内存:

    生成器构建区间内所有数字的列表,然后对其进行打乱。

    【讨论】:

      【解决方案3】:
      1. 取出区间内的所有数字,放入列表/数组中
      2. Shuffle列表/数组
      3. 循环遍历列表/数组

      【讨论】:

        【解决方案4】:

        一种方法是在您的示例中生成有序列表 (0-9)。

        然后使用随机函数从列表中选择一个项目。从原始列表中删除该项目并将其添加到新列表的尾部。

        当原始列表为空时,该过程结束。

        输出新列表。

        【讨论】:

          【解决方案5】:

          一个偶尔有用的替代随机播放的方法是使用一个可下标的集合容器。在每一步,选择一个随机数 0

          主要问题是典型的容器不能有效地处理这个问题。我已经将它与位向量一起使用,但它只有在可能的最大成员相当小的情况下才能正常工作,因为需要对位向量进行线性扫描才能找到第 n 个集合位。

          99% 的情况下,最好的方法是按照其他人的建议进行随机播放。

          编辑

          我错过了一个简单的数组是一个很好的“集合”数据结构这一事实——不要问我为什么,我以前用过它。 “诀窍”是您不关心数组中的项目是否已排序。在每个步骤中,您随机选择一个并提取它。要填充空槽(无需将平均一半的项目向下移动一步),您只需在恒定时间内将当前最终项目移动到空槽中,然后将数组的大小减小一。

          例如...

          class remaining_items_queue
          {
            private:
              std::vector<int>  m_Items;
          
            public:
              ...
          
              bool Extract (int &p_Item);  //  return false if items already exhausted
          };
          
          bool remaining_items_queue::Extract (int &p_Item)
          {
            if (m_Items.size () == 0)  return false;
          
            int l_Random = Random_Num (m_Items.size ());
              //  Random_Num written to give 0 <= result < parameter
          
            p_Item = m_Items [l_Random];
          
            m_Items [l_Random] = m_Items.back ();
            m_Items.pop_back ();
          }
          

          诀窍是获得一个随机数生成器,它给出(具有合理均匀分布的)数字,范围在 0 到 n-1 之间,其中 n 每次都可能不同。大多数标准随机发生器给出一个固定的范围。尽管以下给出了均匀分布,但它通常足够好......

          int Random_Num (int p)
          {
            return (std::rand () % p);
          }
          

          std::rand 返回 0

          【讨论】:

            【解决方案6】:

            图像处理是一种非常有效的方法来打乱一组数字,其中每个索引都是唯一的,并且在应用像素溶解等技术时使用。

            基本上,您从一个有序的二维数组开始,然后移动列和行。顺便说一下,这些排列很容易实现,您甚至可以使用一种精确的方法在 n 次排列后在 x,y 处产生结果值。

            在 3x3 网格上描述的基本技术:

            1) 以有序列表开头,每个数字只能存在一次

            0 1 2
            3 4 5
            6 7 8
            

            2) 选择要随机播放的行/列,将其推进一步。在这种情况下,我将第二行向右移动。

            0 1 2
            5 3 4
            6 7 8
            

            3) 选择一个你想洗牌的行/列...我将第二列洗牌。

            0 7 2
            5 1 4
            6 3 8
            

            4) 选择...例如,第一行,左边一个。

            2 0 7
            5 1 4
            6 3 8
            

            您可以根据需要多次重复这些步骤。您也总是可以在一维数组上进行这种转换。所以你的结果现在是 [2, 0, 7, 5, 1, 4, 6, 3, 8]。

            【讨论】:

              【解决方案7】:

              这已经出现过。尝试使用linearfeedbackshiftregister

              【讨论】:

                【解决方案8】:

                您可以使用带有随机选择参数的linear congruential generator,但这样它会生成完整的周期。您需要小心,因为随机数的质量可能很差,具体取决于参数。

                【讨论】:

                • 如何确保 LCG 覆盖区间内的所有数字?例如维基百科示例 x(n+1) = 3*x(n) + 1 不...
                • 你的例子不符合en.wikipedia.org/wiki/…的条件3
                • 是的,你是对的;我没有想到,在 LCG 中,一旦你重复一个数字,你就会从序列开始。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2013-12-29
                • 1970-01-01
                • 1970-01-01
                • 2023-01-03
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多