Algorithm 如何创建一个集合列表,其中没有两首连续的歌曲在同一个键中
这是一个真正的问题,我正试图自动解决,所以我很乐意回答任何问题或澄清的要求。 提前感谢您的阅读,以及您对此的任何想法。:) 编辑:为了区别可能重复的问题,我希望Clojure特定的程序能够保证返回正确的答案并利用Clojure的核心库和组合库。。。人们有了答案!多谢各位 查找关键有效集合列表的问题 我有一套Algorithm 如何创建一个集合列表,其中没有两首连续的歌曲在同一个键中,algorithm,clojure,combinatorics,constraint-programming,Algorithm,Clojure,Combinatorics,Constraint Programming,这是一个真正的问题,我正试图自动解决,所以我很乐意回答任何问题或澄清的要求。 提前感谢您的阅读,以及您对此的任何想法。:) 编辑:为了区别可能重复的问题,我希望Clojure特定的程序能够保证返回正确的答案并利用Clojure的核心库和组合库。。。人们有了答案!多谢各位 查找关键有效集合列表的问题 我有一套n歌曲(顺序无关紧要) 每首歌曲只有一个密钥签名,简称“key”,它必须是12个字符串中的一个“A”“A”“B”“C”“C”“D”“E”“F”“F”“G”“G” 多首歌曲可以“在同一个键中”(
n
歌曲(顺序无关紧要)
每首歌曲只有一个密钥签名,简称“key”,它必须是12个字符串中的一个“A”“A”“B”“C”“C”“D”“E”“F”“F”“G”“G”
多首歌曲可以“在同一个键中”(为其分配相同的整数)
我需要返回一个长度为n
的有序列表,该列表包含每首歌曲,如果可以找到这样的列表,则其顺序应确保没有两首连续的歌曲位于同一个键中。(我将其称为密钥有效设置列表)。这是因为当你在同一个键中听到两首歌背靠背时,听起来有点无聊。听起来有点像是一首大型歌曲的两个部分
; input 1, here given as a list but really an unordered set, not a key-valid setlist because there are a bunch of songs in the key of A consecutively:
[
{:title "Deep House Track" :key "F#"}
{:title "Breakup Song" :key "B"}
{:title "Love Song" :key "A"}
{:title "Inspirational Song" :key "A"}
{:title "Summer Song" :key "A"}
{:title "Summer Song" :key "A"}
{:title "Power Ballad" :key "D"}
]
; output 1 will be:
[
{:title "Love Song" :key "A"}
{:title "Breakup Song" :key "B"}
{:title "Inspirational Song" :key "A"}
{:title "Power Ballad" :key "D"}
{:title "Summer Song" :key "A"}
{:title "Deep House Track" :key "F#"}
{:title "Summer Song" :key "A"}
]
显然,并非总是能够找到有效的密钥集合列表:
; input 2, with no solution:
[
{:title "Love Song" key "A"}
{:title "Inspirational Song" key "A"}
]
我尝试过的
我试图编写一些东西,在输入上使用Clojure的GROUPBY
,通过密钥签名字符串进行分组(调用生成的映射m
),然后使用累加器实现递归函数(在这里我将建立最终的集合列表)尝试将歌曲从m
放入累加器的有效位置
然而,我无法说服自己,如果这个方法存在的话,它总能找到解决方案
想法
我上面的想法似乎有道理,但可能需要添加回溯。我不知道该如何实现这一点
其他想法包括将其视为数独游戏并使用基于约束的方法——如果有人知道如何使用core.logic
,我会对一种更具声明性的方法感兴趣
未来考虑事项:
我试图在Clojure中实现这一点(我认为
核心.logic
库可能会有所帮助),但显然,该算法可以在任何语言中实现。通过利用Clojure.math.combinations
,您可以轻松做到这一点:
(ns demo.core
(:use tupelo.core)
(:require
[clojure.string :as str]
[schema.core :as s]
[clojure.math.combinatorics :as combo]))
(def Song {:title s/Str :key s/Str})
(def SongPair [(s/one Song "s1")
(s/one Song "s2")])
(s/defn valid-pair?
[song-pair :- SongPair]
(let [[song-1 song-2] song-pair
key-1 (grab :key song-1)
key-2 (grab :key song-2)]
(not= key-1 key-2)))
(s/defn valid-set-list?
[set-list :- [Song]]
(let [song-pairs (partition 2 1 set-list)]
(every? valid-pair? song-pairs)))
(s/defn valid-sets
"Return a list of valid sets (song orderings) from songs that can follow the given lead-song."
[songs :- [Song]]
(let [all-set-lists (combo/permutations songs)
all-set-lists (mapv vec all-set-lists) ; convert set lists => vectors
valid-set-lists (set (filter valid-set-list? all-set-lists))]
valid-set-lists))
单元测试显示它在运行:
(dotest
(let [songs [{:title "A1" :key "A"}
{:title "B1" :key "B"}]]
(is= (valid-sets songs)
#{[{:title "A1", :key "A"} {:title "B1", :key "B"}]
[{:title "B1", :key "B"} {:title "A1", :key "A"}]})))
(dotest
(let [songs [{:title "A1" :key "A"}
{:title "B1" :key "B"}
{:title "B2" :key "B"}]]
(is= (valid-sets songs)
#{[{:title "B2", :key "B"}
{:title "A1", :key "A"}
{:title "B1", :key "B"}]
[{:title "B1", :key "B"}
{:title "A1", :key "A"}
{:title "B2", :key "B"}]})))
(dotest
(let [songs [{:title "A1" :key "A"}
{:title "B1" :key "B"}
{:title "C1" :key "C"}]]
(is= (valid-sets songs)
#{[{:title "A1", :key "A"}
{:title "B1", :key "B"}
{:title "C1", :key "C"}]
[{:title "B1", :key "B"}
{:title "A1", :key "A"}
{:title "C1", :key "C"}]
[{:title "C1", :key "C"}
{:title "B1", :key "B"}
{:title "A1", :key "A"}]
[{:title "C1", :key "C"}
{:title "A1", :key "A"}
{:title "B1", :key "B"}]
[{:title "A1", :key "A"}
{:title "C1", :key "C"}
{:title "B1", :key "B"}]
[{:title "B1", :key "B"}
{:title "C1", :key "C"}
{:title "A1", :key "A"}]})))
例如,有144组可能的歌曲:
(dotest
(let [songs [{:title "Deep House Track" :key "F#"}
{:title "Breakup Song" :key "B"}
{:title "Love Song" :key "A"}
{:title "Inspirational Song" :key "A"}
{:title "Summer Song" :key "A"}
{:title "Power Ballad" :key "D"}]]
(is= 144 (count (valid-sets songs)) )))
这里有一种使用core.logic的方法 我们将定义
secondo
(如firsto
),以便在下一个函数中查看集合中每对项的第二项
(defn secondo [l s]
(fresh [x]
(resto l x)
(firsto x s)))
我们将定义nonconseco
以递归方式检查是否没有连续值:
(defn nonconseco [l]
(conde
[(== l ())]
[(fresh [x] (== l (list x)))]
[(fresh [lhead lsecond ltail]
(conso lhead ltail l)
(secondo l lsecond)
(project [lhead lsecond] ;; project to get your map keys
(!= (:key lhead) (:key lsecond)))
(nonconseco ltail))]))
以及一个函数,用于查找不具有任何连续相同值的coll
的第一个排列:
(defn non-consecutive [coll]
(first
(run 1 [q]
(permuteo coll q)
(nonconseco q))))
这可用于您的示例输入:
(non-consecutive
[{:title "Deep House Track" :key "F#"}
{:title "Breakup Song" :key "B"}
{:title "Love Song" :key "A"}
{:title "Inspirational Song" :key "A"}
{:title "Summer Song" :key "A"}
{:title "Power Ballad" :key "D"}])
=>
({:title "Love Song", :key "A"}
{:title "Breakup Song", :key "B"}
{:title "Inspirational Song", :key "A"}
{:title "Deep House Track", :key "F#"}
{:title "Summer Song", :key "A"}
{:title "Power Ballad", :key "D"})
这里是nonseco
的通用版本,它只查看值,而不是地图中的:key
s:
(defn nonconseco [l]
(conde
[(== l ())]
[(fresh [x] (== l (list x)))]
[(fresh [lhead lsecond ltail]
(conso lhead ltail l)
(secondo l lsecond)
(!= lhead lsecond)
(nonconseco ltail))]))
(non-consecutive [1 1 2 2 3 3 4 4 5 5 5])
=> (3 2 3 4 2 4 5 1 5 1 5)
更新:这里有一个使用谓词函数而不是关系逻辑的更快版本:
(defn non-consecutive? [coll]
(every? (partial apply not=) (partition 2 1 coll)))
然后使用core.logic的pred
将该谓词应用于逻辑变量:
(run 10 [q]
(permuteo coll q)
(pred q non-consecutive?))
我不是很喜欢clojure,但是我很确定你可以有一个地图,地图的实际键是“key”,值是所有歌曲的数组。然后,只需为每个键取一个数组值即可。如果只有一个键,也很容易检测到,从而使结果不可能出现。(删除了我之前的评论。该建议并不完全符合预期。)这可能是难以置信的重复,谢谢。作为一个从未使用过
core.logic
的人,我该如何让它真正运行呢?我可以简单地在REPL中导入core.logic
,或者我需要创建一个新的lein项目吗?@Rucksack如果您使用的是Leiningen,那么您可以在任何需要的地方使用名称空间。我上面的例子假设您已经参考了必要的core.logic定义。