Algorithm 如何实现重复洗牌';它是随机的,但不太随机

Algorithm 如何实现重复洗牌';它是随机的,但不太随机,algorithm,random,shuffle,Algorithm,Random,Shuffle,我有n个项目的清单。我想要一种算法,可以让我从该集合中随机选择一个潜在的无限序列的项,但有几个限制: 一旦一件物品被挑选出来,它就不应该在接下来的几件物品中再次出现(比如在接下来的m件物品中,m明显小于n) 您不必等待太长的时间来显示任何项目-项目应平均每n个选项出现一次 序列应该是非周期性的 基本上,我需要一种算法来生成MP3播放器的播放列表,并启用“shuffle”和“repeat”,以确保它不会播放太近的同一首歌曲,并确保它均匀地播放所有歌曲,没有明显的模式 这些约束消除了两个显而易见的解

我有n个项目的清单。我想要一种算法,可以让我从该集合中随机选择一个潜在的无限序列的项,但有几个限制:

  • 一旦一件物品被挑选出来,它就不应该在接下来的几件物品中再次出现(比如在接下来的m件物品中,m明显小于n)
  • 您不必等待太长的时间来显示任何项目-项目应平均每n个选项出现一次
  • 序列应该是非周期性的
  • 基本上,我需要一种算法来生成MP3播放器的播放列表,并启用“shuffle”和“repeat”,以确保它不会播放太近的同一首歌曲,并确保它均匀地播放所有歌曲,没有明显的模式

    这些约束消除了两个显而易见的解决方案:

    • 我们不能简单地选择rnd(n)作为下一项的索引,因为这不能保证没有重复;挑选一些物品也可能需要很长时间
    • 我们不能只是用Fisher-Yates shuffle预先洗牌列表,然后反复迭代,每次到达末尾都重新洗牌;虽然这保证了物品最多在2n-1次拾取后出现,但它并不能完全防止物品重复
    一个简单的解决方案可能是随机挑选,但如果挑选发生在最后m次挑选中,则拒绝挑选;这意味着保留一个m个先前选择的列表,并每次根据该列表检查每个选择,这使得算法不确定性,同时速度慢-丢失。除非我漏掉了什么明显的东西

    所以我有一个我现在使用的算法,我有点不满意。我通过类比一副牌来推导它,我有一个抽牌堆和一个弃牌堆。我从完整的列表开始,洗牌,在抽牌堆中,弃牌堆为空。下一项从绘图堆的顶部读取,然后放置在丢弃堆中。一旦弃置堆达到一定的大小(m个项目),我将其洗牌,并将其移动到绘图堆的底部

    这符合要求,但每选一个m就洗牌一次,这让我很烦恼。通常是O(1),但O(m)在m中有一次。平均而言,这相当于一个恒定的时间,但必须有一个更干净的解决方案,在丢弃的过程中将其洗掉


    在我看来,这是一个如此简单、通用和常见的问题,它必须有一个双管算法,比如Fisher-Yates或Boyer-Moore。但我的google fu显然并不强大,因为我还没有找到一组术语来定位1973年不可避免的ACM论文,这可能准确地解释了如何在O(1)时间内做到这一点,以及为什么我的算法在某些方面存在严重缺陷。

    播放给定歌曲后,使用伪附加将其放在列表的末尾。您可能希望真正添加大约1/2到2/3,而其他1/3到1/2分散在列表中最后5个左右的项目中

    显然,这对于非常短的列表不起作用,但对于10个或更多的列表应该可以


    编辑(提供有关“伪附加”的更多详细信息):

    下面的伪代码混合使用了多种语言结构,但应该很容易转化为实际代码

    给定列表[歌曲]

    While(PLAY)
        Play(List[0])
        PseudoAppend(List[], 0)
    
    def PseudoAppend(List[], index)
        # test to verify length of list, valid index, etc.
        song = List[index].delete    # < not safe
        List.append(song)
        target = -1
    
        While( (random() < (1/3)) && (target > -3) )
            Swap(List[target], List[target-1])
            target -= 1
    
    下面是一个示例输出,它在列表中运行了9次。第一列只是一个运行索引,第二列显示当前选定的歌曲,第三列显示列表的当前顺序:

    >>> ================================ RESTART ================================
    >>> 
      0 :   0 :  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
      1 :   1 :  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0]
      2 :   2 :  [2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1]
      3 :   3 :  [3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2]
      4 :   4 :  [4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3]
      5 :   5 :  [5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4]
      6 :   6 :  [6, 7, 8, 9, 10, 0, 1, 2, 3, 4, 5]
      7 :   7 :  [7, 8, 9, 10, 0, 1, 2, 3, 4, 5, 6]
      8 :   8 :  [8, 9, 10, 0, 1, 2, 3, 4, 5, 6, 7]
      9 :   9 :  [9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8]
     10 :  10 :  [10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     11 :   0 :  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
     12 :   1 :  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0]
     13 :   2 :  [2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 0]
     14 :   3 :  [3, 4, 5, 6, 7, 8, 9, 10, 1, 0, 2]
     15 :   4 :  [4, 5, 6, 7, 8, 9, 10, 1, 0, 2, 3]
     16 :   5 :  [5, 6, 7, 8, 9, 10, 1, 0, 2, 3, 4]
     17 :   6 :  [6, 7, 8, 9, 10, 1, 0, 2, 3, 4, 5]
     18 :   7 :  [7, 8, 9, 10, 1, 0, 2, 3, 4, 6, 5]
     19 :   8 :  [8, 9, 10, 1, 0, 2, 3, 4, 6, 7, 5]
     20 :   9 :  [9, 10, 1, 0, 2, 3, 4, 6, 7, 5, 8]
     21 :  10 :  [10, 1, 0, 2, 3, 4, 6, 7, 5, 8, 9]
     22 :   1 :  [1, 0, 2, 3, 4, 6, 7, 5, 10, 8, 9]
     23 :   0 :  [0, 2, 3, 4, 6, 7, 5, 10, 8, 9, 1]
     24 :   2 :  [2, 3, 4, 6, 7, 5, 10, 8, 9, 1, 0]
     25 :   3 :  [3, 4, 6, 7, 5, 10, 8, 9, 2, 1, 0]
     26 :   4 :  [4, 6, 7, 5, 10, 8, 9, 2, 1, 0, 3]
     27 :   6 :  [6, 7, 5, 10, 8, 9, 2, 1, 0, 3, 4]
     28 :   7 :  [7, 5, 10, 8, 9, 2, 1, 0, 3, 4, 6]
     29 :   5 :  [5, 10, 8, 9, 2, 1, 0, 3, 4, 6, 7]
     30 :  10 :  [10, 8, 9, 2, 1, 0, 3, 4, 5, 6, 7]
     31 :   8 :  [8, 9, 2, 1, 0, 3, 4, 5, 10, 6, 7]
     32 :   9 :  [9, 2, 1, 0, 3, 4, 5, 10, 6, 7, 8]
     33 :   2 :  [2, 1, 0, 3, 4, 5, 10, 6, 7, 9, 8]
     34 :   1 :  [1, 0, 3, 4, 5, 10, 6, 7, 9, 8, 2]
     35 :   0 :  [0, 3, 4, 5, 10, 6, 7, 9, 8, 2, 1]
     36 :   3 :  [3, 4, 5, 10, 6, 7, 9, 8, 2, 1, 0]
     37 :   4 :  [4, 5, 10, 6, 7, 9, 8, 2, 1, 0, 3]
     38 :   5 :  [5, 10, 6, 7, 9, 8, 2, 1, 0, 3, 4]
     39 :  10 :  [10, 6, 7, 9, 8, 2, 1, 0, 3, 4, 5]
     40 :   6 :  [6, 7, 9, 8, 2, 1, 0, 3, 4, 5, 10]
     41 :   7 :  [7, 9, 8, 2, 1, 0, 3, 4, 5, 10, 6]
     42 :   9 :  [9, 8, 2, 1, 0, 3, 4, 5, 7, 10, 6]
     43 :   8 :  [8, 2, 1, 0, 3, 4, 5, 7, 10, 6, 9]
     44 :   2 :  [2, 1, 0, 3, 4, 5, 7, 10, 6, 9, 8]
     45 :   1 :  [1, 0, 3, 4, 5, 7, 10, 6, 2, 9, 8]
     46 :   0 :  [0, 3, 4, 5, 7, 10, 6, 2, 9, 8, 1]
     47 :   3 :  [3, 4, 5, 7, 10, 6, 2, 9, 8, 1, 0]
     48 :   4 :  [4, 5, 7, 10, 6, 2, 9, 8, 1, 3, 0]
     49 :   5 :  [5, 7, 10, 6, 2, 9, 8, 1, 3, 0, 4]
     50 :   7 :  [7, 10, 6, 2, 9, 8, 1, 3, 5, 0, 4]
     51 :  10 :  [10, 6, 2, 9, 8, 1, 3, 5, 0, 7, 4]
     52 :   6 :  [6, 2, 9, 8, 1, 3, 5, 0, 7, 4, 10]
     53 :   2 :  [2, 9, 8, 1, 3, 5, 0, 7, 6, 4, 10]
     54 :   9 :  [9, 8, 1, 3, 5, 0, 7, 6, 4, 10, 2]
     55 :   8 :  [8, 1, 3, 5, 0, 7, 6, 4, 10, 2, 9]
     56 :   1 :  [1, 3, 5, 0, 7, 6, 4, 10, 2, 9, 8]
     57 :   3 :  [3, 5, 0, 7, 6, 4, 10, 2, 9, 1, 8]
     58 :   5 :  [5, 0, 7, 6, 4, 10, 2, 9, 3, 1, 8]
     59 :   0 :  [0, 7, 6, 4, 10, 2, 9, 3, 1, 8, 5]
     60 :   7 :  [7, 6, 4, 10, 2, 9, 3, 1, 8, 5, 0]
     61 :   6 :  [6, 4, 10, 2, 9, 3, 1, 8, 5, 0, 7]
     62 :   4 :  [4, 10, 2, 9, 3, 1, 8, 5, 0, 7, 6]
     63 :  10 :  [10, 2, 9, 3, 1, 8, 5, 0, 7, 6, 4]
     64 :   2 :  [2, 9, 3, 1, 8, 5, 0, 7, 6, 4, 10]
     65 :   9 :  [9, 3, 1, 8, 5, 0, 7, 6, 4, 10, 2]
     66 :   3 :  [3, 1, 8, 5, 0, 7, 6, 4, 10, 2, 9]
     67 :   1 :  [1, 8, 5, 0, 7, 6, 4, 10, 2, 9, 3]
     68 :   8 :  [8, 5, 0, 7, 6, 4, 10, 2, 9, 3, 1]
     69 :   5 :  [5, 0, 7, 6, 4, 10, 2, 9, 8, 3, 1]
     70 :   0 :  [0, 7, 6, 4, 10, 2, 9, 8, 3, 1, 5]
     71 :   7 :  [7, 6, 4, 10, 2, 9, 8, 3, 0, 1, 5]
     72 :   6 :  [6, 4, 10, 2, 9, 8, 3, 0, 1, 5, 7]
     73 :   4 :  [4, 10, 2, 9, 8, 3, 0, 1, 5, 7, 6]
     74 :  10 :  [10, 2, 9, 8, 3, 0, 1, 5, 7, 6, 4]
     75 :   2 :  [2, 9, 8, 3, 0, 1, 5, 7, 6, 4, 10]
     76 :   9 :  [9, 8, 3, 0, 1, 5, 7, 6, 4, 10, 2]
     77 :   8 :  [8, 3, 0, 1, 5, 7, 6, 4, 10, 2, 9]
     78 :   3 :  [3, 0, 1, 5, 7, 6, 4, 10, 2, 9, 8]
     79 :   0 :  [0, 1, 5, 7, 6, 4, 10, 2, 3, 9, 8]
     80 :   1 :  [1, 5, 7, 6, 4, 10, 2, 3, 9, 8, 0]
     81 :   5 :  [5, 7, 6, 4, 10, 2, 3, 9, 8, 1, 0]
     82 :   7 :  [7, 6, 4, 10, 2, 3, 9, 8, 1, 0, 5]
     83 :   6 :  [6, 4, 10, 2, 3, 9, 8, 1, 0, 7, 5]
     84 :   4 :  [4, 10, 2, 3, 9, 8, 1, 0, 7, 5, 6]
     85 :  10 :  [10, 2, 3, 9, 8, 1, 0, 7, 5, 6, 4]
     86 :   2 :  [2, 3, 9, 8, 1, 0, 7, 5, 6, 4, 10]
     87 :   3 :  [3, 9, 8, 1, 0, 7, 5, 6, 4, 2, 10]
     88 :   9 :  [9, 8, 1, 0, 7, 5, 6, 4, 2, 10, 3]
     89 :   8 :  [8, 1, 0, 7, 5, 6, 4, 2, 10, 3, 9]
     90 :   1 :  [1, 0, 7, 5, 6, 4, 2, 10, 8, 3, 9]
     91 :   0 :  [0, 7, 5, 6, 4, 2, 10, 8, 3, 1, 9]
     92 :   7 :  [7, 5, 6, 4, 2, 10, 8, 3, 1, 9, 0]
     93 :   5 :  [5, 6, 4, 2, 10, 8, 3, 1, 9, 0, 7]
     94 :   6 :  [6, 4, 2, 10, 8, 3, 1, 9, 0, 7, 5]
     95 :   4 :  [4, 2, 10, 8, 3, 1, 9, 0, 7, 6, 5]
     96 :   2 :  [2, 10, 8, 3, 1, 9, 0, 7, 6, 4, 5]
     97 :  10 :  [10, 8, 3, 1, 9, 0, 7, 6, 4, 5, 2]
     98 :   8 :  [8, 3, 1, 9, 0, 7, 6, 4, 5, 2, 10]
    end :   3 :  [3, 1, 9, 0, 7, 6, 4, 5, 2, 10, 8]
    

    要生成列表,请执行以下操作。先抽后弃。最初,丢弃堆是空的。从抽签堆中随机挑选第一件物品。将其添加到播放列表中,然后将其放入丢弃堆中。当丢弃堆中有m个项目时,从丢弃堆中取出最后一个项目(最近使用最少的项目),并将其添加到绘图堆中。播放列表将是随机的,但不需要随机播放

    这里是ruby:

    SONGS=[“Pink Floyd-希望你在这里”,
    “无线电头骨”,
    “Led齐柏林飞艇-黑狗”,
    “治疗-奇怪的一天”,
    “大规模攻击-泪滴”,
    “depche模式-享受宁静”,
    “威尔科-耶稣等”]
    不要重复3次
    def下一个_项目拾取、丢弃
    结果=在(rand(pick.count))处选择.delete_;
    放弃。推送结果
    如果(discard.count>不重复),则选择推(discard.shift)
    结果
    结束
    def播放列表长度为
    放弃=[]
    挑选=歌曲
    播放列表=[]
    (0..n).每个{playlist.push next_item(pick,discard)}
    播放列表
    结束
    

    编辑:添加了“播放列表长度”方法,以更清楚地显示如何调用下一个项目来生成播放列表

    我的想法是有一个要播放的卡片队列。队列被洗牌,然后一次播放一个,直到清空。当每一张牌都在玩时,如果该牌在少于m圈前玩过,则将其添加到队列的末尾,然后选择下一张牌。队列清空后,可以再次填充并重新排列。一个数组可以用来记录一张牌最后一次在哪个回合打。这平均每首歌0(1)

    这是我在F#中的解决方案

    完整的测试程序

    open System
    open System.Collections.Generic
    
    let rnd = new Random()
    
    let shuffle cards =
        let swap (a: _[]) x y =
            let tmp = a.[x]
            a.[x] <- a.[y]
            a.[y] <- tmp
    
        Array.iteri (fun i _ -> swap cards i (rnd.Next(i, Array.length cards))) cards
        cards
    
    let shuffledQueue cards =
        let queue = new Queue<_>()
    
        cards 
        |> shuffle 
        |> Array.iter (fun x -> queue.Enqueue x)
        queue
    
    let deal (deck : _[]) m =
        let played = Array.create (deck.Length) (-m)
    
        let rec subDeal (cards : Queue<_>) i = 
            seq {
                if cards.Count = 0 then
                    yield! subDeal (shuffledQueue deck) i
                else
                    let card = cards.Dequeue()
    
                    if i - played.[card] > m then
                        played.[card] <- i
                        yield card
                    else
                        cards.Enqueue card
    
                    yield! subDeal cards (i + 1)
            }
    
        subDeal (shuffledQueue deck) 1
    
    let size = 7
    let deck = Array.init size (fun i -> i)
    let cards = deal deck 4
    
    let getMaxWait seq value =
        Seq.fold (fun (last, count) test -> 
            if test = value then 
                (0, count) 
            else 
                (last + 1, max (last+1) count)
        ) (0, 0) seq
        |> snd
    
    let test = cards |> Seq.take 2000
    
    test
    |> Seq.take 200
    |> Seq.toArray
    |> printfn "%A"
    
    test
    |> Seq.countBy (fun x -> x)
    |> Seq.toArray
    |> printfn "%A"
    
    deck
    |> Seq.map (fun x -> getMaxWait test x)
    |> Seq.toArray
    |> printfn "%A"
    
    Console.ReadLine() |> ignore
    
    开放系统
    open System.Collections.Generic
    设rnd=new Random()
    让我们洗牌吧=
    让我们交换(a:[])x y=
    设tmp=a[x]
    a、 [x]洗牌
    |>Array.iter(fun x->queue.Enqueue x)
    队列
    let deal(牌组:[])m=
    让播放=数组.创建(牌组.长度)(-m)
    let rec子交易(卡:队列)i=
    序号{
    如果cards.Count=0,则
    屈服!子交易(shuffledQueue牌组)i
    其他的
    let card=cards.Dequeue()
    如果我打了。[牌]>m那么
    打过的。[牌]i)
    让牌=牌组4
    让getMaxWait seq值=
    Seq.fold(乐趣(最后一次,计数)测试->
    如果测试=值,则
    (0,计数)
    其他的
    (最后)
    
    let deal (deck : _[]) m =
        let played = Array.create (deck.Length) (-m)
    
        let rec subDeal (cards : Queue<_>) i = 
            seq {
                if cards.Count = 0 then
                    yield! subDeal (shuffledQueue deck) i
                else
                    let card = cards.Dequeue()
    
                    if i - played.[card] > m then
                        played.[card] <- i
                        yield card
                    else
                        cards.Enqueue card
    
                    yield! subDeal cards (i + 1)
            }
    
        subDeal (shuffledQueue deck) 1
    
    [|3; 1; 4; 0; 2; 6; 5; 4; 0; 2; 3; 6; 1; 5; 0; 1; 2; 6; 4; 3; 5; 2; 0; 6; 3; 4;
      5; 1; 6; 0; 3; 2; 5; 4; 1; 3; 5; 2; 0; 6; 1; 4; 2; 5; 3; 4; 0; 1; 6; 5; 2; 4;
      3; 0; 6; 1; 3; 5; 6; 2; 4; 1; 0; 5; 2; 6; 3; 1; 4; 0; 2; 6; 1; 4; 0; 5; 3; 2;
      1; 0; 5; 6; 4; 3; 2; 1; 3; 0; 5; 6; 4; 3; 1; 2; 0; 5; 6; 4; 3; 0; ...|]
    
    // card number and the number of occurrences of said card
    [|(3, 286); (6, 286); (5, 285); (0, 286); (1, 285); (4, 286); (2, 286)|]
    
    // longest time before each card is repeated
    [|11; 11; 11; 11; 12; 11; 11|]
    
    open System
    open System.Collections.Generic
    
    let rnd = new Random()
    
    let shuffle cards =
        let swap (a: _[]) x y =
            let tmp = a.[x]
            a.[x] <- a.[y]
            a.[y] <- tmp
    
        Array.iteri (fun i _ -> swap cards i (rnd.Next(i, Array.length cards))) cards
        cards
    
    let shuffledQueue cards =
        let queue = new Queue<_>()
    
        cards 
        |> shuffle 
        |> Array.iter (fun x -> queue.Enqueue x)
        queue
    
    let deal (deck : _[]) m =
        let played = Array.create (deck.Length) (-m)
    
        let rec subDeal (cards : Queue<_>) i = 
            seq {
                if cards.Count = 0 then
                    yield! subDeal (shuffledQueue deck) i
                else
                    let card = cards.Dequeue()
    
                    if i - played.[card] > m then
                        played.[card] <- i
                        yield card
                    else
                        cards.Enqueue card
    
                    yield! subDeal cards (i + 1)
            }
    
        subDeal (shuffledQueue deck) 1
    
    let size = 7
    let deck = Array.init size (fun i -> i)
    let cards = deal deck 4
    
    let getMaxWait seq value =
        Seq.fold (fun (last, count) test -> 
            if test = value then 
                (0, count) 
            else 
                (last + 1, max (last+1) count)
        ) (0, 0) seq
        |> snd
    
    let test = cards |> Seq.take 2000
    
    test
    |> Seq.take 200
    |> Seq.toArray
    |> printfn "%A"
    
    test
    |> Seq.countBy (fun x -> x)
    |> Seq.toArray
    |> printfn "%A"
    
    deck
    |> Seq.map (fun x -> getMaxWait test x)
    |> Seq.toArray
    |> printfn "%A"
    
    Console.ReadLine() |> ignore
    
    Play[themes_, minCycle_, iterations_] :=
     Module[{l, queue, played},
      l = Range[themes]; 
      queue = {};
      played = {}; (*just for accounting*)
    
      While [  Length@played < iterations,
       (AppendTo[queue, #]; l = DeleteCases[l, #]) &@RandomChoice[l];
       If[Length[queue] > minCycle, (AppendTo[l, First@queue]; queue = Rest@queue)];
       AppendTo[played, Last@queue]
       ];
      Return[played];
      ]
    
    MatrixPlot[Partition[Play[100, 50, 20000], 100], ColorFunction -> Hue]