Algorithm 如何实现重复洗牌';它是随机的,但不太随机
我有n个项目的清单。我想要一种算法,可以让我从该集合中随机选择一个潜在的无限序列的项,但有几个限制:Algorithm 如何实现重复洗牌';它是随机的,但不太随机,algorithm,random,shuffle,Algorithm,Random,Shuffle,我有n个项目的清单。我想要一种算法,可以让我从该集合中随机选择一个潜在的无限序列的项,但有几个限制: 一旦一件物品被挑选出来,它就不应该在接下来的几件物品中再次出现(比如在接下来的m件物品中,m明显小于n) 您不必等待太长的时间来显示任何项目-项目应平均每n个选项出现一次 序列应该是非周期性的 基本上,我需要一种算法来生成MP3播放器的播放列表,并启用“shuffle”和“repeat”,以确保它不会播放太近的同一首歌曲,并确保它均匀地播放所有歌曲,没有明显的模式 这些约束消除了两个显而易见的解
- 我们不能简单地选择rnd(n)作为下一项的索引,因为这不能保证没有重复;挑选一些物品也可能需要很长时间
- 我们不能只是用Fisher-Yates shuffle预先洗牌列表,然后反复迭代,每次到达末尾都重新洗牌;虽然这保证了物品最多在2n-1次拾取后出现,但它并不能完全防止物品重复李>
在我看来,这是一个如此简单、通用和常见的问题,它必须有一个双管算法,比如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]