Algorithm 如何以允许在任何索引处快速插入的方式表示一行音符?
为了“好玩”,为了学习函数式编程,我在Clojure开发了一个程序,它使用被称为“Westergaardian理论”的音乐理论中的思想进行算法作曲。它产生音乐的线条(其中线条只是由一系列音符组成的一根棍子,每个音符都有音高和持续时间)。它基本上是这样工作的: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
[
{: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}
]
[
{: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映射):
编辑:我把这个问题分成两个问题 你错过的一件事是,不管怎样,理论上,手指树确实能在任何索引处快速插入。它们只直接允许您在任意一端插入,但它们还提供快速拆分和快速连接,因此可以将快速插入任意位置函数框定为“拆分为两个序列,附加到其中一个序列,然后再将它们连接在一起” 我之所以说“理论上”,是因为手指树依赖于恒定时间内存访问,但它们产生的缓存未命中比简单的向量多得多,而且常常不如您预期的好。手指树玩起来很有趣,但在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)