Atur Musik Gereja Gregorian


19

Tahun adalah 930, dan Gereja Gregorian mengalami masalah. Mereka memiliki ribuan halaman nyanyian musik, tetapi masalahnya adalah bahwa semua lembaran musik hanya dibuang ke tumpukan alih-alih memiliki sistem organisasi yang nyata:

Gambar lembaran musik
Gambar oleh pengguna gamerprinter di Cartographers 'Guild .

Gereja perlu mengatur semua lembaran musik, sehingga mereka telah menyewa seorang insinyur perangkat lunak abad pertengahan untuk menulis sebuah program untuk mengaturnya bagi mereka. Anda adalah insinyur perangkat lunak yang telah dipekerjakan. Namun, proses kompilasi di abad pertengahan melibatkan program yang dituliskan di atas kertas oleh tim penulis Alkitab yang lambat. Untuk mengurangi waktu yang dibutuhkan oleh tim juru tulis untuk mengkompilasi kode Anda, Anda harus membuat program sekecil mungkin.

Gereja ingin musik nyanyian disusun berdasarkan skala musik yang mereka tulis. Semua musik nyanyian Gereja ditulis dalam skala Dorian . Mengingat catatan dari sepotong musik tertentu, program Anda akan menampilkan skala Dorian yang ada di sini. Di sini, saya akan menjelaskan dengan tepat apa skala Dorian itu. Jika sudah tahu, Anda dapat melewati bagian ini.

Ada 12 nada yang memungkinkan dalam melodi apa pun. Berikut urutannya:

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

Sebuah semitone (diwakili menggunakan S) adalah incrementing satu langkah ke kanan, membungkus di sekitar (jadi semitone naik dari B akan kembali ke C). Sebuah nada (diwakili menggunakan T) adalah dua semitone. Misalnya, semitone yang naik dari F # adalah G. Nada yang naik dari F # adalah G #.

Untuk membuat skala Dorian, kita mulai dari catatan apa pun dalam daftar, dan kemudian naik ke atas dalam pola berikut, daftar catatan yang kita temui:

T, S, T, T, T, S

Sebuah contoh. Saya mulai dari A. Catatan skala Dorian saya menjadi:

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)

Skala ini memiliki catatan A, B, C, D, E, F #, dan G. Karena saya mulai dari A, kita akan menyebutnya skala Dorian di A . Oleh karena itu ada 12 skala Dorian yang berbeda, masing-masing diberi nama sesuai dengan catatan yang mereka mulai. Masing-masing dari mereka menggunakan pola nada dan semi-nada yang sama, hanya mulai dari posisi yang berbeda. Jika penjelasan saya tidak masuk akal, Anda juga dapat berkonsultasi dengan Wikipedia .

Input program dapat diberikan dari apa pun yang sesuai untuk program Anda (mis. STDIN, argumen baris perintah, raw_input()). Ini mungkin tidak diinisialisasi dalam suatu variabel. Input akan berupa daftar catatan yang dipisah koma, mewakili melodi karya. Mungkin ada catatan berulang. Akan selalu ada cukup berbagai catatan dalam input untuk dapat secara meyakinkan menyimpulkan skala karya. Contoh input:

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

Output dari program harus berupa string Dorian scale in X, di mana X adalah nada awal dari skala. Output dari input contoh:

Dorian scale in B

