Organizza la musica della Chiesa gregoriana


19

L'anno è il 930 e la Chiesa Gregoriana sta avendo un problema. Hanno migliaia di pagine di canti musicali, ma il problema è che tutti gli spartiti sono stati semplicemente gettati su una pila invece di avere un vero sistema organizzativo:

Immagine di spartiti musicali
Immagine dell'utente gamerprinter alla Cartographers 'Guild .

La Chiesa ha bisogno di organizzare tutti gli spartiti, quindi hanno assunto un ingegnere informatico medievale per scrivere un programma per organizzarlo per loro. Sei l'ingegnere del software che è stato assunto. Tuttavia, il processo di compilazione in epoca medievale prevede che il programma venga scritto su carta da una squadra di lenti scribi biblici. Per ridurre il tempo impiegato dal team di scribi per compilare il codice, è necessario rendere il programma il più piccolo possibile.

La Chiesa vuole che la musica del canto sia organizzata in base alla scala musicale in cui sono scritti. Tutta la musica del canto della Chiesa è scritta in scala dorica . Date le note di un certo brano musicale, il vostro programma produrrà la scala dorica in cui si trova. Qui, spiegherò esattamente cos'è una scala dorica. Se lo sai già, puoi saltare questa sezione.

Vi sono 12 note possibili in qualsiasi melodia. Eccoli in ordine:

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

Un semitono (rappresentato usando a S) sta incrementando di un passo verso destra, avvolgendosi (quindi un semitono su da B tornerebbe a C). Un tono (rappresentato usando a T) è di due semitoni. Ad esempio, un semitono su da F # sarebbe G. Un tono da F # sarebbe G #.

Per creare una scala dorica, partiamo da qualsiasi nota nell'elenco, quindi ci spostiamo verso l'alto nel seguente schema, elencando le note che incontriamo:

T, S, T, T, T, S

Un esempio. Comincio da A. Le note della mia scala dorica diventano:

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)

La scala ha le note di A, B, C, D, E, F #, e G. Poiché ho iniziato da A, chiameremo questa la scala Dorian in A . Esistono quindi 12 diverse scale doriche, ognuna delle quali prende il nome dalla nota da cui è partita. Ognuno di essi utilizza lo stesso modello di toni e semitoni, a partire da una posizione diversa. Se la mia spiegazione non è coerente, puoi anche consultare Wikipedia .

L'input del programma può essere dato da qualsiasi cosa sia appropriata per il tuo programma (es. STDIN, argomento della riga di comando, raw_input()). Potrebbe non essere preinizializzato in una variabile. L'input sarà un elenco di note separate da virgola, che rappresentano la melodia del brano. Potrebbero esserci note ripetute. Ci saranno sempre abbastanza note diverse nell'input per essere in grado di dedurre in modo decisivo la scala del pezzo. Un esempio di input:

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

L'output del programma dovrebbe essere la stringa Dorian scale in X, dove X è la nota iniziale della scala. L'output dell'input di esempio:

Dorian scale in B

