在列表中排列元素,使相似的元素相距最远

时间:2014-02-11 17:10:51

标签: python sorting

如何对播放列表进行排序,以便相同艺术家的歌曲尽可能分散?

虽然我找不到或计算出来,但我认为有一些快速而简单的方法可以做到这一点,它适用于各种设置以及两个方面:让我们说(un)按艺术家排序这种类型使得另外一种类型几乎总是跟着不同的类型。 (所以这是一个很好的组合)

所以这是一个示例性播放列表:

from collections import namedtuple

Song = namedtuple('Song', ('artist', 'title', 'length'))

# the length is not correct
Mozart_1 = Song('Mozart', 'Don Giovanni', 3.5)
Mozart_2 = Song('Mozart', 'Serenata Notturna', 2.98)
Mozart_3 = Song('Mozart', 'Violin Concerto No. 3 in G, 1st Movement', 8.43)
Bach_1 = Song('Bach', 'Air', 6.18)
Bach_2 = Song('Bach', 'Toccata in D Minor', 12.44)
Beethoven_1 = Song('Beethoven', 'Für Elise', 2.47)

playlist = [Beethoven_1, Mozart_3, Bach_1, Mozart_2, Mozart_1, Bach_2] # unsorted

这将是一个可能的最佳结果:

OPTIMUM = [Mozart_1, Bach_1, Mozart_2, Beethoven_1, Mozart_3, Bach_2]

2 个答案:

答案 0 :(得分:5)

认为这不是一个不合理的答案,可以将可能性分散开来(即使它不符合你的例子):

from collections import defaultdict
from itertools import count

by_artist = defaultdict(count)
new_list = sorted(playlist, key=lambda L: next(by_artist[L.artist]))

给出播放列表:

[Song(artist='Beethoven', title='Fur Elise', length=2.47),
 Song(artist='Mozart', title='Violin Concerto No. 3 in G, 1st Movement', length=8.43),
 Song(artist='Bach', title='Air', length=6.18),
 Song(artist='Mozart', title='Serenata Notturna', length=2.98),
 Song(artist='Mozart', title='Don Giovanni', length=3.5),
 Song(artist='Bach', title='Toccata in D Minor', length=12.44)]

输出:

[Song(artist='Beethoven', title='Fur Elise', length=2.47),
 Song(artist='Mozart', title='Violin Concerto No. 3 in G, 1st Movement', length=8.43),
 Song(artist='Bach', title='Air', length=6.18),
 Song(artist='Mozart', title='Serenata Notturna', length=2.98),
 Song(artist='Bach', title='Toccata in D Minor', length=12.44),
 Song(artist='Mozart', title='Don Giovanni', length=3.5)]

答案 1 :(得分:-1)

实际上这个问题的一个答案,我的问题据说是重复的,对我有用。所以,我的问题确实是重复的。虽然如果有人想出一个更快的非随机版本,我仍然会将该答案标记为已接受。这是我的修改版本:

import random
from collections import namedtuple, defaultdict
from operator import attrgetter

Song = namedtuple('Song', ('artist', 'title', 'length'))

# the length is not correct
Mozart_1 = Song('Mozart', 'Don Giovanni', 3.5)
Mozart_2 = Song('Mozart', 'Serenata Notturna', 2.98)
Mozart_3 = Song('Mozart', 'Violin Concerto No. 3 in G, 1st Movement', 8.43)
Bach_1 = Song('Bach', 'Air', 6.18)
Bach_2 = Song('Bach', 'Toccata in D Minor', 12.44)
Beethoven_1 = Song('Beethoven', 'Für Elise', 2.47)

playlist = [Beethoven_1, Mozart_3, Bach_1, Mozart_2, Mozart_1, Bach_2] # unsorted

# one possible optimum
# OPTIMUM = [Mozart_1, Bach_1, Mozart_2, Beethoven_1, Mozart_3, Bach_2]


def optimize(items, quality_function, stop=1000, randrange=random.randrange):
    length = len(items)
    no_improvement = 0
    best = 0
    while no_improvement < stop:
        i = randrange(length)
        j = randrange(length)
        copy = items[:]
        copy[i], copy[j] = copy[j], copy[i]

        q = quality_function(copy)
        if q > best:
            items, best = copy, q
            no_improvement = 0
        else:
            no_improvement += 1
    return items


def quality_maxmindist(items):
    s = 0
    for item in set(items):
        indcs = [i for i in range(len(items)) if items[i] == item]
        if len(indcs) > 1:
            s += sum(1. / (indcs[i+1] - indcs[i]) for i in range(len(indcs)-1))
    return 1. / s


def spread_equal_items_apart(items, key, stop=optimize.__defaults__[0]):
    keys, key_to_items = keys_and_key_to_items_dict(items, key)
    keys = optimize(keys, quality_maxmindist)
    re = []
    for k in keys:
        re.append(key_to_items[k].pop())
    return re


def keys_and_key_to_items_dict(items, key):
    key_to_items = defaultdict(list)
    keys = []
    for i in items:
        k = key(i)
        keys.append(k)
        key_to_items[k].append(i)
    return keys, key_to_items


if __name__ == '__main__':
    new = spread_equal_items_apart(playlist, attrgetter('artist'))
    print(new)

所以new现在是:

[Song(artist='Mozart', title='Don Giovanni', length=3.5),
 Song(artist='Bach', title='Toccata in D Minor', length=12.44),
 Song(artist='Beethoven', title='Für Elise', length=2.47),
 Song(artist='Mozart', title='Serenata Notturna', length=2.98),
 Song(artist='Bach', title='Air', length=6.18),
 Song(artist='Mozart', title='Violin Concerto No. 3 in G, 1st Movement', length=8.43)]