Acordes de piano com teclas brancas

9

História de fundo [o que não é verdade]

Um piano é configurado assim:

! [http://www.piano-lessons-made-simple.com/images/2-Octave-Labled.gif

No entanto, no meu piano, todas as teclas pretas estão quebradas!

Ainda quero tocar alguns acordes no meu piano quebrado.

Na música, um acorde é um grupo de notas tocadas juntas. Para permitir a entrada de acordes, primeiro definirei o que é um semitom.

O que é um semitom?

Um semitom é a menor distância na música ocidental. Se você observar a parte superior do piano, verá que geralmente pode passar de uma tecla preta para uma tecla branca ou vice-versa; no entanto, entre Be Ce Ee Fnão há nenhuma chave preta.

O que é um acorde?

Para os propósitos deste desafio, definimos um acorde como um monte de notas com um certo número de semitons entre eles. Por exemplo, vamos dar uma olhada em um 4-3-3acorde a partir de C(para pessoas da música, este é um acorde V 7 em Fá maior). Começamos às C. Contamos até 4 semitons: C#, D, D#, E. A próxima nota é E, e contamos 3 semitons depois que: F, F#, G. A próxima nota é G, e contamos 3 semitons depois que: G#, A, Bb. Então, nós chegamos C-E-G-Bb. Yay! Mas espere ... Bbé uma chave preta e eles estão quebrados ... No entanto, se começarmos G, conseguimos G-B-D-F! Yay!

Entrada

A entrada é fornecida como uma lista de números inteiros em qualquer formato razoável. Isso representa o acorde como descrito acima.

Resultado

A saída deve ser uma lista de notas nas quais eu posso começar a precisar usar apenas teclas brancas. Isso também pode ser apenas uma sequência de até 7 notas, porque todos os nomes-chave terão um caractere. Você também deve poder ter uma saída vazia.

Casos de teste

input -> output // comments
4 3 -> C F G // this is a major triad
3 4 -> D E A // this is a minor triad
4 3 3 -> G // this is the major-minor seventh chord
3 3 3 -> [empty output] // this is the diminished-diminished seventh chord. All of them use black keys
4 4 -> [empty output] // this is an augmented triad
3 3 -> B // this is a diminished triad
1 -> B E // this is just a minor second
11 -> C F // this is just a major seventh

Outras especificações

  • Lacunas padrão proibidas
  • Você pode assumir que a entrada possui pelo menos um número inteiro
  • Você pode assumir que todos os números inteiros são não negativos e menores que 12 (porque o piano repete a cada 12 notas)
  • A saída pode estar em qualquer ordem

Critérios Vencedores

O envio mais curto válido a partir de 15 de abril será aceito.

HyperNeutrino
fonte
Podemos assumir "não negativo e menor que 12" - isso não deveria ser "positivo e menor ou igual a 12"?
Jonathan Allan
@ JonathanAllan Fundamentalmente, não há diferença; meu método permite um uníssono perfeito, mas não uma oitava perfeita; seu vice-versa. Teoricamente, sua restrição pode fazer mais sentido, mas acho que provavelmente não deveria mudá-la, porque já existem respostas e isso não muda o desafio fundamentalmente.
HyperNeutrino

Respostas:

3

Gelatina , 25 bytes

236ḃ2ṙЀ7+\€Ṭ
+\ịþ¢Ạ€TịØA

Experimente online! ou veja uma suíte de testes

Como?

236ḃ2ṙЀ7+\€Ṭ - Link 1, white-note-offsets: no arguments
236ḃ2         - 236 in bijective base 2 [2, 2, 1, 2, 2, 1, 2] - semitones G->A, A->B ...
     ṙЀ7     - rotate left by, mapped over [1,2,3,4,5,6,7] - i.e. as above for each
                    starting white key (1st one A->B,B->C,...; 2nd B->C,C->D,...; etc)
         +\€  - reduce €ach with addition - i.e. absolute number of semitones: [[2,3,5,7,8,10,12],[1,3,5,6,8,10,12],[2,4,5,7,9,11,12],[2,3,5,7,9,10,12],[1,3,5,7,8,10,12],[2,4,6,7,9,11,12],[2,4,5,7,9,10,12]]
            Ṭ - untruth (vectorises) - make lists with 1s at those indexes: [[0,1,1,0,1,0,1,1,0,1,0,1],[1,0,1,0,1,1,0,1,0,1,0,1],[0,1,0,1,1,0,1,0,1,0,1,1],[0,1,1,0,1,0,1,0,1,1,0,1],[1,0,1,0,1,0,1,1,0,1,0,1],[0,1,0,1,0,1,1,0,1,0,1,1],[0,1,0,1,1,0,1,0,1,1,0,1]]

+\ịþ¢Ạ€TịØA - Main link: list of semitone gap integers (even negatives will work)
+\          - reduce by addition - gets the absolute semitone offsets needed
    ¢       - last link (1) as a nilad
   þ        - outer product with:
  ị         -     index into - 7 lists, each with 1s for white and 0s for black keys hit
                      note that indexing is modular and all the lists are length 12
                      so any integer is a valid absolute offset, not just 0-11 inclusive
     Ạ€     - all truthy for €ach - for each get a 1 if all keys are white ones, else 0
       T    - truthy indexes - get the valid starting white keys as numbers from 1 to 7
        ị   - index into:
         ØA -     the uppercase alphabet
Jonathan Allan
fonte
6

MATL , 31 bytes

Obrigado a Jonathan Allan pela correção.

'BAGFEDC'"GYs12X\110BQX@YSYsm?@

Experimente online! Ou verifique todos os casos de teste .

Explicação

O padrão 2 2 1 2 2 2 1especifica os intervalos entre as teclas brancas consecutivas. O programa usa um loop que aplica todos os turnos cíclicos a esse padrão básico, a fim de testar cada tecla como uma nota mais baixa potencial do acorde de entrada. Para cada turno, é obtida a soma cumulativa do padrão. Por exemplo, para Bpotencial nota mais baixa, o padrão foi deslocado para 1 2 2 1 2 2 2e sua soma cumulativa é 1 3 5 6 8 10 12.

Agora, para ver se isso pode suportar um 4 3 3acorde, calculamos a soma cumulativa dos intervalos de acorde, que é 4 7 10; reduza-o via módulo 12 baseado em 1 (um intervalo de 14daria 2); e verifique se esses números são todos membros dos valores permitidos 1 3 5 6 8 10 12. Esse não é o caso neste exemplo. Se fosse esse o caso, exibiríamos a carta B.

A correspondência entre turnos cíclicos e letras de saída é definida pela sequência 'BAGFEDC'. Isso indica que 'B'(primeiro caractere) corresponde a um deslocamento cíclico de 1; 'A'(segundo caractere) corresponde a uma mudança cíclica em 2etc.

'BAGFEDC'  % Push this string
"          % For each character from the string
  G        %   Push input array
  Ys       %   Cumulative sum
  12X\     %   1-based modulo 12, element-wise (1,12,13,14 respectively give 1,12,1,2)
  110BQ    %   Push 110, convert to binary, add 1 element-wise: gives [2 2 1 2 2 2 1]
  X@       %   Push current iteration index, starting at 1
  YS       %   Cyclic shift to the right by that amount
  Ys       %   Cumulative sum
  m        %   Ismember. Gives an array of true of false entries
  ?        %   If all true
    @      %     Push current character
           %   End (implicit)
           % End (implicit)
           % Display (implicit)
Luis Mendo
fonte
5

Mathematica, 110 bytes (codificação ISO 8859-1)

±i_:=#&@@@Select["A#BC#D#EF#G#"~StringTake~{Mod[#,12,1]}&/@#&/@(Accumulate[i~Prepend~#]&/@Range@12),FreeQ@"#"]

Define uma função unária ±usando uma lista de números inteiros como entrada (sem restrições no tamanho ou sinais dos números inteiros, na verdade) e retorna uma lista de cadeias de caracteres de um caractere. Por exemplo, ±{3,4}retorna {"A","D","E"}.

"A#BC#D#EF#G#"~StringTake~{Mod[#,12,1]}&/@#é uma função que transforma uma lista de números inteiros nos nomes de notas correspondentes, exceto que #significa qualquer tecla preta. Isso é aplicado a cada elemento de Accumulate[i~Prepend~#]&/@Range@12, que cria uma lista de valores de notas a partir da lista de entrada de intervalos de notas, começando com cada nota possível de 1 a 12. Filtramos todas as listas de nomes de notas que contêm "#"using Select[...,FreeQ@"#"]e, em seguida, retorne a primeira nota em cada lista restante usando #&@@@.

Greg Martin
fonte
Boa apresentação!
HyperNeutrino 09/04
Pergunta: O Mathematica usa seu próprio sistema de bytes? São 110 caracteres, mas em UTF-8 são 111 bytes por causa do +/-símbolo.
HyperNeutrino 09/04
Você pode retirar completamente a atribuição e apenas "retornar implicitamente" uma função.
Wizzwizz4
@ wizzwizz4: Eu descobri que tinha que nomear a variável Accumulate[i~Prepend~#]&porque, caso contrário, haveria um conflito de curry. Sinta-se livre para encontrar uma solução alternativa!
Greg Martin
@ HyperNeutrino: você está certo de que UTF-8 é a codificação padrão, mas o Mathematica também pode (normalmente) funcionar na codificação ISO 8859-1. Eu notei isso no post.
Greg Martin
3

Python 2, 159 155 bytes

(Postando isso depois de verificar se há um envio válido mais curto que este)

import numpy
s='C.D.EF.G.A.B'
def k(y):return lambda x:s[(x+y)%12]
for i in range(12):
    if s[i]!='.'and'.'not in map(k(i),numpy.cumsum(input())):print s[i]

Praticamente apenas a solução trivial. Entradas como uma lista de números inteiros e saídas com cada caractere em uma linha individual.

-4 bytes removendo uma variável desnecessária

HyperNeutrino
fonte
3

JavaScript (ES6), 72 71 68 bytes

a=>[..."C1D1EF1G1A1B"].filter((c,i,b)=>!+c>a.some(e=>+b[i+=e,i%12]))

Passa por cada tecla, omitindo as teclas pretas, e depois verificando se a soma acumulada de semitons nunca chega a uma tecla preta.

Editar: salvou 3 bytes graças a @Arnauld.

Neil
fonte
4
Legibilidade?! Tem certeza de que está no site certo? :-)
wizzwizz4