Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 如何创建一个集合列表,其中没有两首连续的歌曲在同一个键中_Algorithm_Clojure_Combinatorics_Constraint Programming - Fatal编程技术网

Algorithm 如何创建一个集合列表,其中没有两首连续的歌曲在同一个键中

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” 多首歌曲可以“在同一个键中”(

这是一个真正的问题,我正试图自动解决,所以我很乐意回答任何问题或澄清的要求。 提前感谢您的阅读,以及您对此的任何想法。:)

编辑:为了区别可能重复的问题,我希望Clojure特定的程序能够保证返回正确的答案并利用Clojure的核心库和组合库。。。人们有了答案!多谢各位

查找关键有效集合列表的问题

我有一套
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定义。