更好的算法来随机洗牌(或交错)不同长度的多个列表
我喜欢在旅途中观看我最喜欢的电视节目。我的播放列表中包含我正在关注的每个节目的所有剧集。并非所有节目都包含相同数量的剧集。与一些喜欢马拉松的人不同,我喜欢将一个节目的剧集与另一个节目的剧集交织在一起。
例如,如果我有一个名为 ABC 的节目,有 2 集,还有一个名为 XYZ 的节目,有 4 集,我希望我的播放列表如下所示:
XYZe1.mp4
ABCe1.mp4
XYZe2.mp4
XYZe3.mp4
ABCe2.mp4
XYZe4.mp4
生成此交错播放列表的一种方法是将每个节目表示为剧集列表,并且在所有节目中进行 riffle shuffle。人们可以编写一个函数,计算每个剧集在单位时间间隔上的位置(0.0 和 1.0 之间不包括在内,0.0 是季节开始,1.0 是季节结束),然后根据位置对所有剧集进行排序。
我在 Python 2.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。我想知道您是否有更简单、更高效或只是简单优雅的解决方案。
贝利撒留提出的解决方案(谢谢!):
import itertools
def riffle_shuffle_belisarius(piles_list):
def grouper(n, iterable, fillvalue=None):
args = [iter(iterable)] * n
return itertools.izip_longest(fillvalue=fillvalue, *args)
if not piles_list:
return []
piles_list.sort(key=len, reverse=True)
width = len(piles_list[0])
pile_iters_list = [iter(pile) for pile in piles_list]
pile_sizes_list = [[pile_position] * len(pile) for pile_position, pile in enumerate(piles_list)]
grouped_rows = grouper(width, itertools.chain.from_iterable(pile_sizes_list))
grouped_columns = itertools.izip_longest(*grouped_rows)
shuffled_pile = [pile_iters_list[position].next() for position in itertools.chain.from_iterable(grouped_columns) if position is not None]
return shuffled_pile
运行示例:
>>> riffle_shuffle_belisarius([['ABCe1', 'ABCe2'], ['LMNe1', 'LMNe2', 'LMNe3'], ['XYZe1', 'XYZe2', 'XYZe3', 'XYZe4', 'XYZe5']])
['XYZe1', 'LMNe1', 'XYZe2', 'LMNe2', 'XYZe3', 'LMNe3', 'XYZe4', 'ABCe1', 'XYZe5', 'ABCe2']
I like to watch my favorite TV shows on the go. I have all episodes of each show I'm following in my playlist. Not all shows consist of the same number of episodes. Unlike some who prefer marathons, I like to interleave episodes of one show with those of another.
For example, if I have a show called ABC with 2 episodes, and a show called XYZ with 4 episodes, I would like my playlist to look like:
XYZe1.mp4
ABCe1.mp4
XYZe2.mp4
XYZe3.mp4
ABCe2.mp4
XYZe4.mp4
One way to generate this interleaved playlist is to represent each show as a list of episodes and perform a riffle shuffle on all shows. One could write a function that would compute, for each episode, its position on a unit-time interval (between 0.0 and 1.0 exclusive, 0.0 being beginning of season, 1.0 being end of season), then sort all episodes according to their position.
I wrote the following simple function in Python 2.7 to perform an 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
To get the playlist for the above example, I simply need to call:
riffle_shuffle([['ABCe1.mp4', 'ABCe2.mp4'], ['XYZe1.mp4', 'XYZe2.mp4', 'XYZe3.mp4', 'XYZe4.mp4']])
This works fairly well most of the time. However, there are cases where results are non-optimal--two adjacent entries in the playlist are episodes from the same show. For example:
>>> riffle_shuffle([['ABCe1', 'ABCe2'], ['LMNe1', 'LMNe2', 'LMNe3'], ['XYZe1', 'XYZe2', 'XYZe3', 'XYZe4', 'XYZe5']])
['XYZe1', 'LMNe1', 'ABCe1', 'XYZe2', 'XYZe3', 'LMNe2', 'XYZe4', 'ABCe2', 'LMNe3', 'XYZe5']
Notice that there are two episodes of 'XYZ' that appear side-by-side. This situation can be fixed trivially (manually swap 'ABCe1' with 'XYZe2').
I am curious to know if there are better ways to interleave, or perform riffle shuffle, on multiple lists of varying lengths. I would like to know if you have solutions that are simpler, more efficient, or just plain elegant.
Solution proposed by belisarius (thanks!):
import itertools
def riffle_shuffle_belisarius(piles_list):
def grouper(n, iterable, fillvalue=None):
args = [iter(iterable)] * n
return itertools.izip_longest(fillvalue=fillvalue, *args)
if not piles_list:
return []
piles_list.sort(key=len, reverse=True)
width = len(piles_list[0])
pile_iters_list = [iter(pile) for pile in piles_list]
pile_sizes_list = [[pile_position] * len(pile) for pile_position, pile in enumerate(piles_list)]
grouped_rows = grouper(width, itertools.chain.from_iterable(pile_sizes_list))
grouped_columns = itertools.izip_longest(*grouped_rows)
shuffled_pile = [pile_iters_list[position].next() for position in itertools.chain.from_iterable(grouped_columns) if position is not None]
return shuffled_pile
Example run:
>>> riffle_shuffle_belisarius([['ABCe1', 'ABCe2'], ['LMNe1', 'LMNe2', 'LMNe3'], ['XYZe1', 'XYZe2', 'XYZe3', 'XYZe4', 'XYZe5']])
['XYZe1', 'LMNe1', 'XYZe2', 'LMNe2', 'XYZe3', 'LMNe3', 'XYZe4', 'ABCe1', 'XYZe5', 'ABCe2']
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
确定性解决方案(即非随机)
按剧集数量的递减顺序对节目进行排序。
选择最大的并排列一个矩阵,其列数与这一集的集数相对应,按以下方式填写:
然后按列收集
然后加入
,现在分配序号
编辑
您的案例
< strong>编辑2
由于这里没有Python,也许Mathematica代码可能会有一些用处:
A deterministic solution (ie not random)
Sort your shows by decreasing number of episodes.
Select the biggest and arrange a matrix with the number of columns corresponding to the number of episodes of this one, filled in the following way:
Then collect by columns
Then Join
And now assign sequential numbers
Edit
Your case
Edit 2
As no Python here, perhaps a Mathematica code may be of some use:
我的尝试:
My try:
这将确保节目的两个连续剧集之间至少有 1 个且不超过 2 个其他剧集:
None
None
This would ensure that there is at least 1 and no more than 2 other episodes between two successive episodes of a show:
None
's at the endNone
's at the beginning[x for x in itertools.chain(zip(A,B,C)) if x is not None]
这将确保真正的随机播放,即每次都有不同的结果,并且尽可能没有连续的项目。
您询问的人可能会返回一些(1、2)个结果,但受您的请求限制。
编辑:
给定的剧集顺序是强制性的,只需简化
randpop
函数即可:This will ensure true shuffle i.e. a different result each time, with no contiguous items as much as possible.
The one you ask probably could return a few (1, 2) results limited by your requests.
Edit:
Given episodes order is mandatory just simplify
randpop
function: