Con dos nombres de nota, debe escribir un programa que determine si el intervalo formado por estas dos notas es consonante o disonante.
Introducción
En la música occidental, solo hay 12 tonos "diferentes". Sus nombres, ordenados de menor a mayor, son los siguientes: C, C#, D, D#, E, F, F#, G, G#, A, A#, B
. La secuencia es cíclica, es decir, continúa con otra C
después de la B
, infinitamente.
La distancia entre dos tonos se llama intervalo . El intervalo entre dos notas que son adyacentes en la serie anterior (por ejemplo, C — C#
o E — F
) se llama semitono . El intervalo entre notas más distantes se define como la cantidad de pasos de semitonos necesarios para pasar de la primera a la segunda (mientras se ajusta la secuencia). Algunos ejemplos: D to E
= 2 semitonos, C to G
= 7 semitonos, B to D#
= 4 semitonos (esto envuelve la secuencia). 1
Ahora, estos intervalos se dividen en dos categorías: consonantes (que suena agradablemente si toca las dos notas a la vez) y disonante (no tanto).
Definamos los intervalos de consonantes como: 0, 3, 4, 5, 7, 8 y 9 semitonos.
El resto de ellos es disonante, a saber: 1, 2, 6, 10 y 11 semitonos.
El reto
Escriba un "programa" (en el sentido amplio habitual de la palabra: una función está perfectamente bien) para hacer lo siguiente:
Tome dos nombres de notas (cadenas de la secuencia anterior) como entrada. Puede tomarlos como quiera (desde stdin, como argumentos, separados por lo que quiera, incluso puede tomarlos como una lista de caracteres (por ejemplo
["C","#"]
). Sin embargo, no puede asignar ningún otro nombre a las notas (especialmente usted no puede numerarlos del 0 al 11 y usar los números).Para los fanáticos de la música, las notas se especificarán sin la octava. En este caso, tampoco importa en qué orden vienen las notas y cuál es más bajo y cuál es más alto. Finalmente, no necesita manejar ningún nombre que no esté en la lista anterior. No hay otras enarmónicas como
E#
, sin pisos, dobles alteraciones, etc.Elija dos valores diferentes. Su programa debe generar uno de ellos siempre que el intervalo formado por las dos notas en la entrada sea consonante, y el otro si no lo son. (Podría ser
True
yFalse
, pero incluso π y e si quieres :))Este es un código de golf. El programa más corto en bytes en cada idioma gana. ¡Que te diviertas!
Ejemplos y casos de prueba
Note 1 Note 2 Output Interval [semitones]
C D Dissonant 2
A# A# Consonant 0
G D Consonant 7 (wraparound)
D# A Dissonant 6
F E Dissonant 11
A C Consonant 3
No agrego más de ellos ya que no hay casos particularmente traicioneros en esto.
Este es un primer desafío mío, por lo que cualquier crítica constructiva es calurosamente bienvenida :—). Si encuentra la explicación de la teoría descuidada, no dude en hacer preguntas. Finalmente, por favor no me digas que esto es un engaño de esto o esto . Me aseguré de que no sea así. (Esto último es bastante similar pero más complejo. Pensé que presentar un desafío un poco más simple facilitaría la participación de las personas).
1 : Traté de simplificar esta explicación lo más que pude. Hay mucha más teoría sobre los intervalos. Por favor, no me critiques por dejarlo fuera.
APL (Dyalog),
6239 bytesUses
⎕IO←0
; 0 is consonant, 1 is dissonant. Takes list of base note chars as left argument and list of sharps as right argument.Try it online!
{
…}
anonymous function where⍺
is the left argument and⍵
is the right argument⎕A[
…]∊'BCGKL'
is the Alphabet, indexed by the following, a member of the string?⍕#
format the root namespace (yields the sharp character)⍵=
are the right argument chars (the sharps) equal to that?(
…)+
add the following:'C D EF G A '⍳⍺
indices of the left argument chars in the string-/
difference between those|
absolute valuefuente
MATL,
302726 bytesInputs the two notes in different lines. Outputs
0
for consonant,1
for dissonant.Try it online! Or verify all test cases.
Explanation
The 11-character string
encodes both the notes and the dissonant intervals, as follows.
The program first finds the 1-based indices of the input characters in the above string. A non-sharp input like
D
will give1
,E
will give3
, ...,C
will give11
. These numbers can also be considered 1×1 numeric arrays. A sharp input likeC#
will give the 1×2 array[11 0]
, meaning thatC
was found at position11
and#
was not found.Observe that letters
JPIL
will never be present in the input. For now they are only used as placeholders, so that for example noteE
is two semitones aboveD
. But they will also be useful to define dissonant intervals.The numbers in the first entry of the 1×1 or 1×2 array correspond to note pitch in semitones, not counting sharp symbols (yet). Observe that the scale defined by these numbers doesn't start at
C
; but that doesn't matter because we only want intervals, that is, differences between notes. Subtracting the obtained numbers would give either the interval or 12 minus the interval. But first we need to consider the sharp symbol.To consider sharp notes, a golfy way (in MATL) is to add
1
to each entry of the 1×1 or 1×2 array obtained previously and then sum the array (2 bytes). Thus non-sharp notes are increased by1
and sharp notes by2
. This makes sharp notes 1 semitone higher than non-sharp notes, as required. We are also adding an extra semitone to all notes, but that doesn't change the intervals between them. So now noteD
will give pitch number2
,D#
will give3
, ...,C
will give12
,C#
will give13
.Dissonant intervals are
1
,2
,6
,10
, or11
. These have a modulo-12 symmetry: an interval between two notes is dissonant if and only if the interval with the notes in reverse order, modulo 12, is dissonant.If we compute the consecutive differences of the string
'DJEFPGIALBC'
we get the numeric vectorwhich contains precisely the dissonant intervals, in addition to some negative values, which will be neither useful nor harmful. Observe that it is the choice of additional letters
JPIL
in the string'DJEFPGIALBC'
that defines (via consecutive differences) the dissonant intervals.To see if the two input notes are dissonant we take the absolute difference of their pitch numbers. For example,
C
andD#
will give numbers12
and3
respectively, and the absolute difference is9
. The actual difference would be-9
, and the actual interval would be3
(obtained as-9
modulo 12). But thanks to the symmetry referred to above, we can consider9
instead of3
. Since9
is not present in the vector of consecutive differences, the notes are consonant.fuente
JavaScript (ES6),
6864 bytesTakes the notes as two strings in currying syntax
(a)(b)
. Returns0
for dissonant or1
for consonant.Test cases
Show code snippet
Formatted and commented
fuente
Jelly, 26 bytes
A monadic link taking a list of the two notes (as lists of characters) and returning
0
for consonant and1
for dissonant.Try it online! or see all inputs in the test-suite.
How?
fuente
Jelly, 31 bytes
Try it online!
wheeeeee 32 bytes too long
Explanation
fuente
"G#", "A"
(dissonant) yields a difference of11
which is not in[1,2,6]
.Mathematica, 55 bytes
Map the internal built-in
Sound`PitchToNumber
on the input (list of two strings), take the absolute difference, then pattern match for dissonant interval numbers.Just for fun (non-competing)
Here are some shorter functions that violate the restriction “you may not assign any other names to the notes.” The rudimentary
Music`
package has predefined note constants (likeA4 = 440.
) and the functionHertzToCents
(which can be golfed). Instead of strings, we will use the note constants as arguments, but given in a different format for each function.The package import
<<Music`;
takes 9 bytes.This function converts a string (like
"F#"
) into a note constant (likeFsharp3
):To accept intervals larger than an octave, replace
Abs[…]
withMod[…,12]
.Why are some intervals considered dissonant? An interval is a ratio of two frequencies. If the ratio has a “simple” numerator and denominator, it tends to be more consonant. In 5-limit tuning, ratios can be factored into integer powers of only prime numbers less than or equal to 5. No interval in equal temperament, besides the octave, is a just interval; they are merely close approximations using powers of the 12th root of 2.
Instead of hard-coding which interval numbers are dissonant, we can find a rational approximation of the interval and then determine if its numerator and denominator are “simple” (meaning that the denominator is less than 5 and the ratio does not divide 7).
This table shows each of the steps in that process.
The rational approximation lies within
1/17
of the interval because that is the largest threshold that distinguishes between all 12 equal tempered intervals. We match rational numbers with the patternRational[a_,b_]
(or justa_~_~b_
) first, and then match integers with only_
.This culminates in the following rather short function that determines if an arbitrary frequency ratio (greater than 1) is consonant or dissonant.
fuente
Mathematica, 118 bytes
Input form
Outputs
thanks @JonathanFrech -16 bytes
fuente
Consonant
andDissonant
. You may output any two values instead of them (0/1,... whatever). That could save some bytes.If[...,0,1]
and defineTrue->Consonant; False->Dissonant
?StringCases["CC#DD#EFF#GG#AA#B",_~~"#"...]
– 42 Bytes{1,2,6,10,11}
with1|2|6|10|11
Charcoal, 30 bytes
Try it online! Link is to verbose version of code. Outputs 1 for consonant, 0 for dissonant. Explanation:
fuente
⌕ζ
is used for "find index"?ζ
is the variable assigned to earlier.J, 68 bytes
explanation
A straightforward, not super golfed implementation in J:
Input is given as boxed itemized notes (produced using cut), in order.
Find their indexes in the range of notes:
(<;._1',C,C#,D,D#,E,F,F#,G,G#,A,A#,B')i.]
Subtract the first from the second:
-~/
Take the remainder when divided by 12:
12|
Check if it's one of the dissonant notes:
e.&1 2 6 10 11
Try it online!
fuente
///,
9088 bytesTry it online! (all test cases at once)
,B#
in each test case.,
for consonant,,#
for dissonant.##
) orE#
in some particular cases. Otherwise the output is,
for consonant,#,
for dissonant (thanks to modulo 12 symmetry)fuente
C (gcc), 91 bytes
call:
f("A#", "D")
Return value:
Bonus: The function is case insensitive.
Try it online!
fuente
return (
s?g(char*s){s=(s[1]&1|2**s&15)*4/5;}f(char*x,char*y){x=1952220<<g(x)>>g(y)&2048;}
and nice solution!Python 2,
125 117 83 7877 bytesWhere the
""
at the end actually contains the characters"\x02\x04\x0c\x14\x16"
Try it Online!
(+3 because I forgot 11 or 22 in the list to begin with)
-8 bytes from Jonathan Frech and switching to Python 2.
-34 bytes with suggestions from Jonathan Frech and using
str
's index instead oflist
's.-4 bytes from inlining
i
and Neil's reversing the string suggestion (only -2 really, as i forgot()
around a generator)-5 bytes from un-inlining
i
& changing the input format-1 bytes from Jonathan Frech with
map()
and unprintables.Takes input in one line of stdin in the format:
True
is dissonant,False
is consonant.Old Explanation:
Python
str.index
returns the lowest (positive) starting index of a matching substring, so"ABACABA".index("A") == 0
and"ABACABA".index("BA") == 1
. Because of this, we can put the note names evenly spaced in a string, and as long as (for example)A
comes beforeA#
, the sharedA
won't be a problem.i
is now a function that returns the index in'C C#D D#E F F#G G#A A#B'
its argument (a note name) is, which is 2 * (the number of semitones that note is up fromC
)Python 2's
input()
is (mostly) equivalent toeval(input())
in Python3, so with a valid input of the format'C#','F'
(for example),a='C#'
andb='F'
If the disttance between the first note and the second note in the string is not 2, 4, 12, or 20 (since the note names are represented in 2 characters), then the interval is dissonant, print True, else it is consonant, print False.
fuente
eval(input())
(13 bytes) instead ofinput().split()
(15 bytes).
) instead of an emtpy string.C (gcc), 115
117120bytesTry it online!
Return 1/0 for consonat and dissonat. It is always interesting to do string manipulation with pure C. Take input as
f("A#", "C")
fuente
PowerShell, 107 bytes
Try it online!
Outputs
True
for dissonant andFalse
for consonant.Takes input
$a
and$b
, the two notes, as strings. Performs a-split
operation on the scale, which splits on whitespace, to create an array of the notes, stores that into$x
. Finds the.indexof
$b
in that array, subtracts the index of$a
, and then takes theabs
olute value thereof. Checks whether that number is-in
the dissonant ranges.fuente
Python 2, 68 bytes
Try it online!
Outputs:
1
is dissonant,0
is consonant.fuente
SQL, 582 bytes
SQL Fiddle
I still have some golfing to do on it, but I wanted to get it down here before I end up breaking it completely.
If the input is in a letter format, then putting those letters in a table with values is okay, right?
fuente
Perl 5, 106 bytes
Try it online!
Returns false for dissonant, true for consonant.
fuente