组织格里高利教堂的音乐


19

那是930年,而格里高利教堂遇到了问题。他们有数千页的诵经音乐,但是问题是所有乐谱都被简单地堆放了起来,而不是拥有任何实际的组织系统:

乐谱的形象
制图者协会的用户gamerprinter提供。

教堂需要组织所有乐谱,因此他们聘请了一位中世纪软件工程师来编写程序来为他们组织音乐。您是已雇用的软件工程师。但是,中世纪的编译过程需要由一群缓慢的圣经抄写员将程序写在纸上。为了减少抄写员团队编译代码所花费的时间,您必须使程序尽可能小。

教会希望根据他们所写的音阶来组织圣歌。教会的所有圣歌都以多利安音阶来编写。给定特定音乐的音符,您的程序将输出它所在的Dorian音阶。在这里,我将确切解释Dorian音阶是什么。如果您已经知道,则可以跳过本节。

任何旋律中都有12种可能的音符。在这里,它们按顺序排列:

C C# D D# E F F# G G# A A# B

一个半音(用表示S)正在向右增加一个步骤,环绕(所以从B向上的半音将回到C)。甲色调(使用表示T)是两个半音。例如,从F#开始的半音为G。从F#开始的半音为G#。

要创建多利安音阶,我们从列表中的任何音符开始,然后按照以下模式向上移动,列出遇到的音符:

T, S, T, T, T, S

一个例子。我从A开始。Dorian音阶的音阶变成:

A
B  (up a tone)
C  (up a semitone)
D  (up a tone)
E  (up a tone)
F# (up a tone)
G  (up a semitone)

该秤的音符为A,B,C,D,E,F#和G。由于我是从A开始的,因此在A中将其称为Dorian秤。因此,有12种不同的多里安音阶,每种音阶都以它们起初的音符命名。他们每个人都使用相同的色调和半音模式,只是从不同的位置开始。如果我的解释不一致,您也可以查阅Wikipedia

可以从适合您程序的任何内容(例如STDIN,命令行参数,raw_input())中给出程序的输入。它可能没有在变量中预先初始化。输入将是逗号分隔音符列表,代表乐曲的旋律。可能有重复的注释。输入中将始终有足够多的不同音符,以便能够果断地推断出乐曲的音阶。输入示例:

B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A

程序的输出应为字符串Dorian scale in X,其中X是音阶的起始音符。示例输入的输出:

Dorian scale in B

