我想编写一个“最终洗牌”算法来对我的mp3集合进行排序


33

我正在寻找伪代码建议,以避免标题和艺术家重复的方式对mp3文件进行排序。我听低吟的歌手-弗兰克·辛纳屈(Frank Sinatra),托尼·贝内特(Tony Bennett),埃拉·菲茨杰拉德(Ella Fitzgerald)等歌唱旧唱片。每位歌手都录制许多相同的歌曲-《飞向月球》,《今夜的样子》,《星尘》等。我的目标是安排歌曲(或订购播放列表),并在歌手和歌曲名称之间留出最大的空间。因此,如果我有2000首歌曲,而Ella有20首,我想每100首歌曲只听一次她。如果有10位歌手演唱《飞向月球》,我希望每200首歌曲中听到一次。当然,我想结合这两个要求来创建我的“最终洗牌”。

我知道这是一个相当广泛的问题。我尚未开始对其进行编程,因此我只是在寻找采用一种好的方法的建议。关于均匀间隔其他歌曲属性,我实际上还有一些其他要求,但这里不再赘述。


首先,我正在修改在这里找到的代码以处理mp3文件和读取ID3标签。

我编写了一个小应用程序,使用下面的parsifal回答可以满足我的需求。我还在这里写了一个后续问题。感谢您的所有好评!


3
很酷的问题,很酷的问题,非常了解算法的人可能会为您提供基于形式化方法的好答案。
Jimmy Hoffa 2013年

因此,如果您的音乐收藏中有50%来自同一位艺术家,那么您想每隔2首歌曲收听一次该艺术家,而不管有多少其他艺术家...也许不多于50%,但您会得到理念。也许只是我的意见,但这听起来不像是“最终的洗牌”,除非您从每位歌手那里获得的歌曲数量大致相同。另一方面,如果您只有一首歌手的歌曲,则您不希望其播放过多。在这两个之间找到平衡并不难。
2013年

我只想做这样的伪代码:while (length(songs) > 0) { x := rand(); addElem(shuffle, songs[x]); remElem(songs, x); },但是您说您想要“最终洗牌”。我不知道您真正想要的是什么,甚至没有阅读问题...
Cole Johnson

您可以将歌曲列表上传到某处-标题和艺术家标签或单独的管道或XML
tgkprog

在Banshee中拥有(作为插件或核心)真是太好了!
phw

Answers:


5

您是否要运行一次程序并生成播放列表,或者现场直播下一首歌曲?

如果是后者,则答案很简单:

  • 创建一个数组,其中包含您的所有歌曲以及歌手和标题
  • 创建一个列表(最好是链接列表)来保存最近播放的歌曲标题。此列表开始时为空,每次播放一首歌曲时,便将其添加到列表中。当列表达到所需的“无重复歌曲”大小时,请删除最早的(第一个)条目。
  • 同上艺术家名单。

然后挑选一首歌曲将成为以下步骤序列:

  1. 从“所有歌曲”数组中随机选择一首歌曲。这只是一个介于0和数组大小之间的随机数。
  2. 查看该歌曲是否已经在播放的歌曲列表中。如果是,请返回步骤1。
  3. 查看该艺术家是否已经在播放的艺术家列表中。如果是,请返回步骤1。
  4. 将歌曲艺术家/标题添加到适当的列表,如果需要,则删除旧条目。
  5. 播放歌曲。

有两个可能的问题,但是只有当您将其作为家庭作业而不是真正的项目进行时,它们才有意义。

  • 正如@Dukeling在评论中说的那样,如果您的收藏集在单个歌手或歌曲名称的支持下明显不平衡,则您可能会陷入循环,不断拒绝歌曲。实际上,这不会成为问题。解决方案是您需要减小“已显示”列表的大小。在步骤2和步骤3添加计数器可以告诉您是否有问题(如果您连续看到10个失败,则发出警告和/或减小列表的大小)。
  • 如果您试图生成一个播放列表,其中包含所有您只播放过一次的歌曲,则需要从源数组中删除歌曲。这也将改变您处理过多“最近玩过的”失败的方式(因为最终您可能最终在源数组中只有一位艺术家)。
  • 如果您的ID3标签与我的标签相同,则它们包含许多拼写错误。“埃灵顿公爵”是否需要与“埃林顿公爵”不同?如果是,则在扫描“最近播放”列表时考虑使用Levenstein匹配器。

我使用RockBox(rockbox.org)。对于任何歌曲文件夹,它都可以创建一个动态播放列表(也可以保存和添加书签)。我计划为每首歌曲标题0001、0002加上前缀,然后按顺序播放它们。
DeveloperDan

