Мне нравится смотреть мои любимые сериалы на ходу. У меня есть все эпизоды каждого шоу, на которое я смотрю, в моем плейлисте. Не все шоу состоят из одинакового количества эпизодов. В отличие от тех, кто предпочитает марафоны, мне нравится чередовать эпизоды одного шоу с эпизодами другого.
Например, если у меня есть шоу под названием ABC с 2 эпизодами и шоу под названием XYZ с 4 эпизодами, я бы хотел, чтобы мой список воспроизведения выглядел следующим образом:
XYZe1.mp4
ABCe1.mp4
XYZe2.mp4
XYZe3.mp4
ABCe2.mp4
XYZe4.mp4
Один из способов создания этого чередованного плейлиста состоит в том, чтобы представлять каждое шоу в виде списка эпизодов и выполнять перемешивание на всех шоу. Можно написать функцию, которая будет вычислять для каждого эпизода его положение в интервале времени (от 0,0 до 1,0 исключая, 0,0 - начало сезона, 1,0 - конец сезона), а затем сортировать все эпизоды в соответствии с их положением.
Я написал следующую простую функцию в Python 2.7 для выполнения перемешивания:
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').
Мне любопытно узнать, есть ли более эффективные способы чередования или выполнения перемешивания в нескольких списках различной длины. Я хотел бы знать, есть ли у вас решения, которые проще, эффективнее или просто элегантны.
Решение, предложенное Велисарием (спасибо!):
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']