Organiser la musique de l'église grégorienne

19

Nous sommes en 930 et l'Église grégorienne a un problème. Ils ont des milliers de pages de musique de chant, mais le problème est que toutes les partitions ont été simplement jetées sur une pile au lieu d'avoir un véritable système d'organisation:

Image de partitions
Image de l'utilisateur gamerprinter à la Cartographers 'Guild .

L'Église a besoin d'organiser toutes les partitions, alors ils ont engagé un ingénieur logiciel médiéval pour écrire un programme pour l'organiser pour eux. Vous êtes l'ingénieur logiciel embauché. Cependant, le processus de compilation à l'époque médiévale implique que le programme soit écrit sur papier par une équipe de scribes bibliques lents. Pour réduire le temps nécessaire à l'équipe de scribes pour compiler votre code, vous devez rendre le programme aussi petit que possible.

L'Église veut que la musique de chant soit organisée en fonction de l'échelle musicale dans laquelle ils sont écrits. Toute la musique de chant de l'Église est écrite en échelles doriennes . Étant donné les notes d'un certain morceau de musique, votre programme affichera l'échelle dorienne dans laquelle il se trouve. Ici, je vais expliquer exactement ce qu'est une échelle dorienne. Si vous le savez déjà, vous pouvez ignorer cette section.

Il y a 12 notes possibles dans n'importe quelle mélodie. Les voici dans l'ordre:

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

Un demi-ton (représenté à l'aide de a S) incrémente d'un pas vers la droite, s'enchaînant (donc un demi-ton à partir de B serait de retour à C). Un ton (représenté par un T) est deux demi-tons. Par exemple, un demi-ton de F # serait G. Un ton de F # serait G #.

Pour créer une échelle dorienne, nous partons de n'importe quelle note de la liste, puis montons dans le modèle suivant, en listant les notes que nous rencontrons:

T, S, T, T, T, S

Un exemple. Je pars de A. Les notes de ma gamme Dorian deviennent:

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)

L'échelle a les notes A, B, C, D, E, F # et G. Parce que je partais de A, nous appellerons cela l' échelle Dorian en A . Il y a donc 12 échelles doriennes différentes, chacune nommée d'après la note à partir de laquelle elle est partie. Chacun d'eux utilise le même modèle de tons et de demi-tons, en commençant simplement par une position différente. Si mon explication n'est pas cohérente, vous pouvez également consulter Wikipedia .

L'entrée du programme peut être donnée à partir de ce qui est approprié pour votre programme (par exemple STDIN, argument de ligne de commande, raw_input()). Il peut ne pas être pré-initialisé dans une variable. L'entrée sera une liste de notes séparées par des virgules, représentant la mélodie de la pièce. Il peut y avoir des notes répétées. Il y aura toujours suffisamment de notes différentes dans l'entrée pour pouvoir déduire de manière décisive l'échelle de la pièce. Un exemple d'entrée:

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

La sortie du programme doit être la chaîne Dorian scale in X, où X est la note de départ de la gamme. La sortie de l'exemple d'entrée:

Dorian scale in B

En comparant cela avec l'échelle dorienne en B ( B C# D E F# G# A), nous voyons que toutes les notes de la mélodie sont dans cette échelle. La note C # n'est pas utilisée dans ce cas. Cependant, il y a suffisamment de notes pour identifier sans ambiguïté B Dorian comme la clé correcte. Aucune autre gamme dorienne ne convient, car quelle que soit la gamme que nous essayons, il y a toujours au moins une note de la mélodie qui n'appartient pas à la gamme.

Il s'agit du code golf, donc l'entrée avec le plus petit nombre de caractères l'emporte. Demandez dans les commentaires si vous avez des questions.

Absinthe
la source
Donc, ce que nous devons faire est d'interpréter uniquement le premier ton / demi-ton?
avall
@Avall Je suis désolé, je ne comprends pas votre question. L'entrée ne commencera pas toujours par le tonique, si c'est ce que vous demandez.
absinthe
Veuillez nous fournir plus d'exemples. Surtout ceux qui ne commencent pas par le tonique.
avall
1
Le problème inverse est l' échelle de la clé et du mode
Peter Taylor
1
@David Conformément à cette méta-question , j'ai attribué l'acceptation à la réponse la plus courte après une période d'attente de 12 jours depuis le début du défi. Il se trouve que la réponse CJam a été publiée juste au moment où j'allais accepter la prochaine plus courte.
absinthe

Réponses:

2

CJam - 61

C,q',/f{"FCGDAEB"_5<'#f++:s@m<7<:A-!{"Dorian scale in "A3=}*}

Essayez-le sur http://cjam.aditsu.net/

aditsu
la source
Wow, ce doit être ma victoire la plus rapide .. moins de 1 minute :)
aditsu
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);}

Analyser des chaînes en C n'est pas si simple, j'ai donc opté pour une approche plus mathématique.

Je profite du Cercle des cinquièmes. Si nous organisons les notes dans l'ordre suivant en comptant jusqu'à 7 demi-tons à la fois (connu comme un "cinquième"), nous constatons que toutes les notes autorisées dans une échelle donnée forment un bloc consécutif de 7 notes et toutes les notes interdites former un bloc consécutif de 5 notes.

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

