Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.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_Data Structures_Clojure_Functional Programming - Fatal编程技术网

Algorithm 如何以允许在任何索引处快速插入的方式表示一行音符?

Algorithm 如何以允许在任何索引处快速插入的方式表示一行音符?,algorithm,data-structures,clojure,functional-programming,Algorithm,Data Structures,Clojure,Functional Programming,为了“好玩”,为了学习函数式编程,我在Clojure开发了一个程序,它使用被称为“Westergaardian理论”的音乐理论中的思想进行算法作曲。它产生音乐的线条(其中线条只是由一系列音符组成的一根棍子,每个音符都有音高和持续时间)。它基本上是这样工作的: [ {:note :C3 :dur 1} {:note :D3 :dur 1} {:note :E3 :dur 1} {:note :F3 :dur 1} {:note :G3 :dur 1} {:note :A4 :dur 1} {:no

为了“好玩”,为了学习函数式编程,我在Clojure开发了一个程序,它使用被称为“Westergaardian理论”的音乐理论中的思想进行算法作曲。它产生音乐的线条(其中线条只是由一系列音符组成的一根棍子,每个音符都有音高和持续时间)。它基本上是这样工作的:

[
{:note :C3 :dur 1}
{:note :D3 :dur 1}
{:note :E3 :dur 1}
{:note :F3 :dur 1}
{:note :G3 :dur 1}
{:note :A4 :dur 1}
{:note :B4 :dur 1}
]
  • 从一行三个音符开始(如何选择这些音符的细节并不重要)
  • 在这一行上随机执行几个“操作”中的一个。该操作从满足特定条件的所有相邻音符对中随机选取(对于每对,该条件仅取决于该对,且独立于行中的其他音符)。它在所选对之间插入1个或多个注释(取决于操作)。每个操作都有自己独特的标准
  • 继续在线路上随机执行这些操作,直到线路达到所需长度 我遇到的问题是,我的实现非常缓慢,我怀疑它可以更快。一般来说,我对Clojure和函数式编程是新手(尽管我对OO很有经验),所以我希望有更多经验的人能指出,如果我不是在函数式范式中思考,或者错过了一些FP技术

    我目前的实现是每一行都是一个包含地图的向量。每个地图都有一个:note和一个:dur音符的值是表示音符的关键字,如:A4或:C#3:dur的值是一个分数,表示音符的持续时间(1是整音符,1/4是四分之一音符,等等)。例如,从C3开始代表C大调音阶的线条如下:

    [
    {:note :C3 :dur 1}
    {:note :D3 :dur 1}
    {:note :E3 :dur 1}
    {:note :F3 :dur 1}
    {:note :G3 :dur 1}
    {:note :A4 :dur 1}
    {:note :B4 :dur 1}
    ]
    
    这是一个有问题的表示,因为实际上没有一种快速的方法插入到向量的任意索引中。但在这些线路上,插入是最频繁执行的操作。我目前在一行中插入注释的糟糕函数基本上是在插入点使用subvec拆分向量,使用conj连接第一部分+注释+最后一部分,然后使用flatten和vec使它们都位于一维向量中。例如,如果我想将C3和D3插入索引3处的C大调音阶(其中F3是),它会这样做(我将使用注释名称代替:note和:dur映射):

  • (conj[C3 D3 E3][C3 D3][F3 G3 A4 B4]),它创建[C3 D3 E3[C3 D3][F3 G3 A4 B4]]
  • (vec(展平上一个向量))给出[C3 D3 E3 C3 D3 F3 G3 A4 B4]
  • 它的运行时间是O(n),AFAIK

    我正在寻找一种方法来加快插入速度。我搜索了有关Clojure数据结构的信息,这些数据结构具有快速插入功能,但没有找到任何可行的方法。我找到了“手指树”,但它们只允许在列表的开头或结尾快速插入


    编辑:我把这个问题分成两个问题

    你错过的一件事是,不管怎样,理论上,手指树确实能在任何索引处快速插入。它们只直接允许您在任意一端插入,但它们还提供快速拆分和快速连接,因此可以将快速插入任意位置函数框定为“拆分为两个序列,附加到其中一个序列,然后再将它们连接在一起”

    我之所以说“理论上”,是因为手指树依赖于恒定时间内存访问,但它们产生的缓存未命中比简单的向量多得多,而且常常不如您预期的好。手指树玩起来很有趣,但在clojure中并不常用,我也不建议真正使用它们

    一种可能是继续使用慢速操作。如果向量从来都不是很长,而且性能也不是很关键,那么O(n)插入操作就没有多大关系

    如果这不好的话,有一个解决方案可以插入您想要的O(log(n)),尽管这不是很有趣。答案是…模拟可变指针!这是一种经常有效的方法:如果指针是可变的,您可以只拥有一个链表,其中每个单元格都知道它的两个邻居,并在插入时根据需要更新它们。但在这里不能,因为循环引用对于函数数据不是很好。但是,您可以添加一个间接级别:给每个单元格一个唯一的“标签”,并让它只存储其相邻单元格的标签。这样您就没有循环引用,并且可以便宜地进行本地更新。下面是我描述的布局示例,您的C大调音阶:

    {:cell-data {0 {:left nil :right 1, :note :C3 :dur 1}
                 1 {:left 0 :right 2, :note :D3 :dur 1}
                 2 {:left 1 :right 3, :note :E3 :dur 1}
                 3 {:left 2 :right 4, :note :F3 :dur 1}
                 4 {:left 3 :right 5, :note :G3 :dur 1}
                 5 {:left 4 :right 6, :note :A4 :dur 1}
                 6 {:left 5 :right nil, :note :B4 :dur 1}}
     :first-node 0, :last-node 6}
    
    这里的数字是连续的,但是您可以看到如何在5和6之间添加节点,方法是使用
    {:left 5:right 6}
    创建一个新节点,并更改节点5的
    :right
    和节点6的
    :left


    这个组织有点麻烦,但它确实满足了您的需要。

    在地图中使用比率键怎么样?通过这种方式,插入是使用一个键执行的,该键是所选对的键的平均值

    如果需要在构建时遍历,甚至可以使用已排序的地图

    编辑:以您的示例:

    • C大调音阶:
    (定义行{0{:注意:C3:dur 1}
    1{:注:D3:dur 1}
    2{:注:E3:dur 1}
    3{:注:F3:dur 1}
    4{:注:G3:dur 1}
    5{:注:A4:dur 1}
    6{:注:B4:dur 1})
    
    • 插入C3的位置(E3和F3之间)
    (def介于-E3-和-F3[23]之间)
    
    • 插入C3:
    (让[[pos-E3 pos-F3]在-E3-和-F3之间
    C3{:注:C3:dur 1}
    位置C3(/(+pos-E3位置F3)2);5/2
    线路(accoc线路pos-C3)]
    ...)
    
    • 然后插入D3(在新插入的C3和F3之间):
    (让[[pos-E3 pos-F3]在-E3-和-F3之间
    C3{:注:C3:dur 1}
    位置-C3(/(+po)