Cuantos semitonos

21

Pautas

Tarea

Dadas dos notas, ingresadas como cadenas o listas / matrices, calcule cuántos semitonos están separados (incluidas las notas en sí mismas), generando un número.

Explicación de un semitono:

Un semitono es un paso hacia arriba o hacia abajo del teclado. Un ejemplo es C a C #. Como puede ver a continuación, la nota C está en una nota blanca y C # es la nota negra, justo una arriba. Los semitonos son los saltos de una nota negra a la siguiente nota blanca, hacia arriba o hacia abajo, excepto por:

  • B a C
  • C a B
  • E a F
  • F a E

teclado

Ejemplos

'A, C' -> 4

'G, G#' -> 2

'F#, B' -> 6

'Bb, Bb' -> 13


Reglas

  • La mayor distancia entre dos notas es de 13 semitonos.
  • La segunda nota ingresada siempre estará por encima de la primera nota ingresada.
  • Puede tomar la entrada como una cadena o como una matriz / lista. Si lo toma como una cadena, las notas estarán separadas por comas (por ejemplo String -> 'A, F', Array -> ['A', 'F']).
  • Puede suponer que siempre recibirá dos notas válidas.
  • Los objetos punzantes se denotarán como #y los planos se denotarán comob
  • Su código debe admitir equivalentes enarmónicos (por ejemplo, debe admitir F # y Gb)
  • Su código no necesita admitir notas con nombres, pero se puede nombrar sin una nitidez o un plano (es decir, no necesita admitir E # o Cb). Sin embargo, puntos de bonificación si su código lo admite.
  • Su código no necesita admitir objetos punzantes dobles o planos dobles.
  • Puede suponer que si obtiene las mismas notas o el mismo tono (por ejemplo, 'Gb, Gb' o 'A #, Bb'), el segundo no será exactamente una octava por encima del primero.
  • Este es el código de golf, por lo que gana la respuesta con la menor cantidad de bytes.
Amorris
fuente
Me dan 2 G -> G#porque ambos están incluidos.
HyperNeutrino
@HyperNeutrino Sí, lo siento. Error en mi nombre.
Amorris
1
¿Tenemos que atender notas como Cbo E#? ¿Qué pasa con los objetos punzantes / pisos dobles?
Sok
1
@Sok No, su código no necesita admitir notas como E # o Cb, y no necesita admitir objetos punzantes o planos dobles. He actualizado la pregunta para que quede más clara. Perdón por cualquier confusión.
Amorris
2
Para ser claros, cuando se habla desde una teoría de la música, la distancia sensorial en semitonos no incluye la nota con la que comienza. En matemáticas, se representaría de (X, Y]modo que C a C # sea 1 semitono y C a C sea 12 semitonos.
Dom

Respuestas:

7

Python 2 , 66 bytes

r=1
for s in input():r=cmp(s[1:]+s,s)-ord(s[0])*5/3-r
print-r%12+2

Pruébalo en línea!


Python 2 , 68 bytes

lambda s,t:13-(q(s)-q(t))%12
q=lambda s:ord(s[0])*5/3+cmp(s,s[1:]+s)

Pruébalo en línea!

xnor
fuente
Puntos extra por poder manejar notas como B # y Fb, sin dejar de ser el más corto hasta ahora.
Amorris
7

JavaScript (ES6), 78 bytes

Guardado 1 byte gracias a @Neil

Toma las notas en sintaxis curry (a)(b).

a=>b=>((g=n=>'0x'+'_46280ab_91735'[parseInt(n+3,36)*2%37%14])(b)-g(a)+23)%12+2

Casos de prueba

Función hash

El propósito de la función hash es convertir una nota en un puntero en una tabla de búsqueda que contiene las compensaciones de semitonos (C = 0, C # = 1, ..., B = 11), almacenadas en hexadecimal.

Nos primera append un '3' a la nota y analizar la cadena resultante en base 36, que conduce a un número entero N . Debido a que '#' es un carácter no válido, simplemente se ignora, junto con cualquier carácter que lo siga.

Luego calculamos:

H(N) = ((N * 2) MOD 37) MOD 14

A continuación se muestra un resumen de los resultados.

 note | +'3' | parsed as | base 36->10 |   *2  | %37 | %14 | offset
------+------+-----------+-------------+-------+-----+-----+--------
  C   |  C3  |    c3     |         435 |   870 |  19 |   5 |  0x0
  C#  |  C#3 |    c      |          12 |    24 |  24 |  10 |  0x1
  Db  |  Db3 |    db3    |       17247 | 34494 |  10 |  10 |  0x1
  D   |  D3  |    d3     |         471 |   942 |  17 |   3 |  0x2
  D#  |  D#3 |    d      |          13 |    26 |  26 |  12 |  0x3
  Eb  |  Eb3 |    eb3    |       18543 | 37086 |  12 |  12 |  0x3
  E   |  E3  |    e3     |         507 |  1014 |  15 |   1 |  0x4
  F   |  F3  |    f3     |         543 |  1086 |  13 |  13 |  0x5
  F#  |  F#3 |    f      |          15 |    30 |  30 |   2 |  0x6
  Gb  |  Gb3 |    gb3    |       21135 | 42270 |  16 |   2 |  0x6
  G   |  G3  |    g3     |         579 |  1158 |  11 |  11 |  0x7
  G#  |  G#3 |    g      |          16 |    32 |  32 |   4 |  0x8
  Ab  |  Ab3 |    ab3    |       13359 | 26718 |   4 |   4 |  0x8
  A   |  A3  |    a3     |         363 |   726 |  23 |   9 |  0x9
  A#  |  A#3 |    a      |          10 |    20 |  20 |   6 |  0xa
  Bb  |  Bb3 |    bb3    |       14655 | 29310 |   6 |   6 |  0xa
  B   |  B3  |    b3     |         399 |   798 |  21 |   7 |  0xb

Sobre pisos y objetos punzantes

A continuación se muestra la prueba de que esta función hash asegura que una nota seguida de un '#' dé el mismo resultado que la siguiente nota seguida de una 'b' . En este párrafo, usamos el prefijo @ para cantidades de base 36.

Por ejemplo, Db se convertirá a @ db3 y C # se convertirá a @c (consulte el párrafo anterior). Queremos demostrar que:

H(@db3) = H(@c)

O en el caso general, con Y = X + 1 :

H(@Yb3) = H(@X)

@ b3 es 399 en decimal. Por lo tanto:

H(@Yb3) =
@Yb3 * 2 % 37 % 14 =
(@Y * 36 * 36 + 399) * 2 % 37 % 14 =
((@X + 1) * 36 * 36 + 399) * 2 % 37 % 14 =
(@X * 1296 + 1695) * 2 % 37 % 14

1296 es congruente con 1 módulo 37 , por lo que esto se puede simplificar como:

(@X + 1695) * 2 % 37 % 14 =
((@X * 2 % 37 % 14) + (1695 * 2 % 37 % 14)) % 37 % 14 =
((@X * 2 % 37) + 23) % 37 % 14 =
((@X * 2 % 37) + 37 - 14) % 37 % 14 =
@X * 2 % 37 % 14 =
H(@X)

Un caso especial es la transición de G # a Ab , ya que esperaríamos Hb para cumplir con las fórmulas anteriores. Sin embargo, este también funciona porque:

@ab3 * 2 % 37 % 14 = @hb3 * 2 % 37 % 14 = 4
Arnauld
fuente
@Neil ¡Gracias! Su optimización ahorra más bytes que la mía.
Arnauld
Huh, en realidad encontré lo contrario con mi solución Batch ...
Neil
@Neil Debido a que el signo del módulo en Batch es el signo del divisor, supongo.
Arnauld
No, es el signo del dividendo, como en JS, pero resultó ser un poco golfista de corregir el signo del resultado que se había invertido debido a un golf anterior.
Neil
4

Perl, 39 32 bytes

Incluye +1parap

Dé las notas de inicio y fin como dos líneas en STDIN

(echo "A"; echo "C") | perl -pe '$\=(/#/-/b/-$\+5/3*ord)%12+$.}{'; echo

Solo el código:

$\=(/#/-/b/-$\+5/3*ord)%12+$.}{
Ton Hospel
fuente
@wastl Así me lo han dicho. Sin embargo, me gustaría saber qué metaetiqueta para poder ir allí y estar en desacuerdo :-)
Ton Hospel
Mi comentario es un enlace. Siéntase libre de hacer clic en él.
wastl
Parece que esto funciona de manera muy similar a la mía, pero increíblemente corto para Perl, +1
Level River St
@LevelRiverSt bueno, esto es Ton Hospel.
msh210
4

Japt , 27 bytes

®¬x!b"C#D EF G A"ÃrnJ uC +2

¡Pruébelo en línea! Toma la entrada como una matriz de dos cadenas.

¡También funciona para cualquier cantidad de objetos punzantes o planos en cualquier nota base!

Explicación

®¬x!b"C#D EF G A"ÃrnJ uC +2   Let's call the two semitones X and Y.
®                Ã            Map X and Y by
 ¬                              splitting each into characters,
  x                             then taking the sum of
   !b"C#D EF G A"               the 0-based index in this string of each char.
                                C -> 0, D -> 2, E -> 4, F -> 5, G -> 7, A -> 9.
                                # -> 1, adding 1 for each sharp in the note.
                                b -> -1, subtracting 1 for each flat in the note.
                                B also -> -1, which happens to be equivalent to 11 mod 12.
                                The sum will be -2 for Bb, 2 for D, 6 for F#, etc.
                              Now we have a list of the positions of the X and Y.
                  rnJ         Reduce this list with reversed subtraction, starting at -1.
                              This gets the difference Y - (X - (-1)), or (Y - X) - 1.
                      uC      Find the result modulo 12. This is 0 if the notes are 1
                              semitone apart, 11 if they're a full octave apart.
                         +2   Add 2 to the result.
ETHproducciones
fuente
2

Perl 5 +-p , 66 bytes

s/,/)+0x/;y/B-G/013568/;s/#/+1/g;s/b/-1/g;$_=eval"(-(0x$_-1)%12+2"

Pruébalo en línea!

Toma valores separados por comas. También funciona para Cb, B #, E #, Fb y múltiples # / b.

Explicación:

# input example: 'G,G#'
s/,/)+0x/; # replace separator with )+0x (0x for hex) => 'G)+0xG#'
y/B-G/013568/; # replace keys with numbers (A stays hex 10) => '8)+0x8#'
s/#/+1/g; s/b/-1/g; # replace accidentals with +1/-1 => '8)+0x8+1'
$_ = eval # evaluate => 2
    "(-(0x$_-1)%12+2" # add some math => '(-(0x8)+0x8+1-1)%12+2'

Explicación para eval:

(
    - (0x8) # subtract the first key => -8
    + 0x8 + 1 # add the second key => 1
    - 1 # subtract 1 => 0
) % 12 # mod 12 => 0
+ 2 # add 2 => 2
# I can't use % 12 + 1 because 12 (octave) % 12 + 1 = 1, which is not allowed
wastl
fuente
2

Ruby , 56 bytes

->a{a.map!{|s|s.ord*5/3-s[-1].ord/32}
13-(a[0]-a[1])%12}

Pruébalo en línea!

Las letras se analizan de acuerdo con su código ASCII veces de la 5/3siguiente manera (esto da el número requerido de semitonos más un desplazamiento de 108)

A    B    C    D    E    F    G
108  110  111  113  115  116  118

El último carácter ( #, bo la letra de nuevo) se analiza como su código ASCII dividido por 32 de la siguiente manera

# letter (natural) b 
1  { --- 2 --- }   3

Esto se resta del código de la letra.

Luego, el resultado final se devuelve como 13-(difference in semitones)%12

Level River St
fuente
2

Stax , 25 24 bytes

╝─°U┤ƒXz☺=≡eA╕δ┴╬\¿☺zt┼§

Ejecútelo y depúrelo en línea

La representación ascii correspondiente del mismo programa es esta.

{h9%H_H32/-c4>-c9>-mrE-v12%^^

Efectivamente, calcula el índice del teclado de cada nota usando una fórmula, luego calcula el intervalo resultante.

  1. Comience desde la nota base, A = 2, B = 4, ... G = 14
  2. Calcule el desplazamiento accidental 2 - code / 32dondecode está el código ASCII del último carácter.
  3. Agréguelos juntos.
  4. Si el resultado es> 4, reste 1 para eliminar B #.
  5. Si el resultado es> 7, reste 1 para eliminar E #.
  6. Reste de forma modular los dos índices de notas resultantes y agregue 1.
recursivo
fuente
1
["F#","B"]debería ser 6.
Weijun Zhou
1
Gracias. Cambié la mitad del cálculo sin ajustar la otra. Está arreglado.
recursivo
1

Lote, 136 135 bytes

@set/ac=0,d=2,e=4,f=5,g=7,a=9,r=24
@call:c %2
:c
@set s=%1
@set s=%s:b=-1%
@set/ar=%s:#=+1%-r
@if not "%2"=="" cmd/cset/a13-r%%12

Explicación: Las sustituciones en la csubrutina reemplazan #en el nombre de la nota con +1y bcon -1. Como esto no distingue entre mayúsculas y minúsculas, se Bbvuelve -1-1. Por lo tanto, las variables para C... A(también entre mayúsculas y minúsculas) se eligen para que sean el número apropiado de semitonos de distancia B=-1. Luego se evalúa la cadena resultante, y el truco de @ xnor de restar el resultado del valor da el efecto deseado de restar los valores de las notas entre sí. Editar: Finalmente uso el truco de @ Arnauld de restar el módulo de 13 para lograr la respuesta deseada, ahorrando 1 byte.

Neil
fuente
1

Python 3 , 95 bytes

lambda a,b:(g(b)+~g(a))%12+2
g=lambda q:[0,2,3,5,7,8,10][ord(q[0])-65]+" #".find(q.ljust(2)[1])

Pruébalo en línea!

-14 bytes gracias a user71546

Hiperneutrino
fuente
-8 bytes con ord(q[0])-65reemplazo "ABCDEFG".find(q[0]);)
Shieru Asakoto
Oh, -6 bytes más con (g(b)+~g(a))%12+2reemplazo1+((g(b)-g(a))%12or 12)
Shieru Asakoto
@ user71546 oh genial, gracias!
HyperNeutrino
1

Jalea , 28 bytes

O64_ṠH$2¦ḅ-AḤ’d5ḅ4µ€IḞṃ12FṪ‘

Un enlace monádico que acepta una lista de dos listas de caracteres y devuelve un entero.

Pruébalo en línea! o ver todos los casos posibles .

¿Cómo?

Realiza una aritmética extraña en los ordinales de los caracteres de entrada para asignar las notas a los enteros de cero a doce y luego realiza una descompresión de la base como proxy del módulo por doce, donde cero se reemplaza por 12 y luego agrega uno.

O64_ṠH$2¦ḅ-AḤ’d5ḅ4µ€IḞṃ12FṪ‘ - Main link, list of lists    e.g. [['F','#'],['B']]  ...or [['A','b'],['G','#']]
                  µ€         - for €ach note list          e.g.  ['F','#'] ['B']          ['A','b'] ['G','#']
O                            - { cast to ordinal (vectorises)    [70,35]   [66]           [65,98]   [71,35]
 64                          -   literal 64
   _                         -   subtract (vectorises)           [-6,29]   [-2]           [-1,-34]  [-7,29]
        ¦                    -   sparse application...
       2                     -   ...to indices: [2] (just index 2)
      $                      -   ...do: last two links as a monad:
    Ṡ                        -          sign                     [-6,1]    [-2]           [-1,-1]   [-7,1]
     H                       -          halve                    [-6,-0.5] [-2]           [-1,-0.5] [-7,0.5]
         ḅ-                  -   convert from base -1            5.5       -2             0.5       7.5
           A                 -   absolute value                  5.5       2              0.5       7.5
            Ḥ                -   double                          11.0      4              1.0       15.0
             ’               -   decrement                       10.0      3              0.0       14.0
              d5             -   divmod by 5                     [2.0,2.0] [0,3]          [0.0,0.0] [2.0,4.0]
                ḅ4           -   convert from base 4             10.0      3              0.0       12.0
                             - } -->                             [10.0,3]                 [0.0,12.0]
                    I        - incremental differences           [-7.0]                   [12.0]
                     Ḟ       - floor (vectorises)                [-7]                     [12]
                      ṃ12    - base decompress using [1-12]      [[5]]                    [[1,12]]
                         F   - flatten                           [5]                      [1,12]
                          Ṫ  - tail                              5                        12
                           ‘ - increment                         6                        13

También a 28 bytes ...

Un puerto (no tan directo) de la respuesta Python 2 de xnor ...

O×5:3z60_Ṡ¥2¦60U1¦Fḅ-‘N%12+2

Prueba todos los casos posibles

Jonathan Allan
fuente