【问题标题】:Get a random element in single direction linked list by one time traverse一次遍历获取单向链表中的随机元素
【发布时间】:2011-04-29 07:17:59
【问题描述】:

我有一个单向链表,但不知道它的大小。

我想在这个列表中得到一个随机元素,我只有一次机会遍历这个列表。 (我不允许遍历两次或多次)

这个问题的算法是什么?谢谢!

【问题讨论】:

    标签: algorithm random traversal


    【解决方案1】:

    这只是 reservoir sampling,水库大小为 1。

    其实很简单

    1. 无论如何都选择第一个元素(对于长度为 1 的列表,第一个元素始终是样本)。
    2. 对于概率为 1/n 的每个其他元素,其中 n 是到目前为止观察到的元素数,您将已选择的元素替换为当前所在的元素。

    这是均匀抽样的,因为在一天结束时选择任何元素的概率是 1/n(读者练习)。

    【讨论】:

    • @Giacomon 您是否有理由认为这不适用于小型收藏。我理解问题是提供一个在线统一采样算法,我认为这很合适
    • @Aurojit:我认为 Giacomo 只是在说这种解决方案对大型和小型收藏都有好处。
    • @Giacomo 谢谢,我并不是真的在抱怨你的评论,我只是想找出问题所在,如果它被认为是苛刻的,对不起
    • 真的是1/n概率吗?取最后一个元素:它将有 1/2 的机会被选中(在最后一步中),其余元素的总和为 1/2。这对我来说似乎不对。
    • @iuliux 为什么它的概率是1/2。元素本身有 1/n 的机会被选中。如果未选择,则任何选择的元素都以 (n - 1)/n 的概率存活。 (1/n-1) = 1/n。因此均匀概率。
    【解决方案2】:

    这可能是一个面试问题。数据科学家使用水库抽样从大量数据流中将相关数据存储在有限的存储空间中。

    如果你必须从任何元素 n 的数组中收集 k 个元素,这样你收集到的每个元素的概率应该是相同的 (k/n),你遵循两个步骤,

    1) 在存储中存储前 k 个元素。 2)当下一个元素(k + 1)来自流时,显然你的集合中没有空间了。生成一个从o到n的随机数,如果生成的随机数小于k,假设l,替换storage[l ] 与流中的 (k+1) 个元素。

    现在,回到你的问题,这里的存储大小是 1。所以你将选择第一个节点,迭代列表中的第二个元素。现在生成随机数,如果它是 1,则不理会样本,否则切换列表中的存储元素

    【讨论】:

      【解决方案3】:

      这个问题可以使用水库采样来完成。它基于从 n 个项目中选择 k 个随机项目,但这里的 n 可能非常大(不一定要放入内存!)并且(如您的情况)最初是未知的。

      维基百科有一个可以理解的算法,我在下面引用:

      array R[k];    // result
      integer i, j;
      
      // fill the reservoir array
      for each i in 1 to k do
          R[i] := S[i]
      done;
      
      // replace elements with gradually decreasing probability
      for each i in k+1 to length(S) do
          j := random(1, i);   // important: inclusive range
          if j <= k then
              R[j] := S[i]
          fi
      done
      

      这个问题只需要 1 个值,所以我们取 k=1。

      C 实现:

      https://ideone.com/txnsas

      【讨论】:

        【解决方案4】:

        这是我找到的最简单的方法,效果很好并且可以理解:

        public int findrandom(Node start) {
            Node curr = start;
            int count = 1, result = 0, probability;
            Random rand = new Random();
        
            while (curr != null) {
                probability = rand.nextInt(count) + 1;
                if (count == probability)
                    result = curr.data;
                count++;
                curr = curr.next;
            }
            return result;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-09-28
          • 2012-08-08
          • 2018-11-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多