Confrontando questo con la scala dorica in B ( B C# D E F# G# A) vediamo che tutte le note della melodia sono all'interno di questa scala. La nota C # non è utilizzata in questo caso. Tuttavia, ci sono note sufficienti per identificare senza ambiguità B Dorian come chiave corretta. Nessun'altra scala dorica si adatta, perché qualunque altra scala proviamo, c'è sempre almeno una nota della melodia che non appartiene alla scala.

Questo è il codice golf, quindi vince la voce con il minor numero di caratteri. Chiedi nei commenti se hai domande.


Quindi, ciò che dovremmo fare è interpretare solo il primo tono / semitono?
Avall

@Avall, mi dispiace, non capisco la tua domanda. L'input non inizierà sempre con il tonico, se è quello che stai chiedendo.
assenzio

Per favore forniscici altri esempi. Soprattutto quelli che non iniziano con il tonico.
Avall


1
@David In base a questa meta domanda , ho assegnato l'accettazione alla risposta più breve dopo un periodo di attesa di 12 giorni da quando ho iniziato la sfida. È appena successo che la risposta di CJam è stata pubblicata proprio quando avrei accettato il successivo più breve.
assenzio

Risposte:



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

L'analisi delle stringhe in C non è così semplice, quindi ho optato per un approccio più matematico.

Approfitto del Circle of Fifths. Se disponiamo le note nel seguente ordine in base al conteggio di 7 semitoni alla volta (noto come "quinto"), scopriamo che tutte le note consentite in una data scala formano un blocco consecutivo di 7 note e tutte le note proibite formare un blocco consecutivo di 5 note.

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

(è un cerchio, si avvolge Falla fine.)

La posizione di una nota naturale nella sequenza sopra può essere calcolata come (ASCII code) * 2 % 7. Quindi se il carattere successivo è dispari (vale #ma non virgola, spazio o zero byte) aggiungiamo 7 per renderlo più nitido. Memorizziamo una bitmap delle note che sono state utilizzate.

Il numero 243(binario 11111000) corrisponde alle note proibite nella scala di A # Dorian. Ho moltiplicato questo (1<<12)+1=4097per dare il numero magico 1016056. Questo è spostato per verificare (tramite ANDing) se la melodia contiene note proibite per ciascuna delle 12 scale a turno. Se la melodia non contiene note vietate, la scala viene stampata.

Per l'output dobbiamo stampare il nome della scala codificato nell'ordine inverso per scorrere i quinti sopra, ricorda che stiamo andando indietro perché stiamo spostando i diritti.) La sequenza ASCII ADGCFBEADGCFè generata da 65+i*3%7. Per i primi cinque di questi deve essere stampato anche uno sharp.

Codice 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 di input non valido: se vengono fornite note insufficienti per determinare in modo inequivocabile la scala, produrrà tutte le scale possibili. Se viene fornita una combinazione impossibile di note, non verrà prodotto nulla. Le note devono essere delimitate da una virgola (o da un altro carattere non bianco con un codice ASCII pari <= 64.) Gli spazi non possono essere utilizzati poiché tutto dopo il primo spazio sarebbe considerato un argomento diverso. I codici ASCII> 64 verranno interpretati come note nel modo descritto.


Mi ha scioccato che il cerchio dei quinti abbia questa proprietà! Forse posso usarlo per giocare un po 'di più.
Ray

1
@Ray Questo è in realtà il motivo per cui abbiamo l'insieme di note che abbiamo. L'ottava ha un rapporto di frequenza 2: 1. Il quinto, come definito da Pitagora, ha un rapporto di 3: 2 ed è l'intervallo musicale più importante dopo l'ottava. Poiché 1,5 ^ 12 è vicino ma non uguale a 2 ^ 7, il quinto moderno temperato uguale viene schiacciato a 1,4983 in modo che esattamente 12 quinti si adattino a 7 ottave. La vecchia soluzione consisteva nell'usare solo 7 note su 12 disponibili dal cerchio. Ecco perché abbiamo una scala basata su 7 note con spaziatura irregolare. Non è una convenzione casuale, c'è qualche solida matematica dietro di essa.
Level River St

Esistono numerosi strumenti che organizzano le note in quinte per motivi di praticità (il violino viene accordato in questo modo e il basso viene accordato in quarte, con un rapporto di 4: 3). L'esempio più eclatante (e l'unico strumento che conosco con note disposte in un cerchio di quinte per un buon design acustico) è lo steelpan: google.es/patents/US7696421 . Con questo layout non importa se la nota accanto a quella che stai suonando suona un po '.
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 caratteri

Non è così breve, ma trovo la gioia di Python di scrivere più loop annidati per loop in una riga, anche quando non si gioca a golf. Sfortunatamente, ho dovuto mettere l'istruzione di input su una riga separata in modo che non venisse eseguita più di una volta.

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]

Non uso Python 3, ma credo che questa sia un'istanza rara in cui l'istruzione print non avrebbe bisogno di più caratteri. Dal momento che printè una funzione lì, sarei in grado di compensare la necessità di parentesi con l'uso *dell'operatore di decompressione dell'elenco per sostituire l'ultimo [0].


2
Si sarebbe anche in grado di sostituire inputper raw_inpute salvare 4 caratteri in Python 3.
comperendinous

"Trovo la gioia di Python di scrivere più cicli annidati per loop in una riga": ma trovi gioia nel leggerli?
Caleb Paul,

@Wideshanks Certo che no ... si tratta solo del codice di sola scrittura!
feersum

3

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

Input dalla riga di comando args.
per esempioruby dorianscale.rb B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A

Provalo su: ideone


3

Haskell - 140

Utilizza la proprietà Circle of Fifths introdotta da @steveverrill. Se lasciamo circle0 = words "C G D A E B F# C# G# D# A# F"e circle = circle0 ++ circle0, allora possiamo costruire tutte le scale prendendo 7 note consecutive in circle.

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

In ogni scala costruita in questo modo scale !! 3, il 4 ° elemento è il nome della scala.

Codice

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

Utilizzando il metodo del cerchio dei quinti. Input dalla riga di comando args ie

scala dorianscale.scala B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.