Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 更好的算法,以riffle洗牌(或交织)多个不同长度的列表_Python_Algorithm_Language Agnostic_Shuffle - Fatal编程技术网

Python 更好的算法,以riffle洗牌(或交织)多个不同长度的列表

Python 更好的算法,以riffle洗牌(或交织)多个不同长度的列表,python,algorithm,language-agnostic,shuffle,Python,Algorithm,Language Agnostic,Shuffle,我喜欢边走边看我最喜欢的电视节目。我的播放列表中有我关注的每个节目的所有剧集。并非所有节目都有相同数量的剧集。与一些喜欢马拉松的人不同,我喜欢将一个节目的插曲与另一个节目的插曲交织在一起 例如,如果我有一个名为ABC的节目有2集,一个名为XYZ的节目有4集,我希望我的播放列表如下所示: XYZe1.mp4 ABCe1.mp4 XYZe2.mp4 XYZe3.mp4 ABCe2.mp4 XYZe4.mp4 生成此交错播放列表的一种方法是将每个节目表示为一个剧集列表,并在所有节目上执行即兴播放。可

我喜欢边走边看我最喜欢的电视节目。我的播放列表中有我关注的每个节目的所有剧集。并非所有节目都有相同数量的剧集。与一些喜欢马拉松的人不同,我喜欢将一个节目的插曲与另一个节目的插曲交织在一起

例如,如果我有一个名为ABC的节目有2集,一个名为XYZ的节目有4集,我希望我的播放列表如下所示:

XYZe1.mp4
ABCe1.mp4
XYZe2.mp4
XYZe3.mp4
ABCe2.mp4
XYZe4.mp4
生成此交错播放列表的一种方法是将每个节目表示为一个剧集列表,并在所有节目上执行即兴播放。可以编写一个函数,计算每一集在单位时间间隔内的位置(0.0到1.0之间互斥,0.0表示季初,1.0表示季末),然后根据位置对所有集进行排序

我在Python2.7中编写了以下简单函数来执行in-shuffle:

def riffle_shuffle(piles_list):
    scored_pile = ((((item_position + 0.5) / len(pile), len(piles_list) - pile_position), item) for pile_position, pile in enumerate(piles_list) for item_position, item in enumerate(pile))
    shuffled_pile = [item for score, item in sorted(scored_pile)]
    return shuffled_pile
要获取上述示例的播放列表,我只需调用:

riffle_shuffle([['ABCe1.mp4', 'ABCe2.mp4'], ['XYZe1.mp4', 'XYZe2.mp4', 'XYZe3.mp4', 'XYZe4.mp4']])
这在大多数情况下都相当有效。然而,在某些情况下,结果是非最佳的——播放列表中的两个相邻条目是来自同一个节目的片段。例如:

>>> riffle_shuffle([['ABCe1', 'ABCe2'], ['LMNe1', 'LMNe2', 'LMNe3'], ['XYZe1', 'XYZe2', 'XYZe3', 'XYZe4', 'XYZe5']])
['XYZe1', 'LMNe1', 'ABCe1', 'XYZe2', 'XYZe3', 'LMNe2', 'XYZe4', 'ABCe2', 'LMNe3', 'XYZe5']
请注意,“XYZ”有两集并排出现。这种情况可以简单地解决(手动将'ABCe1'与'XYZe2'交换)

我很想知道是否有更好的方法在不同长度的多个列表上交错或执行riffle-shuffle。我想知道你们是否有更简单、更高效或简单优雅的解决方案


belisarius提出的解决方案(谢谢!):

运行示例:

>>> riffle_shuffle_belisarius([['ABCe1', 'ABCe2'], ['LMNe1', 'LMNe2', 'LMNe3'], ['XYZe1', 'XYZe2', 'XYZe3', 'XYZe4', 'XYZe5']])
['XYZe1', 'LMNe1', 'XYZe2', 'LMNe2', 'XYZe3', 'LMNe3', 'XYZe4', 'ABCe1', 'XYZe5', 'ABCe2']

确定性解决方案(即非随机)

通过减少剧集数量对节目进行排序

选择最大的一个,排列一个矩阵,列数对应于这一集的集数,按以下方式填充:

A   A   A   A   A   A  <- First show consist of 6 episodes
B   B   B   B   C   C  <- Second and third show - 4 episodes each
C   C   D   D          <- Third show 2 episodes
然后加入

{A,B,C,A,B,C,A,B,D,A,B,D,A,C,A,C}
现在分配序列号

{A1, B1, C1, A2, B2, C2, A3, B3, D1, A4, B4, D2, A5, C3, A6, C4}
编辑

你的案子

[['A'] * 2, ['L'] * 3, ['X'] * 5])

X  X  X  X  X
L  L  L  A  A

-> {X1, L1, X2, L2, X3, L3, X4, A1, X5, A2}
编辑2

由于这里没有Python,Mathematica代码可能有一些用处:

l = {, , ,};                                 (* Prepare input *)
l[[1]] = {a, a, a, a, a, a};
l[[2]] = {b, b, b, b};
l[[3]] = {c, c, c, c};
l[[4]] = {d, d};
le = Length@First@l;