@DeveloperDan-相同的过程有效,但正如我在最后指出的那样,您可能会拥有不符合规则的歌曲。您有两种选择:调整规则并重新运行,或者(如果没有很多)随机插入歌曲。
parsifal

我将在步骤1中创建一个列表,然后在步骤2和3中将其删除。这使得不可能陷入循环,如果列表为空,则您需要更改规则并重新扫描。更强大的方法。
Macke 2014年

13

在使用生成器之前,我已经做过类似的事情(在C#中,是yield每个循环迭代的无限循环)。每次迭代都会查看其歌曲库(或其他内容),并丢弃最近播放过的歌曲(或任何否定标准)。然后,从过滤列表中选择一个,并更新状态。当您的状态发生变化(播放非Sinatra歌曲)时,条件将失效,并且您排除的歌曲开始重新包含在内。

当然,有一些极端的情况要处理:

  • 如果扔掉所有歌曲会怎样?(通常只是随机选择一个,希望破坏国家稳定)
  • 应该优先考虑一些标准吗?(通常是这种情况,也许您不想背对背玩《飞向月球》,并且宁愿不背对背玩Sinatra,但如果您仅此而已...)
  • 如果您的歌曲集在战斗中更新会怎样?(通常易于处理,但并发可能会因使用情况而出现问题)

11

忽略Telastyn提出的问题的离群值,听起来您在背包问题上有不同之处。幸运的是,这是一个非常有据可查的算法。

来自维基百科

给定一组项目,每个项目都有权重和一个值,请确定要包含在集合中的每个项目的数量,以使总重量小于或等于给定的限制,并且总值尽可能大。

该文章中列出了一些潜在的相关变化以及背包问题的其他列表


背包问题的一种变型是多目标背包问题。建议将蚁群算法作为解决该问题的一种方法。蚁群方法可能是避免问题的NP问题最简单的方法。

我还可以将您的问题视为旅行商问题的极端变体。每个游览的城市实际上都是您想要播放的歌曲,但是我不确定您如何指定艺术家之间的间隔。这个建议也与/可以通过蚁群方法解决。


8

我假设这是“这里是我的库,运行此程序并生成播放歌曲的命令”。

这还没有实现,我不确定它会进行多大的改组。可能是因为我对过滤器的要求严格了,在给定初始歌曲集的情况下,对于其余部分,这将导致(我相信)规定的顺序。

一个ideal_gap散列。这是通过具有给定属性(艺术家,专辑,标题)的歌曲的密度来计算的。如果一首歌曲有2000首歌曲,而其中有20首是由一位名叫Ella的艺术家创作的,那ideal_gap{'artist'}{"ella"}将是100首。

具有该信息的人也具有ideal_gap值的最大值。让我们称之为max_gap

考虑:具有一个最大值以ideal_gap防止只有两位歌手演唱过的歌曲阻止另一首歌曲在以后播放1000首歌曲,并且还极大地增加了max_gap值,从而导致“回退,无歌曲,回退”的许多迭代关闭,没有歌曲”。

检查最近播放的max_gap歌曲(可以从上一次运行中填充,以便在Frank Sinatra演唱《 Fly Me To the Moon》结束后,下一次运行不会偶然从同一首歌曲开始),从其中筛选出一首歌曲库产生了一组候选歌曲。如果歌曲的所有间隙都小于ideal_gap这些属性的间隙,则该歌曲将仅出现在候选歌曲中。

从候选歌曲集中,随机选择一个。

考虑:对集合进行加权,以使具有最大最大间隙的属性的歌曲更有可能被加权。这样,就不会在播放列表的末尾堆积所有较大的最大空缺歌曲。

考虑:不要使所有三个属性都大于理想间隙,而应使三分之二。这可能意味着可以比理想理想更快地播放某首歌,但是会增加候选歌曲集的大小,这意味着“随机选择一个”有更多选择。

如果没有满足要求的歌曲,则将“ max_gap1” 后退,然后将所有“ ideal_gaps”按n/max_gap百分比后退,其中n此后退的次数。这样,如果a的max_gap值为100,并且在此迭代中已被退回5次,则将Ideal_gap的100临时调整为95,将Ideal_gap的20临时调整为19。直到至少有一首候选歌曲,然后如上选择。

考虑:具有最小池大小。这增加了差异,但是当有另一首歌曲可以播放时,可能会导致歌曲播放得比理想间隔快。


1

这是一个优化的工作,一个非常复杂的一个,如果你正在寻找最佳解决方案。幸运的是,我相信这将是足够好的事例之一。

首先要做的是建立数学质量标准,即给定列表的排列将返回一个描述该排列的好坏的数字。

一个简单的公式建议,应该给您要考虑的每个准则一个权重,对重要准则赋予较高的权重,而对于许多歌曲具有相同属性的准则,则赋予较低的权重,以使这些歌曲不占主导地位:

For each song on the list
    For each other song on the list
        For each criteria
            If the two songs share that criteria
                Add to the quality value: square root( [criteria weight]/[distance between the two songs] )

此过程产生的值越低,列表排列就越好。

进行排列

现在,您可以将此公式用于math.stackexchange,并让他们告诉您,找到除了数量不多的歌曲之外的其他任何东西的最佳解决方案是多么疯狂,甚至可能几乎是不可能的,或者您可以只花一个时钟周期并获得一个好的解决方案。

有很多方法可以做到,这是一种:

Start with a random permutation of the list.
Several million times do the following:
    Select two entries at random
    For each of those two entries calculate their contribution to the quality value
    Swap the positions of the two entries
    Calculate the contribution to the quality value of the two entries at their new position
    If the sum of the calculations in the new positions is greater than the sum in the old positions
        Swap back

这是一种有点浪费的算法,但是它易于实现,并且可以根据需要处理尽可能多的标准。

最佳化

可以应用不同的调整和优化的负载,以下是一些:

在质量值计算中,不要费心检查一首歌曲与列表中的其他每首歌曲,而只是针对100首左右的最近一首歌曲进行检查。对于常用值,此速度优化实际上对结果质量没有影响。

对于给定属性的稀有值,跟踪该值的现有实例可能比搜索它们更有效。

如果您认为将几乎没有实例的值的间距接近于偶数而不是相距很远很重要,则可能有必要增加这些特定值的权重,而不要增加该标准的其他值。

从列表中以均等分布挑选所有可能的对的伪随机函数,其每次挑选的效率可能比正常随机挑选的效率略高。


我相信您的算法是模拟退火的一种形式,可能是进一步完善它的地方。

@MichaelT不,模拟退火使用“温度”,以使其退回至较低状态,从而避免陷入局部最大值。这只是一个本地搜索,可以相对轻松地修改为模拟退火或其他许多概率搜索算法中的任何一种,但是我认为没有太多需要。基本上,所有其他算法的不同之处是试图避免局部最大值,但是我认为您不会为该问题找到局部最大值,这不是可接受的解决方案。
aaaaaaaaaaaaaa

0

人们采用不同的方法很有趣。我将执行以下操作:

根据到目前为止播放的所有曲目,给每个曲目打分。播放得分最低的曲目(或在得分相同的情况下,随机播放一个得分最低的曲目)。重复。

当然,最困难的一点是得分。对于接下来可能要播放的每个曲目,您都必须遍历已经播放过的每个曲目(或数量有限)。如果[可能的下一个]曲目和[最近播放的]曲目有共同点,您可以添加分数,具体取决于它们的共同点,它们的共同点以及[最近播放的]曲目多久之前演奏。您可能希望“一点都没有”为0,因此您可以将所有音轨都设为0。

首先,您可能会想尝试一些手工制作的播放列表,以使数学正确无误-您是否想要共同的单词数,还是共同的单词数的平方,或者该数字的平方根?共同的词?遍历整个播放列表,查看哪些是“最常见”的浮动列表,然后手动调整因素以达到平衡。也许您想按字母排列,所以“伊灵顿公爵”与“伊灵顿公爵”相比得分较高,但与“埃勒·杜顿王”相比得分更高(如果我没丢任何字母的话:) 。您应该非常仔细地考虑要比较哪些字段,以及是否要在各个字段之间进行比较。您甚至可以考虑使用双字母(成对的字母;就艾林顿公爵而言,是“ Du”,

请注意,如果您有很多特定的艺术家,则可能会优先考虑该艺术家-您可能会听到5位独特艺术家的曲目,然后才能听到杜克·埃灵顿的所有10条曲目。这可能是您想要的,也可能不是。您可以通过设置一个字典来比较所有要比较的东西以及它们出现的频率,从而避免这种情况,因此,如果您有很多Duke Ellington音轨,那么Duke Ellington的两个音轨就比Billy Joe Shaver的两个音轨“不太相似” 。

甚至可能需要将两对歌曲的每种组合预先计入一张桌子。同样,在考虑接下来播放哪首歌时,您只需要记住到目前为止的最佳歌曲即可;如果要考虑的下一首歌曲的得分比到目前为止的最佳歌曲差,则可以跳到下一首。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.