用于沿多个类别(id3标签)均匀地间隔列表项(播放列表歌曲)的算法

时间:2015-08-06 20:11:04

标签: arrays algorithm list sorting playlist

我在设计算法以帮助创建mp3播放列表时遇到问题,尽管可以根据我的用途调整列表中均匀分隔项目的更一般情况的算法。

一般情况是我想重新排序列表中的项目以最大化它们沿多个轴的多样性。

我的具体用例是我想将一堆歌曲转储到一个集合中,然后在集合上运行我的算法以生成一个有序的播放列表。我希望订单遵循这套标准:

  1. 最大化同一艺术家实例之间的距离
  2. 最大化同一类型实例之间的距离
  3. 最大化类别X实例之间的距离
  4. 等N个类别
  5. 显然我们不能保证平等地优化所有类别,所以第一类加权最重要,第二加权更少,等等。我肯定想满足前两个标准,但是使算法可扩展以满足N将是太棒了。最大化随机性(shuffle)不是优先事项。无论我在播放列表中的哪个位置,我都希望将听觉体验多样化。

    这似乎接近于描述的问题并解决了here,但是当所有项目都在具有多个维度的相同列表中而不是单独的列表时,我无法理解如何应用此问题。不同的尺寸。

    这似乎是一个本来可以解决很多次的问题,但是我找不到它的任何例子。

5 个答案:

答案 0 :(得分:1)

这是我的想法:你创建一个图形,其中歌曲是顶点,路径代表它们的多样性。

例如我们有五首歌曲:

  1. “A”,国家,由John Doe撰写
  2. “B”,国家,由Jane Dean撰写
  3. “C”,techno,由Stan Chang撰写
  4. “D”,techno,由John Doe撰写
  5. “E”,国家,由John Doe撰写
  6. 我们将权重2分配给艺术家,将1分配给流派,并使用乘法反转作为路径的值。一些路径将如下所示:

    A-B2*1 + 1*0 = 2 =>路径的值是1/2 = 0.5

    A-C2*1 + 1*1 = 3 =>路径的值是1/3 = 0.33

    A-D2*0 + 1*1 = 1 =>路径的值是1/1 = 1

    A-E2*0 + 1*0 = 0 =>路径的值是1/0 = MAX_DOUBLE

    您可以根据需要添加任意数量的类别,并根据需要进行加权。

    计算完所有歌曲之间的所有路径后,您所要做的就是为Travelling Salesman Problem使用一些启发式算法。

    编辑:

      

    我想对这个问题提出另一个限制:“最大距离”应该考虑播放列表可能重复的事实。这意味着简单地将同一位艺术家的两首歌曲放在播放列表的两端会失败,因为当列表重复时,它们会相互“相邻”。

    旅行商问题的一部分问题是,最终你会回到你的原点,所以你的播放列表的两端会有相同的歌曲,而且两首路径(从歌曲到歌曲)都将以最好的方式计算使用启发式允许的效率。因此,您所要做的就是从结果中删除最后一个条目(因为它与第一个条目相同),您可以安全地重复而不会破坏您的要求。

答案 1 :(得分:1)

This should be much faster than brute-force:

  1. Order all the songs randomly.

  2. Compute the weights for each song slot (i.e. how close is it to the same artist/genre/etc.). It will be a number from 1-N indicating how may songs away it is from a match. Lower is worse.

  3. Take the song with the lowest weight, and swap that song with a random other song.

  4. Re-compute the weights of the swapped songs. If either got worse, reverse the swap and go back to 3.

  5. For debugging, print the "lowest weight" and overall average weight. (debugging)

  6. Go to 2

You won't find the optimal this way, but it should give mediocre results pretty fast, and eventually improve.

Step 2 can be made fast this way: (pseudo code in Ruby)

# Find the closest match to a song in slot_number
def closest_match(slot_number)
  # Note: MAX can be less than N. Maybe nobody cares about songs more than 20 steps away.
  (1..MAX).each |step|
    return step if matches?(slot_number+step, slot_number) or matches?(slot_number-step, slot_number)
  end
  return MAX