k = DeleteCases[                              (*Make the matrix*)
   Flatten@Transpose@Partition[Flatten[l], le, le, 1, {Null}], Null];

Table[r[i] = 1, {i, k}];                      (*init counters*)
ReplaceAll[#, x_ :> x[r[x]++]] & /@ k         (*assign numbers*)

->{a[1], b[1], c[1], a[2], b[2], c[2], a[3], b[3], d[1], a[4], b[4], 
   d[2], a[5], c[3], a[6], c[4]}
我的尝试:

program, play = [['ABCe1.mp4', 'ABCe2.mp4'], 
                 ['XYZe1.mp4', 'XYZe2.mp4', 'XYZe3.mp4', 'XYZe4.mp4', 
                  'XYZe5.mp4', 'XYZe6.mp4', 'XYZe7.mp4'],
                 ['OTHERe1.mp4', 'OTHERe2.mp4']], []
start_part = 3
while any(program):
    m = max(program, key = len)
    if (len(play) >1 and 
        play[-1][:start_part] != m[0][:start_part] and 
        play[-2].startswith(play[-1][:start_part])):
        play.insert(-1, m.pop(0))
    else:
        play.append(m.pop(0))

print play

这将确保真正的洗牌,即每次都有不同的结果,尽可能没有连续项

你问的那个可能会返回一些(1,2)结果,这些结果受你的请求限制

from random import choice, randint
from operator import add

def randpop(playlists):
    pl = choice(playlists)
    el = pl.pop(randint(0, len(pl) -1))
    return pl, el

def shuffle(playlists):
    curr_pl = None
    while any(playlists):
        try:
            curr_pl, el = randpop([pl for pl in playlists if pl and pl != curr_pl])
        except IndexError:
            break
        else:
            yield el
    for el in reduce(add, playlists):
        yield el
    raise StopIteration

if __name__ == "__main__":
    sample = [
        'A1 A2 A3 A4'.split(),
        'B1 B2 B3 B4 B5'.split(),
        'X1 X2 X3 X4 X5 X6'.split()
        ]
    for el in shuffle(sample):
        print(el)
编辑:

给定的剧集顺序是强制性的,只需简化
randpop
功能:

def randpop(playlists):
    pl = choice(playlists)
    el = pl.pop(0)
    return pl, el

这将确保在一个节目的两个连续剧集之间至少有一个且不超过两个其他剧集:

  • 虽然有3个以上的节目,但将两个最短(即插曲最少)的节目端到端连在一起
  • 让A是最长的演出,B和C是另外两个
  • 如果B比A短,则在其末尾填充
    None
  • 如果C比A短,则在开始处用
    None
    填充它
  • 洗牌播放列表是
    [itertools.chain(zip(A,B,C))中的x代表x,如果x不是None]

一个问题是,如果一个系列(如“XYZ”)的项目比所有其他系列多(两)个,则不可能没有并排出现。@ypercube:事实上,当一个列表的项目数是所有其他列表总和的两倍以上时,将有属于同一列表的相邻项目。然而,通常情况并非如此。我正在寻找能更好地避免这种邻接问题的算法。你想要随机的吗?你接受确定性的解决方案吗?@belisarius:两者都可以,只要每个列表中的项目(每个节目的片段)在最终播放列表中按顺序出现。我不想在第五集之前看第六集@类似《星球大战》的阿德尔:)这种算法绝对避免了邻接问题。然而,它确实倾向于将情节组合在一起。总的来说,我喜欢它我将在这个问题中编辑一个Python实现。谢谢@Adeel使用相同的算法,你可能会得到一系列不同的结果,方法是选择的列数大于最大节目的集数(但不到所有节目总集数的一半)有趣的想法。然而,由于情节的随机选择,通常一个节目的最近一集出现在同一节目的过去一集之前。同一节目的所有剧集都需要按顺序出现在最终播放列表中。我可能对此含糊其辞。不过,为了澄清这一点,我编辑了我的问题。谢谢。在我写的时候,我不会处理事件的排序,但是在<代码>列表(Sffffle(样本))< /代码>中,很容易在最近和过去之间交换。欢迎您,如果您觉得合适的话,请考虑接受我的答案。Cheers我看到你的算法从剩余最长的列表中弹出项目,直到它们全部消失。很不错的。谢谢至少它很简单。不确定它是否能正常工作,如果最长的一个长得多,固定得很少,以避免混淆顺序,但首先从最长的顺序取零件。
from random import choice, randint
from operator import add

def randpop(playlists):
    pl = choice(playlists)
    el = pl.pop(randint(0, len(pl) -1))
    return pl, el

def shuffle(playlists):
    curr_pl = None
    while any(playlists):
        try:
            curr_pl, el = randpop([pl for pl in playlists if pl and pl != curr_pl])
        except IndexError:
            break
        else:
            yield el
    for el in reduce(add, playlists):
        yield el
    raise StopIteration

if __name__ == "__main__":
    sample = [
        'A1 A2 A3 A4'.split(),
        'B1 B2 B3 B4 B5'.split(),
        'X1 X2 X3 X4 X5 X6'.split()
        ]
    for el in shuffle(sample):
        print(el)
def randpop(playlists):
    pl = choice(playlists)
    el = pl.pop(0)
    return pl, el