Organisieren Sie die Musik der Gregorianischen Kirche

19

Wir schreiben das Jahr 930 und die Gregorianische Kirche hat ein Problem. Sie haben Tausende von Seiten mit Gesangsmusik, aber das Problem ist, dass alle Noten einfach auf einen Stapel geworfen wurden, anstatt über ein echtes Organisationssystem zu verfügen:

Bild von Noten
Bild von User gamerprinter bei Cartographers 'Guild .

Die Kirche muss alle Noten organisieren, deshalb haben sie einen mittelalterlichen Software-Ingenieur beauftragt, ein Programm zu schreiben, um es für sie zu organisieren. Sie sind der Softwareentwickler, der eingestellt wurde. Bei der Zusammenstellung im Mittelalter wird das Programm jedoch von einem Team langsamer Bibelschreiber auf Papier geschrieben. Um die Zeit zu verkürzen, die das Schreiberteam benötigt, um Ihren Code zu kompilieren, müssen Sie das Programm so klein wie möglich halten.

Die Kirche möchte, dass die Gesangsmusik auf der Grundlage der musikalischen Skala organisiert wird, in der sie geschrieben ist. Alle Gesangsmusik der Kirche ist in dorischen Skalen geschrieben . Ausgehend von den Noten eines bestimmten Musikstücks gibt Ihr Programm die dorische Skala aus, in der es sich befindet. Hier erkläre ich genau, was eine dorische Skala ist. Wenn Sie es bereits wissen, können Sie diesen Abschnitt überspringen.

Es gibt 12 mögliche Noten in jeder Melodie. Hier sind sie in Ordnung:

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

Ein Halbton (dargestellt mit a S) wird um einen Schritt nach rechts erhöht und umbrochen (ein Halbton von B nach C). Ein Ton (dargestellt mit einem T) besteht aus zwei Halbtönen. Zum Beispiel wäre ein Halbton von F # nach oben G. Ein Ton von F # nach oben wäre G #.

Um eine dorische Skala zu erstellen, gehen wir von einer beliebigen Notiz in der Liste aus und bewegen uns dann in folgendem Muster nach oben, wobei wir die Noten auflisten, auf die wir stoßen:

T, S, T, T, T, S

Ein Beispiel. Ich gehe von A aus. Die Noten meiner dorischen Skala werden:

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)

Die Skala hat die Noten A, B, C, D, E, F # und G. Da ich von A ausging, werden wir dies die dorische Skala in A nennen . Es gibt also 12 verschiedene dorische Skalen, von denen jede nach der Note benannt ist, mit der sie begonnen haben. Jeder von ihnen verwendet das gleiche Muster von Tönen und Halbtönen, nur ausgehend von einer anderen Position. Wenn meine Erklärung nicht kohärent ist, können Sie auch Wikipedia konsultieren .

Die Eingabe des Programms kann von einer beliebigen Stelle aus erfolgen, die für Ihr Programm geeignet ist (z. B. STDIN, Befehlszeilenargument, raw_input()). Es kann sein, dass es in einer Variablen nicht vorinitialisiert ist. Die Eingabe ist eine Liste von durch Kommas getrennten Noten, die die Melodie des Stücks darstellen. Möglicherweise werden Notizen wiederholt. Die Eingabe wird immer genug unterschiedliche Noten enthalten, um die Tonleiter des Stückes entscheidend bestimmen zu können. Ein Beispiel Input:

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

Die Ausgabe des Programms sollte der String sein Dorian scale in X, wobei X die Startnote der Skala ist. Die Ausgabe der Beispieleingabe:

Dorian scale in B