Membandingkan ini dengan skala Dorian dalam B ( B C# D E F# G# A) kita melihat bahwa semua nada melodi berada dalam skala ini. Catatan C # tidak digunakan dalam kasus ini. Namun ada catatan yang cukup untuk secara jelas mengidentifikasi B Dorian sebagai kunci yang benar. Tidak ada skala Dorian lain yang cocok, karena skala apa pun yang kami coba, selalu ada setidaknya satu nada melodi yang tidak termasuk dalam skala.

Ini adalah kode golf, jadi entri dengan jumlah karakter terpendek akan menang. Tanyakan dalam komentar jika Anda memiliki pertanyaan.


Jadi, apa yang harus kita lakukan adalah hanya menafsirkan nada / semitone pertama?
avall

@Allall Maaf, saya tidak mengerti pertanyaan Anda. Masukan tidak akan selalu dimulai dengan tonik, jika itu yang Anda tanyakan.
absinthe

Tolong berikan kami lebih banyak contoh. Terutama yang tidak mulai dengan tonik.
avall

1
Masalah terbalik adalah Skala dari tombol dan mode
Peter Taylor

1
@David Sesuai pertanyaan meta ini , saya memberikan jawaban untuk jawaban tersingkat setelah jangka waktu tunggu 12 hari sejak saya memulai tantangan. Kebetulan jawaban CJam diposting tepat ketika saya akan menerima yang terpendek berikutnya.
absinth

Jawaban:



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

Mengurai string dalam C tidak semudah itu, jadi saya pergi untuk pendekatan yang lebih matematis.

Saya memanfaatkan Lingkaran Kelima. Jika kami mengatur catatan dalam urutan berikut berdasarkan penghitungan hingga 7 semiton pada satu waktu (dikenal sebagai "kelima"), kami menemukan bahwa semua catatan diizinkan dalam skala tertentu membentuk blok 7 catatan berurutan dan semua catatan yang dilarang membentuk 5 blok berturut-turut dari catatan.

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

(Ini sebuah lingkaran, itu membungkus kembali sekitar Fpada akhirnya.)

Posisi nada alami dalam urutan di atas dapat dihitung sebagai (ASCII code) * 2 % 7. Kemudian jika karakter berikutnya ganjil (berlaku untuk #tetapi tidak koma, spasi atau nol byte) kita tambahkan 7 untuk membuatnya tajam. Kami menyimpan bitmap dari not yang telah digunakan.

Angka 243(biner 11111000) sesuai dengan catatan yang dilarang dalam skala A # Dorian. Saya mengalikannya dengan (1<<12)+1=4097memberikan angka ajaib 1016056. Ini hak untuk memeriksa (dengan ANDing) jika melodi berisi catatan yang dilarang untuk masing-masing dari 12 skala pada gilirannya. Jika melodi tidak mengandung catatan yang dilarang, skala dicetak.

Untuk output yang kita butuhkan untuk mencetak nama skala yang dikodekan dalam urutan terbalik ke siklus dari perlima di atas, ingat kita akan mundur karena kita hakhifting.) Urutan ASCII ADGCFBEADGCFdihasilkan oleh 65+i*3%7. Untuk lima yang pertama ini harus dicetak dengan tajam.

Kode tidak dikunci

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

Perilaku input tidak valid: Jika catatan yang diberikan tidak mencukupi untuk menentukan skala, ia akan menampilkan semua skala yang mungkin. Jika kombinasi not yang mustahil diberikan, maka tidak akan menghasilkan apa-apa. Catatan harus dibatasi oleh koma (atau karakter non-spasi putih dengan kode ASCII genap <= 64). Spasi tidak dapat digunakan karena semuanya setelah spasi pertama akan dianggap sebagai argumen yang berbeda. Kode ASCII> 64 akan ditafsirkan sebagai catatan dengan cara yang dijelaskan.


Itu mengejutkan saya bahwa lingkaran perlima memiliki properti ini! Mungkin saya bisa menggunakannya untuk bermain golf lebih banyak.
Ray

1
@ Ray Ini sebenarnya mengapa kita memiliki set catatan yang kita miliki. Oktaf memiliki rasio frekuensi 2: 1. Kelima seperti yang didefinisikan oleh Pythagoras memiliki rasio 3: 2 dan merupakan interval musik paling penting setelah oktaf. Karena 1,5 ^ 12 dekat dengan tetapi tidak sama dengan 2 ^ 7, modern kelima tempered equal diperas ke 1,4983 sehingga tepat 12 perlima pas dalam 7 oktaf. Solusi kuno adalah dengan hanya menggunakan 7 catatan dari 12 yang tersedia dari lingkaran. Itu sebabnya kami memiliki skala berdasarkan 7 not spasi tidak merata. Ini bukan konvensi acak, ada beberapa matematika yang kuat di belakangnya.
Level River St

Ada sejumlah instrumen yang mengatur nada di urutan kelima untuk alasan kenyamanan (biola disetel dengan cara ini, dan gitar bass disetel di urutan keempat, yang merupakan rasio 4: 3). Contoh yang paling mencolok (dan satu-satunya instrumen yang saya tahu yang memiliki catatan diletakkan dalam lingkaran perlima untuk desain akustik yang baik) adalah steelpan: google.es/patents/US7696421 . Dengan tata letak ini, tidak masalah jika nada di sebelah nada yang Anda tekan sedikit berdering.
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

Tidak disatukan

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 karakter

Ini tidak sesingkat itu, tapi saya merasa senang Python untuk menulis banyak bersarang untuk loop dalam satu baris, bahkan ketika tidak bermain golf. Sayangnya, saya harus meletakkan pernyataan input pada baris terpisah sehingga tidak akan dieksekusi lebih dari sekali.

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]

Saya tidak menggunakan Python 3, tapi saya percaya ini adalah contoh yang langka ketika pernyataan cetak tidak membutuhkan lebih banyak karakter. Karena printada fungsi di sana, saya akan dapat mengimbangi kebutuhan untuk tanda kurung dengan menggunakan *operator daftar membongkar untuk menggantikan yang terakhir [0].


2
Anda juga akan bisa menggantikan inputuntuk raw_inputdan menyimpan 4 karakter dengan Python 3.
comperendinous

"Saya merasakan kegembiraan Python untuk menulis banyak bersarang untuk loop dalam satu baris": tetapi apakah Anda menemukan sukacita dalam membacanya?
Caleb Paul

@Wideshanks Tentu saja tidak ... ini semua tentang kode hanya menulis!
feersum

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}

Masukan dari baris perintah args.
misalnyaruby dorianscale.rb B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A

Cobalah di: ideone


3

Haskell - 140

Manfaatkan properti Circle of Fifths yang diperkenalkan oleh @steveverrill. Jika kita membiarkan circle0 = words "C G D A E B F# C# G# D# A# F"dan circle = circle0 ++ circle0, maka kita dapat membangun semua skala dengan mencatat 7 not berturut-turut circle.

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

Dalam setiap skala yang dibangun dengan cara ini,, scale !! 3elemen ke-4 adalah nama skala.

Kode

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

Tidak disatukan

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

Menggunakan metode circle of 5th. Masukan dari baris perintah args yaitu

scala dorianscale.scala B,B,D,E,D,B,A,G#,A,G#,E,D,F#,E,F#,E,F#,G#,A
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.