Сколько полутонов

21

Методические рекомендации

задача

Учитывая две ноты, введенные в виде строк или списков / массивов, вычислите, сколько между ними полутонов (включая сами ноты), и выведите их в виде числа.

Объяснение полутона:

Полутон - это один шаг вверх или вниз по клавиатуре. Примером является C на C #. Как вы можете видеть ниже, нота C находится на белой ноте, а C # - черная нота, только одна над ней. Полутона - это скачки от черной ноты к следующей белой ноте, вверх или вниз, за ​​исключением:

  • От B до C
  • С на Б
  • От E до F
  • От F до E

клавиатура

Примеры

'A, C' -> 4

'G, G#' -> 2

'F#, B' -> 6

'Bb, Bb' -> 13


правила

  • Наибольшее расстояние между двумя нотами составляет 13 полутонов.
  • Вторая введенная заметка всегда будет выше первой введенной заметки.
  • Вы можете принимать входные данные либо как строку, либо как массив / список. Если вы возьмете его за строку, заметки будут разделены запятыми (например String -> 'A, F', Array -> ['A', 'F']).
  • Вы можете предположить, что вам всегда будут предоставлены две действительные заметки.
  • Шарпы будут обозначаться как, #а квартиры будут обозначаться какb
  • Ваш код должен поддерживать энгармонические эквиваленты (например, он должен поддерживать как F #, так и Gb)
  • Ваш код не должен поддерживать заметки, которые названы с помощью, но могут быть названы без резких или плоских (т.е. вам не нужно поддерживать E # или Cb). Бонусные баллы, если ваш код поддерживает это все же.
  • Ваш код не должен поддерживать двойные острые или двойные квартиры.
  • Вы можете предположить, что если вы получаете одинаковые ноты или одинаковую высоту звука (например, «Gb, Gb» или «A #, Bb»), вторая не будет точно на одну октаву выше первой.
  • Это код гольф, поэтому ответ с наименьшим количеством байтов выигрывает.
Amorris
источник
Я получаю 2 за, G -> G#потому что они оба включены.
HyperNeutrino
@HyperNeutrino Да, извини. Ошибка от моего имени.
Amorris
1
Должны ли мы обслуживать такие заметки, как Cbили E#? А как насчет двойных острых предметов / квартир?
Сок
1
@ Сок нет, ваш код не должен поддерживать заметки, такие как E # или Cb, и он не должен поддерживать двойные резкие или плоские. Я обновил вопрос, чтобы сделать его более понятным. Извините за любую путаницу.
Amorris
2
Просто чтобы прояснить, при разговоре с точки зрения теории музыки расстояние в полутонах не включает ноту, с которой вы начинаете. В математике это будет представлено (X, Y]так, что C к C # - 1 полутон, и C к C - 12 полутонов.
Дом

Ответы:

7

JavaScript (ES6), 78 байт

Сохранено 1 байт благодаря @Neil

Делает заметки в карри синтаксиса (a)(b).

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

Контрольные примеры

Хэш-функция

Цель хэш-функции - преобразовать заметку в указатель в таблице поиска, содержащей смещения полутонов (C = 0, C # = 1, ..., B = 11), хранящиеся в шестнадцатеричном формате.

Сначала мы присоединять «3» на ноту и анализировать полученную строку в базе-36, что приводит к целому числу N . Поскольку «#» является недопустимым символом, он просто игнорируется вместе с любым последующим символом.

Затем мы вычисляем:

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

Ниже приведена сводка результатов.

 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

О квартирах и острых предметах

Ниже приведено доказательство того, что эта хеш-функция гарантирует, что примечание, за которым следует «#», дает тот же результат, что и следующее примечание, за которым следует «b» . В этом параграфе мы используем префикс @ для базовых 36 величин.

Например, Db будет преобразован в @ db3, а C # будет преобразован в @c (см. Предыдущий абзац). Мы хотим доказать, что:

H(@db3) = H(@c)

Или в общем случае с Y = X + 1 :

H(@Yb3) = H(@X)

@ b3 - 399 в десятичном виде. Следовательно:

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 соответствует 1 по модулю 37 , так что это можно упростить как:

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

Особый случай - переход от G # к Ab , поскольку мы ожидаем, что Hb соответствует приведенным выше формулам. Тем не менее, этот также работает, потому что:

@ab3 * 2 % 37 % 14 = @hb3 * 2 % 37 % 14 = 4
Arnauld
источник
@Neil Спасибо! Ваша оптимизация экономит больше байтов, чем моя.
Арнаулд
Да, я действительно нашел обратное с моим Пакетным решением ...
Нил
@Neil Потому что знак по модулю в Batch - это знак делителя, наверное?
Арнаулд
Нет, это признак дивидендов, как в JS, но оказалось, что он немного сложнее исправить знак результата, который был инвертирован из-за более раннего гольфа.
Нил
4

Perl, 39 32 байта

Включает +1в себя дляp

Дайте начальную и конечную заметки в виде двух строк на STDIN

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

Просто код:

$\=(/#/-/b/-$\+5/3*ord)%12+$.}{
Тон Хоспел
источник
@wastl Так мне сказали. Хотелось бы узнать, какой мета пост, чтобы я мог пойти туда и не согласиться :-)
Тон Хоспел
Мой комментарий это ссылка. Не стесняйтесь нажимать на нее.
Wastl
Похоже, это работает очень похоже на мое - но очень коротко для Perl, +1
Level River St
@LevelRiverSt хорошо, это является Ton Hospel.
msh210
4

Japt , 27 байт

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

Проверьте это онлайн! Принимает ввод как массив из двух строк.

Также работает для любого количества острых предметов или квартир на любой базовой ноте!

объяснение

®¬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.
ETHproductions
источник
2

Perl 5+ -p, 66 байт

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

Попробуйте онлайн!

Принимает значения через запятую. Также работает для Cb, B #, E #, Fb и нескольких # / b.

Объяснение:

# 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'

Объяснение для 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
источник
2

Рубин , 56 байт

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

Попробуйте онлайн!

Буквы анализируются в соответствии с их временами кодирования ASCII 5/3следующим образом (это дает требуемое количество полутонов плюс смещение 108)

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

Последний символ ( #, bили снова буква) анализируется как его ASCII - код , деленное на 32 следующим образом

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

Это вычитается из буквенного кода.

Тогда окончательный результат возвращается как 13-(difference in semitones)%12

Уровень реки St
источник
2

Stax , 25 24 байта

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

Запустите и отладьте его онлайн

Соответствующее представление ascii той же программы таково.

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

По сути, он вычисляет индекс клавиатуры каждой ноты, используя формулу, а затем вычисляет результирующий интервал.

  1. Начните с базовой ноты, A = 2, B = 4, ... G = 14
  2. Вычислить случайное смещение, 2 - code / 32где codeесть код ASCII последнего символа.
  3. Добавьте их вместе.
  4. Если результат> 4, вычтите 1, чтобы удалить B #.
  5. Если результат> 7, вычтите 1, чтобы удалить E #.
  6. Модульно вычесть два результирующих индекса и добавить 1.
рекурсивный
источник
1
["F#","B"]должно быть 6.
Вейцзюнь Чжоу
1
Спасибо. Я изменил одну половину расчета без корректировки другой. Это фиксированный.
рекурсивный
1

Пакет, 136 135 байтов

@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

Объяснение: замены в cподпрограмме заменить #в имени заметки на +1и bс -1. Поскольку это без учета регистра, Bbстановится -1-1. Поэтому переменные для C... A(также не чувствительные к регистру) выбираются так, чтобы они соответствовали количеству полутонов от B=-1. Результирующая строка затем оценивается, и уловка @ xnor вычитания результата из значения дает желаемый эффект вычитания значений нот друг от друга. Изменить: Наконец, я использую трюк @ Арно, вычитая по модулю из 13, чтобы получить желаемый ответ, сохраняя 1 байт.

Нил
источник
1

Python 3 , 95 байт

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

Попробуйте онлайн!

-14 байт благодаря user71546

HyperNeutrino
источник
-8 байт с ord(q[0])-65заменой "ABCDEFG".find(q[0]);)
Шиеру Асакото
О, еще 6 байт с (g(b)+~g(a))%12+2заменой1+((g(b)-g(a))%12or 12)
Шиеру Асакото
@ user71546 о, круто, спасибо!
HyperNeutrino
1

Желе , 28 байт

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

Монадическая ссылка, принимающая список из двух списков символов и возвращающая целое число.

Попробуйте онлайн! или посмотрите все возможные случаи .

Как?

Выполняет некоторую причудливую арифметику для ординалов входных символов, чтобы сопоставить ноты с целыми числами от нуля до двенадцати, а затем выполняет базовую декомпрессию как прокси для модуля по двенадцати, где ноль затем заменяется на 12, а затем добавляет один.

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

Также на 28 байтов ...

(Не очень прямой) порт ответа xnor Python 2 ...

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

Попробуйте все возможные случаи

Джонатан Аллан
источник