Organize a música da Igreja Gregoriana


19

O ano é 930 e a Igreja Gregoriana está tendo um problema. Eles têm milhares de páginas de música cantada, mas o problema é que toda a partitura foi simplesmente jogada em uma pilha em vez de ter um sistema de organização real:

Imagem de partituras
Imagem do usuário gamerprinter no Cartographers 'Guild .

A Igreja precisa organizar todas as partituras, para que eles contrataram um engenheiro de software medieval para escrever um programa para organizá-lo para eles. Você é o engenheiro de software que foi contratado. No entanto, o processo de compilação nos tempos medievais envolve a escrita do programa no papel por uma equipe de escribas bíblicos lentos. Para diminuir o tempo que a equipe de escribas leva para compilar seu código, você deve tornar o programa o menor possível.

A Igreja quer que a música cantada seja organizada com base na escala musical em que está escrita. Toda a música cantada da Igreja é escrita em escalas dóricas . Dadas as notas de uma certa peça de música, seu programa produzirá a escala dórica em que está. Aqui, explicarei exatamente o que é uma escala dórica. Se você já sabe, pode pular esta seção.

Existem 12 notas possíveis em qualquer melodia. Aqui estão eles em ordem:

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

Um semitom (representado usando a S) está incrementando um passo para a direita, envolvendo (para que um semitom acima de B retorne a C). Um tom (representado usando a T) é de dois semitons. Por exemplo, um semitom acima de F # seria G. Um tom acima de F # seria G #.

Para criar uma escala dórica, partimos de qualquer anotação da lista e, em seguida, avançamos no seguinte padrão, listando as anotações que encontramos:

T, S, T, T, T, S

Um exemplo. Começo com A. As notas da minha escala Doriana se tornam:

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 escala tem as notas A, B, C, D, E, F #, e G. Por ter começado a partir de A, vamos chamar isso de escala Dorian em A . Existem, portanto, 12 escalas dóricas diferentes, sendo que cada uma delas recebe o nome da nota da qual elas começaram. Cada um deles usa o mesmo padrão de tons e semitons, apenas começando de uma posição diferente. Se minha explicação não for coerente, você também pode consultar a Wikipedia .

A entrada do programa pode ser fornecida a partir do que for apropriado para o seu programa (por exemplo, STDIN, argumento da linha de comando raw_input()). Pode não ser pré-inicializado em uma variável. A entrada será uma lista de notas separadas por vírgula, representando a melodia da peça. Pode haver notas repetidas. Sempre haverá notas diferentes suficientes na entrada para poder deduzir decisivamente a escala da peça. Um exemplo de entrada:

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

A saída do programa deve ser a sequência Dorian scale in X, em que X é a nota inicial da escala. A saída da entrada de exemplo:

Dorian scale in B

Comparando isso com a escala Doriana em B ( B C# D E F# G# A), vemos que todas as notas da melodia estão dentro dessa escala. A nota C # não é usada neste caso. No entanto, existem notas suficientes para identificar inequivocamente B Dorian como a chave correta. Nenhuma outra escala dórica se encaixa, porque qualquer que seja a outra escala que tentemos, sempre há pelo menos uma nota da melodia que não pertence à escala.

Isso é código de golfe, portanto a entrada com o menor número de caracteres vence. Pergunte nos comentários se você tiver dúvidas.


Então, o que devemos fazer é interpretar apenas o primeiro tom / semitom?
avall

@ Avall Me desculpe, eu não entendo sua pergunta. A entrada nem sempre começa com o tônico, se é isso que você está perguntando.
absinthe

Por favor, forneça-nos mais exemplos. Especialmente aqueles que não começam com o tônico.
avall

11
O problema inverso é Escala de chave e modo
Peter Taylor

11
@ David Conforme esta meta questão , concordei com a resposta mais curta após um período de espera de 12 dias desde que comecei o desafio. Aconteceu que a resposta CJam foi postada exatamente quando eu aceitaria a próxima resposta mais curta.
absinto

Respostas:



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);}

A análise de cadeias de caracteres em C não é tão fácil, então fui para uma abordagem mais matemática.

Aproveito o Círculo dos Quintos. Se organizarmos as notas na seguinte ordem com base na contagem de 7 semitons por vez (conhecido como "quinto"), descobriremos que todas as notas permitidas em uma determinada escala formam um bloco consecutivo de 7 notas e todas as notas proibidas formar um bloco consecutivo de 5 notas.

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

(é um círculo, volta ao Ffinal.)