(c'est un cercle, il s'enroule Fà la fin.)

La position d'une note naturelle dans la séquence ci-dessus peut être calculée comme suit (ASCII code) * 2 % 7. Ensuite, si le caractère suivant est impair (s'applique à #mais pas à la virgule, à l'espace ou à zéro octet), nous ajoutons 7 pour le rendre net. Nous stockons une image bitmap des notes qui ont été utilisées.

Le nombre 243(binaire 11111000) correspond aux notes interdites dans l'échelle de A # Dorian. J'ai multiplié cela par (1<<12)+1=4097pour donner le nombre magique 1016056. Ceci est décalé pour vérifier (par AND) si la mélodie contient des notes interdites pour chacune des 12 gammes à tour de rôle. Si la mélodie ne contient pas de notes interdites, la gamme est imprimée.

Pour la sortie, nous devons imprimer le nom de l'échelle codé dans l'ordre inverse pour faire défiler les quintes ci-dessus, rappelez-vous que nous allons en arrière parce que nous changeons les droits.) La séquence ASCII ADGCFBEADGCFest générée par 65+i*3%7. Pour les cinq premiers, il faut également imprimer un tranchant.

Code non golfé

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

Comportement d'entrée non valide: si des notes insuffisantes sont fournies pour déterminer sans ambiguïté l'échelle, il affichera toutes les échelles possibles. Si une combinaison impossible de notes est fournie, elle ne produira rien. Les notes doivent être délimitées par une virgule (ou tout autre caractère non blanc avec un code ASCII pair <= 64.) Les espaces ne peuvent pas être utilisés car tout ce qui suit après le premier espace serait considéré comme un argument différent. Les codes ASCII> 64 seront interprétés comme des notes de la manière décrite.

Level River St
la source
Cela m'a choqué que le cercle des quintes ait cette propriété! Je peux peut-être l'utiliser un peu plus pour jouer au golf.
Ray
1
@Ray C'est en fait pourquoi nous avons l'ensemble de notes que nous avons. L'octave a un rapport de fréquence 2: 1. Le cinquième tel que défini par Pythagore a un rapport de 3: 2 et est l'intervalle musical le plus important après l'octave. Étant donné que 1,5 ^ 12 est proche de 2 ^ 7 mais pas égal à lui, le cinquième tempéré égal moderne est réduit à 1,4983 de sorte que exactement 12 cinquièmes tiennent sur 7 octaves. La solution à l'ancienne était de n'utiliser que 7 notes sur les 12 disponibles du cercle. C'est pourquoi nous avons une échelle basée sur 7 notes inégalement espacées. Ce n'est pas une convention aléatoire, il y a des mathématiques solides derrière.
Level River St
Il y a un certain nombre d'instruments qui arrangent les notes en quintes pour des raisons de commodité (le violon est accordé de cette façon et la guitare basse est accordée en quarts, ce qui est un rapport de 4: 3). L'exemple le plus frappant (et le seul instrument que je connaisse qui ait des notes disposées en cercle de quintes pour une bonne conception acoustique) est l'acier: google.es/patents/US7696421 . Avec cette disposition, peu importe si la note à côté de celle que vous frappez sonne un peu.
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

Non golfé

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)
Rayon
la source
3

Python 2 - 177 caractères

Ce n'est pas si court, mais je trouve que la joie de Python est d'écrire plusieurs boucles imbriquées sur une même ligne, même en ne jouant pas au golf. Malheureusement, j'ai dû mettre la déclaration d'entrée sur une ligne distincte afin qu'elle ne s'exécute pas plus d'une fois.

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]

Je n'utilise pas Python 3, mais je pense que c'est un cas rare où l'instruction print n'aurait pas besoin de plus de caractères. Puisqu'il printy a une fonction là-bas, je serais en mesure de compenser le besoin de parenthèses avec l'utilisation de l' *opérateur de déballage de liste pour remplacer le dernier [0].

feersum
la source
2
Vous souhaitez également être en mesure de remplacer inputpour raw_inputet enregistrer 4 caractères en Python 3.
comperendinous
"Je trouve la joie de Python d'écrire plusieurs boucles imbriquées sur une seule ligne": mais trouvez-vous la joie de les lire?
Caleb Paul
@Wideshanks Bien sûr que non ... il s'agit du code en écriture seule!
feersum
3

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

Entrée depuis les arguments de la ligne de commande.
par exempleruby dorianscale.rb B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A

Essayez-le à: ideone

Vectorisé
la source
3

Haskell - 140

Utilisez la propriété Circle of Fifths introduite par @steveverrill. Si nous laissons circle0 = words "C G D A E B F# C# G# D# A# F"et circle = circle0 ++ circle0, nous pouvons construire toutes les gammes en prenant 7 notes consécutives circle.

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

Dans chaque échelle construite de cette manière scale !! 3, le 4ème élément est le nom de l'échelle.

Code

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

Non golfé

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)
Rayon
la source
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))

En utilisant la méthode du cercle des quintes. Entrée depuis les arguments de la ligne de commande ie

scala dorianscale.scala B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A
paradigmsort
la source