end

# Given 2 slots, do the songs there match?
# Handles out-of-bounds
def matches?(x,y)
  return false if y > N or y < 1
  return false if x > N or x < 1
  s1 = song_at(x)
  s2 = song_at(y)
  return true if s1.artist == s2.artist or s1.genre == s2.genere
  return false
end

You also don't have to re-compute the whole array: If you cache the weights, you only need to recompute songs that have weight >=X if they are X steps away from a swapped song. Example:

|  Song1   |  Song2   |   Song3  |  Song4   |  Song5  |
| Weight=3 | Weight=1 | Weight=5 | Weight=3 | Weight=2|

If you are swapping Song 2, you don't have to re-compute song 5: It's 3 steps away from Song 3, but it's weight was 2, so it won't "see" Song 3.

答案 2 :(得分:1)

你的问题可能是NP难的。为了理解它,这里减少了CLIQUE(一个NP难题)。这并不能证明你的问题是NP难的,但至少可以说明这两个问题之间存在联系。 (为了明确地证明你的问题是NP难的,你需要减少另一个方向:显示CLIQUE可以减少你的问题。我觉得这是可能的,但是获得正确的细节是挑剔的。)

假设您有n = 6首歌曲,A,B,C,D,E和F.将它们放在如下图表中:

1 2 3 4 5 6
A A A A A A
B B B B B B
C C C C C C
D D D D D D
E E E E E E
F F F F F F

将第1列中的每个项目与边缘连接到每个其他列中的每个其他项目,但同一行中的项目除外。因此,第1列中的A连接到第2列中的B,C,D,E,F,第3列中的B,C,D,E,F,依此类推。图中有n ^ 2 = 36个节点,图中有n*(n-1)^2 + n*(n-1)*(n-2) + n*(n-1)*(n-3) + ... = n*(n-1)*n*(n-1)/2 = O(n^4)个边。

播放列表是此图表中的最大集团,换句话说,是一个相互一致的选择(没有播放两次歌曲)。到目前为止,并不是那么难:可以很快找到许多最大派系(只是歌曲的排列)。

现在我们将有关歌曲相似性的信息添加为边缘权重。两首相似且接近的歌曲获得低边缘重量。相似且相距很远的两首歌曲获得更高的边缘权重。现在的问题是找到一个具有最大总边缘权重的最大团,换句话说就是NP难问题CLIQUE。

有一些攻击CLIQUE的算法,但当然它们是指数级的。在合理的时间内你能做的最好的事情就是运行其中一种算法并获得它在那段时间内可以产生的最佳结果,或者在给定的时间内随机生成排列并选择分数最高的那个。您可以使用模拟退火等方法获得更好的自然数据结果来解决优化问题,但CLIQUE“很难近似”,所以我觉得你不会得到比通过随机生成提案更好的结果并选出最高分。

答案 3 :(得分:0)

蛮力算法很容易。

maxDistance = 0
foreach ordering
  distance = 0
  foreach category
    for i=1 to numsongs
      for j=i+1 to numsongs
        if song i and song j in this ordering have same value for this category
          distance = distance + (j-i)*weight_for_this_category
        endif
      endfor
    endfor
  endfor
  if ( distance > maxDistance )
    maxDistance = distance
    mark ordering as best one so far
  endif
endfor

但是这种算法的歌曲数量比指数复杂度更差,所以它需要非常快速的无法控制的时间。困难的部分是在合理的时间内完成的。

答案 4 :(得分:0)

我在想一个“春天”的方法。如果新项目被添加到列表的末尾,他们会向前挤压类似的项目。

如果我将Pink Floyd添加到列表中,那么所有其他Floyd歌曲都会被压缩以留出空间。

我会在最常见的维度之前实现最不常见的维度,以确保更好地管理更常见的维度。

For tags in song ordered by count tags in list asc
   Evenly space earlier songs with knowledge new song being added
Add song