A posição de uma nota natural na sequência acima pode ser calculada como (ASCII code) * 2 % 7. Então, se o próximo caractere for ímpar (aplica-se a #vírgula, espaço ou zero byte), adicionamos 7 para torná-lo nítido. Armazenamos um bitmap das notas que foram usadas.

O número 243(binário 11111000) corresponde às notas proibidas na escala de A # Dorian. Eu multipliquei isso (1<<12)+1=4097para dar o número mágico 1016056. É ativado o direito de verificar (por AND) se a melodia contém notas proibidas para cada uma das 12 escalas por vez. Se a melodia não contiver notas proibidas, a escala será impressa.

Para a saída, precisamos imprimir o nome da balança codificado na ordem inversa para o ciclo de quintos acima, lembre-se de que estamos voltando atrás porque estamos mudando de direitos.) A sequência ASCII ADGCFBEADGCFé gerada por 65+i*3%7. Nos cinco primeiros, também deve ser impressa uma nitidez.

Código ungolfed

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.
}

Comportamento de entrada inválido: se notas insuficientes forem fornecidas para determinar sem ambiguidade a escala, ela produzirá todas as escalas possíveis. Se uma combinação impossível de notas for fornecida, ela não produzirá nada. As anotações devem ser delimitadas por vírgula (ou outro caractere que não seja um espaço em branco com um código ASCII par <<64.) Os espaços não podem ser usados, pois tudo após o primeiro espaço seria considerado um argumento diferente. Os códigos ASCII> 64 serão interpretados como notas da maneira descrita.


Chocou-me que o círculo dos quintos tenha essa propriedade! Talvez eu possa usá-lo para jogar um pouco mais.
Ray

11
@ Ray É por isso que temos o conjunto de notas que temos. A oitava tem uma taxa de frequência 2: 1. O quinto, conforme definido por Pitágoras, possui uma proporção de 3: 2 e é o intervalo musical mais importante após a oitava. Como 1,5 ^ 12 é próximo, mas não é igual a 2 ^ 7, o quinto temperado igual moderno é reduzido para 1,4983, de modo que exatamente 12 quintos se encaixam em 7 oitavas. A solução antiquada era usar apenas 7 notas das 12 disponíveis do círculo. É por isso que temos uma escala baseada em 7 notas desigualmente espaçadas. Não é uma convenção aleatória, há algumas matemáticas sólidas por trás disso.
Level River St

Existem vários instrumentos que organizam notas em quintos por razões de conveniência (o violino é afinado dessa maneira e o baixo é afinado em quartos, que é uma proporção de 4: 3). O exemplo mais impressionante (e o único instrumento que conheço que possui notas dispostas em um círculo de quintos para um bom design acústico) é o steelpan: google.es/patents/US7696421 . Com esse layout, não importa se a nota ao lado da que você está tocando soa um pouco.
Level River St

4

Haskell - 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

Ungolfed

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 caracteres

Não é tão curto, mas acho que é uma alegria para o Python escrever vários loops aninhados em uma linha, mesmo quando não estiver jogando golfe. Infelizmente, tive que colocar a instrução de entrada em uma linha separada para que não fosse executada mais de uma vez.

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]

Não uso o Python 3, mas acredito que esse seja um caso raro em que a declaração de impressão não precisaria de mais caracteres. Como printexiste uma função lá, eu seria capaz de compensar a necessidade de parênteses com o uso do *operador de desempacotamento de lista para substituir o último [0].


2
Você também seria capaz de substituir inputpor raw_inpute salvar 4 caracteres em Python 3.
comperendinous

"Acho que é uma alegria para o Python escrever vários loops aninhados em uma linha": mas você sente alegria ao lê-los?
Caleb Paul

@ Wideshanks Claro que não ... é tudo sobre o código somente para gravação!
precisa saber é

3

Ruby - 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}

Entrada da linha de comando args.
por exemploruby dorianscale.rb B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A

Experimente: ideone


3

Haskell - 140

Utilize a propriedade Circle of Fifths introduzida por @steveverrill. Se deixarmos circle0 = words "C G D A E B F# C# G# D# A# F"e circle = circle0 ++ circle0, podemos construir todas as escalas anotando 7 notas consecutivas circle.

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

Em cada escala construída dessa maneira scale !! 3, o quarto elemento é o nome da escala.

Código

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

Ungolfed

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

Scala, 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))

Usando o método do círculo dos quintos. Entrada da linha de comando args

scala dorianscale.scala B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.