【问题标题】:An Efficient way of randomizing an array - Shuffle code随机化数组的有效方法 - 随机播放代码
【发布时间】:2011-05-02 12:02:53
【问题描述】:

我在一次面试中被问到这个问题,我给出了各种解决方案,但面试官并不相信。我有兴趣找到解决方案。请发表你的看法:

问:编写一个高效的数据结构来实现 ipod 中的 shuffle。它必须播放所有歌曲,每次以不同的随机顺序播放,同一首歌曲不应重复。 (主要是 O(n))

一个解决方案,面试后我想到了:我可以在没有递归的情况下进行随机快速排序。我们随机选择 1 个枢轴 O(1),然后进行快速排序 O(n)。现在歌曲将按某种顺序排序,我将它们播放到最后。一旦到达终点,我将再次选择一个随机枢轴,并一次又一次地重复此过程。

问候, 瑟图

【问题讨论】:

    标签: data-structures random performance memory-efficient


    【解决方案1】:

    你想要的是Fisher-Yates Shuffle。这是java中的一个实现:

    public void shuffle(Song[] songs) {
        Random r = new Random();
        for(int i = 0; i < songs.length - 1; i++) {
            int swap = i + r.nextInt(songs.length-1-i);
            T temp = songs[i];
            songs[i] = songs[swap];
            songs[swap] = temp;
        }
    }
    /* r.nextInt(max) returns integer 0 to max-1 inclusive */
    

    它的工作原理是将整个数组视为一顶帽子,并开始拉出随机元素并将它们排列在数组的前面。 i 之后的所有元素都“在桶中”,i 之前的所有元素都被打乱了。

    【讨论】:

      【解决方案2】:

      我是初学者,说一个想到的解决方案,如果有什么问题请告诉我。

      让我们假设歌曲存储在单链表或双链表中。每次打开音乐播放器时,选择一个小于(您希望的任何数字)假设 k 的随机数,并反转列表中的每个 k 个节点,类似地执行两次或最多三次(如您所愿),这将花费 O( 2n) 或 O(3n) 时间洗牌。 最后有一个指向列表最后一个节点的指针。 每次听一首歌(访问一个节点)时,删除该节点并将其插入到最后一个节点旁边,这可以在 O(1) 时间内完成。 这种情况一直持续到音乐播放器关闭。

      谢谢, 急于知道答案的正确性。

      【讨论】:

        【解决方案3】:

        你想要Fisher-Yates shuffle。请注意该页面上提到的实施错误,因为您当前接受的答案与其中一个错误。

        【讨论】:

          【解决方案4】:

          Nate 的(已编辑)和 Brian 的算法是 Fisher-Yates shuffle O(n),而通过排序进行shuffle 是 O(nlogn),但实际上可能更快(http://en.wikipedia.org/wiki /Fisher%E2%80%93Yates_shuffle#Comparison_with_other_shuffling_algorithms)。弄错歌曲洗牌可能会产生微不足道的后果,但如果您正在为在线扑克游戏编写洗牌算法,请确保您知道自己在做什么(http://www.cigital.com/news/index.php?pg=艺术&artid = 20)。

          【讨论】:

            【解决方案5】:

            嗯,想到的第一个线性时间解决方案:

            你可以创建一个所有歌曲的链接列表,这大约需要 O(n) (假设插入是恒定时间操作)。然后,生成一个随机数,对列表的大小取模得到一个随机索引,然后删除该索引并将其附加到一个新列表中(两者都是常数时间操作)。

            每个 O(n) 的插入 + 每个 O(n) 的删除 + 第二次插入 O(n)。这总体上会导致线性时间解决方案。

            编辑:我完全忘记了遍历列表。因此,您可以将结果设为固定长度的数组。弹出链表的头部,为其分配随机索引,然后填充数组。

            【讨论】:

            • 几乎是我要建议的。使用 C# 列表,您会得到 RemoveAt,它会为您执行删除和收缩(不适用于 iPod,但是...)
            • 删除链表中某个索引处的元素不是恒定时间。一个元素的实际删除(意味着只是删除指向/从它的指针并释放内存)是 O(1),但因为您首先需要导航到该元素,所以在最坏的情况下它是 O(n)。跨度>
            • 这里我们要确保我们选择的随机数不重复。我遇到了同样的问题,你有什么解决办法吗?
            【解决方案6】:

            将所有歌曲放在一个数组中... 对于数组中的每个元素,将其与随机位置交换。

            【讨论】:

            • 但是在这种情况下,歌曲被重复的机会很高吧?
            • @sethu:如果你交换它就不会。就像将 ABCD 更改为 CBAD。每次交换只是将其更改为原始元素的不同排列。
            • 这是不正确的。这不会产生随机排列。这个错误很常见,在维基百科上有描述:en.wikipedia.org/wiki/…
            • @Dijkstra:脱帽致敬!你说得对。虽然在这个应用程序中它可能无关紧要。
            猜你喜欢
            • 2011-01-27
            • 2023-03-10
            • 1970-01-01
            • 2010-09-27
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多