Vergleicht man dies mit der Dorian-Skala in B ( B C# D E F# G# A), so sieht man, dass alle Noten der Melodie innerhalb dieser Skala liegen. Die Note C # wird in diesem Fall nicht verwendet. Es gibt jedoch genügend Hinweise, um B Dorian eindeutig als den richtigen Schlüssel zu identifizieren. Keine andere dorische Tonleiter passt, denn egal welche andere Tonleiter wir versuchen, es gibt immer mindestens eine Note der Melodie, die nicht zur Tonleiter gehört.

Dies ist Codegolf, daher gewinnt der Eintrag mit der kürzesten Anzahl von Zeichen. Fragen Sie in den Kommentaren, wenn Sie Fragen haben.

Absinth
quelle
Was wir also tun sollten, ist, nur den ersten Ton / Halbton zu interpretieren?
Avall
@Avall Es tut mir leid, ich verstehe deine Frage nicht. Die Eingabe beginnt nicht immer mit dem Tonic, wenn Sie danach fragen.
Absinth
Bitte nennen Sie uns weitere Beispiele. Vor allem diejenigen, die nicht mit dem Tonic anfangen.
Avall
1
Das umgekehrte Problem ist Skalieren von Taste und Modus
Peter Taylor
1
@David Aufgrund dieser Meta-Frage erteilte ich nach einer Wartezeit von 12 Tagen seit Beginn der Challenge die Bestätigungsmeldung an die kürzeste Antwort. Es ist einfach passiert die CJam Antwort wurde gepostet Recht , wenn ich das nächste kürzeste akzeptieren würde.
Absinth

Antworten:

2

CJam - 61

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

Versuchen Sie es unter http://cjam.aditsu.net/

aditsu
quelle
Wow, das muss mein schnellster Sieg sein .. weniger als 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);}

Das Parsen von Strings in C ist nicht so einfach, deshalb habe ich mich für einen mathematischeren Ansatz entschieden.

Ich nutze den Kreis der Fünftel. Wenn wir die Noten in der folgenden Reihenfolge anordnen, indem wir 7 Halbtöne gleichzeitig zählen (als "fünfte" bezeichnet), stellen wir fest, dass alle in einer bestimmten Skala zugelassenen Noten einen aufeinanderfolgenden Block von 7 Noten und alle verbotenen Noten bilden Bilden Sie einen fortlaufenden Block mit 5 Noten.

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

(Es ist ein Kreis, der sich Fam Ende nach hinten dreht .)

Die Position einer natürlichen Note in der obigen Reihenfolge kann wie folgt berechnet werden (ASCII code) * 2 % 7. Wenn das nächste Zeichen ungerade ist (gilt #jedoch nicht für Komma, Leerzeichen oder Null-Byte), addieren wir 7, um es scharf zu machen. Wir speichern eine Bitmap der verwendeten Notizen.

Die Zahl 243(binär 11111000) entspricht den in der Skala von A # Dorian verbotenen Noten. Ich multiplizierte dies mit (1<<12)+1=4097der magischen Zahl 1016056. Dies wird mit der rechten Maustaste verschoben, um zu überprüfen (durch UND-Verknüpfung), ob die Melodie verbotene Noten für jede der 12 Skalen enthält. Wenn die Melodie keine verbotenen Noten enthält, wird die Skala gedruckt.

Denken Sie daran, dass wir für die Ausgabe den Skalennamen in umgekehrter Reihenfolge drucken müssen, um die oben genannten Fünftel zu durchlaufen, da wir die Rechte verschieben.) Die ASCII-Sequenz ADGCFBEADGCFwird von generiert 65+i*3%7. Für die ersten fünf muss zusätzlich ein Scharfes gedruckt werden.

Ungolfed Code

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

Ungültiges Eingabeverhalten: Werden nicht genügend Noten zur Verfügung gestellt, um die Skala eindeutig zu bestimmen, werden alle möglichen Skalen ausgegeben. Wenn eine unmögliche Kombination von Noten angegeben wird, wird nichts ausgegeben. Notizen müssen durch ein Komma (oder ein anderes Nicht-Leerzeichen mit einem geraden ASCII-Code <= 64) getrennt werden. Leerzeichen können nicht verwendet werden, da alles, was nach dem ersten Leerzeichen steht, als anderes Argument angesehen wird. ASCII-Codes> 64 werden wie beschrieben als Hinweise interpretiert.

