Abordando grafos através das escalas musicais
Como desenvolvedor de software e também amante de música, embora não toque nenhum instrumento, mas com um raso conhecimento em teoria musical. Resolvi unificar essas duas coisas nesse artigo, para apresentar uma breve explicação sobre grafos e como implementar um algoritmo que traga a escala maior de determinada nota musical utilizando a estrutura de grafo.
O que é um grafo?
A princípio, o que seria um grafo? Um grafo é uma estrutura de dados, baseada na teoria dos grafos (caso queira se aprofundar mais, leia sobre o problema das 7 pontes de Königsberg, de Leonhard Euler), organizada em vértices e arestas, onde os vértices são interconectados entre si através das arestas, seguindo um determinado padrão/regra que determine uma relação entre eles. Trazendo para dentro do contexto das escalas musicais, é sabido que a escala maior de C (dó) é: D (ré), E (mi), F (fá), G (sol), A (lá), B (si), C (dó).
Desse modo, a vértice C (dó) estaria conectada às vértices D (ré), E (mi), F (fá), G (sol), A (lá), B (si), C (dó)
Na imagem acima está como ficaria o grafo seguindo a escala de C (dó), note que cada vértice que faz parte da escala maior de C (dó) está conectado na vértice C, as linhas fazendo essa conexão seriam as arestas.
Implementando o algoritmo para a escala maior
A escala maior de uma nota consiste nas 7 notas em sequência seguindo o padrão: tom, tom, semitom, tom, tom, tom, semitom, a partir da nota tônica (a nota de onde está sendo tirada a escala). Esse tom e semitom seria a distância entre duas notas, ex: D (ré) está a um tom de distância de E (mi) que por sua vez está meio tom (semitom) de distância de F (fá).
Dado o contexto e qual o padrão para se obter uma escala maior de uma nota, nossa implementação em java ficaria assim:
package notesScale;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MajorScaleGraph {
private int[] majorScalePattern = { 2, 2, 1, 2, 2, 2, 1 }; // iniciando um array com os passos de tom e semitom
private String[] notes = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; // inciando um array com as notas
private Map<String, List<String>> nodes = new HashMap<String, List<String>>(); // iniciando um hashmap para representar as vertices e com quais estao conectadas
private void addNode(String node) {
nodes.put(node, new ArrayList<String>()); // adicionando uma vertice no grafo
}
private void addEdge(String node, String value) {
List<String> edges = nodes.get(node);
edges.add(value); // adicinando uma aresta em um vertice, para conecta-lo em outro vertice.
nodes.put(node, edges); // atualizando as arestas do vertice
}
public void init() {
// percorre a lista de notas de gera a escala maior de cada uma
for (int i = 0; i < notes.length; i++) {
String note = notes[i];
addNode(note);
int nextNoteFromScaleIndex = i;
// executa o padrao e conecta nota encontrada na vertice da nota tonica da escala
for (int tone : majorScalePattern) {
nextNoteFromScaleIndex = (nextNoteFromScaleIndex + tone) % 12;
String nextNoteFromScale = notes[nextNoteFromScaleIndex];
addEdge(note, nextNoteFromScale);
}
}
}
// retorna a escala de acordo com a nota
public List<String> getMajorScaleFromNote(String note) {
List<String> majorScale = nodes.get(note);
return majorScale;
}
}
Essa seria a classe que representaria nosso grafo, nela tem toda a regra de negócio do nosso grafo e como ele deve funcionar.
A nossa classe principal ficaria assim:
package notesScale;
public class Main {
public static void main(String[] args) {
MajorScaleGraph graph = new MajorScaleGraph(); // instanciando nosso grafo
graph.init(); // chamando o metodo init para iniciar o grafo e ja conectar os vertices entre si
System.out.println(graph.getMajorScaleFromNote("C")); // tras a escala maior de acordo com a nota passada como parametro
// output: [D, E, F, G, A, B, C]
System.out.println(graph.getMajorScaleFromNote("D"));
// output: [E, F#, G, A, B, C#, D]
}
}
No final, depois de organizado nosso grafo ficaria organizado dessa forma:
Conclusão
Neste artigo, foi abordado de forma bem simples um pouco do conceito e funcionamento de um grafo, sobre como ele é organizado, também foi implementando um algoritmo em java que gera as escalas maiores das notas musicais para exemplificar a estrutura de um grafo. Espero que tenham curtido e até mais. Obrigado pela leitura e pela atenção!
Caso queira conferir o repositório e até mesmo fazer sua própria implementação e/ou implementar as escalar menores, segue o link do repositório: https://github.com/magnojunior07/noteScale
Top comments (2)
Muito bom! Subi um pull request com algumas melhorias no código.
Em resumo:
static
, já que representa algo a nível de classe, não instância; efinal
, pra garantir a imutabilidadeenum
“Note”, pra aumentar a segurança de tipo e prover consistência à maneira como as notas são referenciadasLinkedHashSet
se deu pois ele também assegura a ordem de inserção — mantendo o comportamento da lista originalOpa @vieirandre, muito obrigado pelo feedback e pela sugestão de melhoria! Irei sim implementar as sugestões de melhoria