将其与B(B C# D E F# G# A)中的多利安音阶进行比较,我们看到旋律的所有音符都在该音阶内。在这种情况下,注释C#未使用。但是,有足够的注释可以明确地将B Dorian标识为正确的密钥。没有其他的多利安音阶适合,因为无论我们尝试使用哪种其他音阶,总是至少有一个不属于该音阶的旋律音符。

这是代码高尔夫球,因此字符数最少的条目将获胜。如有疑问,请在评论中提问。


那么,我们应该做的只是解释第一个音调/半音?
2014年

抱歉,我不明白你的问题。如果您要输入的话,输入内容不一定总是以补品开头
2014年

请提供更多示例。特别是那些不是从补品开始的。
2014年

1

1
大卫·大卫(David)根据这个元问题,我从开始挑战以来等待了12天后才将接受的答案最短。它只是发生在CJam答案被张贴当我准备接受下一个最短的一个。
苦艾酒

Answers:



8

C,171 146

i,b;main(int c,char**v){for(;c=v[1][i];)b|=c/65<<c*2%7+v[1][++i]%2*7;for(i=12;i--;)b&(1016056>>i)||printf("Dorian scale in %c%c",65+i*3%7,(i<5)*35);}

用C解析字符串并不是那么容易,所以我采用了一种更数学的方法。

我利用了第五圈。如果我们基于一次最多计数7个半音(以下称为“第五个”),按以下顺序排列音符,则发现在任何给定音阶中允许的所有音符都形成了连续的7音符块和所有禁止音符形成5个音符的连续块。

F C G D A E B F# C# G# D# A#

(这是一个圆圈,它绕回到F最后。)

上面序列中自然音符的位置可以计算为(ASCII code) * 2 % 7。然后,如果下一个字符为奇数(适用于#但不包含逗号,空格或零字节),则我们加7使其变尖。我们存储已使用笔记的位图。

数字243(二进制11111000)对应于A#Dorian等级中禁止使用的注释。我把这个乘以(1<<12)+1=4097一个神奇的数字1016056。右移以检查(通过ANDing)该旋律是否依次包含12个音阶中每个音阶的禁止音符。如果旋律不包含禁止的音符,则会打印音阶。

对于输出,我们需要打印以与上面的五分之五的循环相反的顺序编码的音阶名称,请记住,由于要向右移动,所以我们要倒退。)ASCII序列ADGCFBEADGCF由生成65+i*3%7。对于其中的前五个,还必须打印锋利的文字。

非高尔夫代码

i,b;
main(int c,char**v){
  for(;c=v[1][i];)                          //for each character in first commanline argument v[1]
                                               //if it is a letter (assume uppercase, ASCII 65 or over)
   b|=c/65<<c*2%7+v[1][++i]%2*7;               //convert to position in the circle of fifths. 
                                               //Add 7 if the next character is odd (ASCII'#')
                                               //leftshift 1 by this number and OR this with the contents of b.

  for(i=12;i--;)b&(1016056>>i)||printf         //if melody includes no prohibited notes for the scale i, print
   ("Dorian scale in %c%c",65+i*3%7,(i<5)*35); //the scale letter, and a # (ASCII 35) if required, otherwise an ASCII 0.
}

输入行为无效:如果提供的音符不足以明确确定音阶,它将输出所有可能的音阶。如果提供了不可能的音符组合,它将不会输出任何内容。注释必须用逗号(或其他具有偶数ASCII码<= 64的非空白字符)定界。不能将空格用作第一个空格之后的所有内容,将其视为不同的参数。ASCII代码> 64将按照所述方式解释为注释。


令我震惊的是,五分之一圈具有此属性!也许我可以用它来打更多的高尔夫球。

1
@Ray这实际上就是为什么我们拥有一组笔记。八度具有2:1的频率比。毕达哥拉斯(Pythagoras)定义的第五个比例为3:2,是八度之后最重要的音乐间隔。由于1.5 ^ 12接近但不等于2 ^ 7,因此将现代的等温五分之一压缩为1.4983,以便恰好有十二个五分之一适合7个八度。老式的解决方案是仅使用圈子中可用的12个注释中的7个注释。这就是为什么我们有一个基于7个不均匀间隔音符的音阶的原因。这不是随机的约定,背后有一些可靠的数学依据。
级圣河

为了方便起见,有许多乐器将音符排列在五分之五(小提琴以这种方式调音,贝司吉他以四分之一调音,比率为4:3)。最引人注目的示例(也是我所知的唯一乐器,它的音符以五分之一圈排列,以获得良好的声学设计)是steelpan:google.es/patents/US7696421。使用这种布局,您要敲击的音符旁边的音符是否有点响都没关系。
级圣河

4

哈斯克尔-152

w=words
n=w"C C# D D# E F F# G G# A A# B"
f s="Dorian scale in "++[n!!i|i<-[0..11],all(`elem`[(n++n)!!(i+j)|j<-[0,2,3,5,7,9,10]])s]!!0
main=interact$f.w

不打高尔夫球

type Note = String
type Scale = [Note]

notes :: [Note]
notes = words "C C# D D# E F F# G G# A A# B"

isScale :: Scale -> [Note] -> Bool
isScale scale notes = all (`elem` scale) notes

takeScale :: Int -> Scale
takeScale i = [(notes ++ notes) !! (i + j) | j <- [0, 2, 3, 5, 7, 9, 10]]

findScale :: [Note] -> Note
findScale xs = head [notes !! i | i <- [0..11], isScale (takeScale i) xs]

main = interact (("Dorian scale in "++) . findScale . words)

3

Python 2-177个字符

并没有那么短,但是我发现Python的乐趣在于即使在没有打高尔夫球的情况下,也可以在一行中编写多个嵌套的for循环。不幸的是,我不得不将输入语句放在单独的行上,这样它才不会多次执行。

j=set(raw_input().split(','))
print"Dorian Scale in",[x for x in[["A A# B C C# D D# E F F# G G#".split()[(b+n)%12]for n in[0,2,3,5,7,9,10]]for b in range(12)]if j<set(x)][0][0]

我不使用Python 3,但我相信这是一种罕见的情况,当print语句不需要更多字符时。由于print那里是一个函数,因此我可以使用*列表拆包运算符代替last 来抵消对括号的需要[0]


2
您还希望能够代替input用于raw_input在Python 3,保存4个字
comperendinous

“我发现用Python在一行中编写多个嵌套的for循环很高兴”:但是您在阅读它们时发现了欢乐吗?
卡勒布·保罗

@Wideshanks当然不是...这全都与只写代码有关!
feersum

3

红宝石-132

12.times{|i|$*[0].split(?,)-(g=(0..6).map{|j|%w{C C# D D# E F F# G G# A A# B}[-i+=~(58>>j&1)]})==[]?(puts"Dorain scale in "+g[0]):g}

从命令行参数输入。
例如ruby dorianscale.rb B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A

试试:ideone


3

哈斯克尔-140

利用@steveverrill引入的“第五圈”属性。如果让circle0 = words "C G D A E B F# C# G# D# A# F"circle = circle0 ++ circle0,那么我们可以通过连续记7个音符来构造所有音阶circle

scales = [take 7 . drop i $ circle | i <- [0..11]]

在以此方式构造的每个比例尺中scale !! 3,第4个元素是比例尺名称。

w=words
n=w"C G D A E B F# C# G# D# A# F"
f s="Dorian scale in "++[x!!3|x<-[take 7.drop i$n++n|i<-[0..]],all(`elem`x)s]!!0
main=interact$f.w

不打高尔夫球

type Note = String
type Scale = [Note]

notes :: [Note]
notes = words "C G D A E B F# C# G# D# A# F"

scales :: [Scale]
scales = [take 7 . drop i $ notes ++ notes | i <- [0..11]]

findScale :: [Note] -> Note
findScale xs = head [scale !! 3 | scale <- scales, all (`elem` scale) xs]

main = interact (("Dorian scale in "++) . findScale . words)

2

斯卡拉 130 128 127

print("Dorian scale in "+(".#?".r findAllIn "FCGDAEBF#C#G#D#A#"*2 sliding(7)find{l=>args(0)split','forall(l contains _)}get 3))

使用五分之一圈法。从命令行参数输入,即

scala dorianscale.scala B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A
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.