Level River St
quelle
Es hat mich schockiert, dass der Quintenkreis diese Eigenschaft hat! Vielleicht kann ich damit ein bisschen mehr Golf spielen.
Ray
1
@ Ray Das ist eigentlich der Grund, warum wir die Notizen haben, die wir haben. Die Oktave hat ein Frequenzverhältnis von 2: 1. Die von Pythagoras definierte fünfte hat ein Verhältnis von 3: 2 und ist das wichtigste musikalische Intervall nach der Oktave. Da 1,5 ^ 12 nahe bei 2 ^ 7 liegt, wird die moderne gleich temperierte Quinte auf 1,4983 gedrückt, sodass genau 12 Quinten in 7 Oktaven passen. Die altmodische Lösung bestand darin, nur 7 Noten aus den verfügbaren 12 aus dem Kreis zu verwenden. Aus diesem Grund haben wir eine Skala, die auf 7 ungleichmäßig verteilten Noten basiert. Es ist keine zufällige Konvention, dahinter steckt eine solide Mathematik.
Level River St
Es gibt eine Reihe von Instrumenten, die aus praktischen Gründen Noten in Quinten anordnen (die Violine ist auf diese Weise gestimmt, und die Bassgitarre ist in Quinten gestimmt, was einem Verhältnis von 4: 3 entspricht). Das auffälligste Beispiel (und das einzige mir bekannte Instrument, dessen Noten für ein gutes akustisches Design in einem Quintenkreis angeordnet sind) ist das Steelpan: google.es/patents/US7696421 . Bei diesem Layout spielt es keine Rolle, ob die Note neben der, die Sie schlagen, etwas klingelt.
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)
Strahl
quelle
3

Python 2 - 177 Zeichen

Es ist nicht so kurz, aber ich finde es die Freude von Python, mehrere geschachtelte for-Loops in einer Zeile zu schreiben, auch wenn Sie nicht Golf spielen. Leider musste ich die Eingabeanweisung in eine separate Zeile stellen, damit sie nicht mehr als einmal ausgeführt wurde.

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]

Ich verwende Python 3 nicht, aber ich glaube, dies ist ein seltener Fall, in dem die print-Anweisung keine weiteren Zeichen benötigt. Da printes dort eine Funktion gibt, wäre ich in der Lage, die Notwendigkeit von Klammern durch die Verwendung des *Operators zum Auspacken der Liste auszugleichen, um die letzte zu ersetzen [0].

Feersum
quelle
2
Sie würden auch ersetzen können , inputfür raw_inputund speichern 4 Zeichen in Python 3.
comperendinous
"Ich finde es die Freude von Python, mehrere verschachtelte for-Schleifen in einer Zeile zu schreiben": Aber macht es Ihnen Spaß, sie zu lesen?
Caleb Paul
@Wideshanks Natürlich nicht ... es geht nur um den Nur-Schreib-Code!
Feersum
3

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

Eingabe über Befehlszeilenargumente.
z.Bruby dorianscale.rb B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A

Probieren Sie es bei: ideone

Vektorisiert
quelle
3

Haskell - 140

Nutzen Sie die von @steveverrill eingeführte Circle of Fifths-Eigenschaft. Wenn wir circle0 = words "C G D A E B F# C# G# D# A# F"und lassen circle = circle0 ++ circle0, können wir alle Skalen konstruieren, indem wir 7 aufeinanderfolgende Noten aufnehmen circle.

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

In jeder auf diese Weise konstruierten Skala scale !! 3ist das vierte Element der Skalenname.

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

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)
Strahl
quelle
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))

Nach der Methode des Quintenkreises. Eingabe von Kommandozeilenargumenten dh

scala dorianscale.scala B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A
Paradigmensort
quelle