Created
April 21, 2019 07:54
-
-
Save peterkos/910bbf468e07feb6d74344e1c50cf463 to your computer and use it in GitHub Desktop.
1st order markov chain of the melody to "Mary Had A Little Lamb".
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// main.swift | |
// MarkovTest | |
// | |
// Created by Peter Kos on 4/20/19. | |
// Copyright © 2019 UW. All rights reserved. | |
// | |
import Foundation | |
// Enum for each of our notes | |
// @TODO: Allow for better comparisons with raw Strings? | |
// (May have to do in acutal algo below) | |
enum Note: Int { | |
case C | |
case D | |
case E | |
case F | |
case G | |
case A | |
case B | |
} | |
// This lets us subscript strings like a NORMAL LANGUAGE | |
extension String { | |
subscript (i: Int) -> Character { | |
return self[index(startIndex, offsetBy: i)] | |
} | |
} | |
// Raw input note string | |
let input = "EDCDEEEDDDEGGEDCDEEEEDDEDC" | |
// 1st order transition probability matrix | |
var matrix = Array(repeating: Array(repeating: 0.0, count: 5), count: 5) | |
var noteProbability = Array(repeating: 0.0, count: 5) | |
// We want to store notes in our own type, see above. | |
var notes = [Note]() | |
// Convert input to enum | |
for letter in input { | |
switch letter { | |
case "C": notes.append(.C) | |
case "D": notes.append(.D) | |
case "E": notes.append(.E) | |
case "F": notes.append(.F) | |
case "G": notes.append(.G) | |
case "A": notes.append(.A) | |
case "B": notes.append(.B) | |
default: print("Unable to parse letter \(letter) in string \(input).") | |
} | |
} | |
// Compute the next events of each note. | |
// For example, if "CD" appeared 3 times, matrix[.C][.D] == 3. | |
// This sets up the probability calcualtion later on, using | |
// the total number of occurences also calcualted in this function. | |
for noteIndex in 0..<notes.count { | |
// Grab the current note and increment its overall probability | |
let note = notes[noteIndex] | |
noteProbability[note.rawValue] += 1 | |
// Grab the NEXT note in the sequence. | |
// Since we're looking one ahead, and always want to keep track | |
// of every node (i.e., not ignroe the last), this if statemnt | |
// is necessecary to keep bounds under control. | |
if (noteIndex + 1 < notes.count) { | |
let nextNote = notes[noteIndex + 1] | |
matrix[note.rawValue][nextNote.rawValue] += 1 | |
// Wrap around case | |
// If we reach the end of the list, | |
// set the next probability to point to the first note | |
if (noteIndex + 1 == notes.count - 1) { | |
matrix[nextNote.rawValue][notes[0].rawValue] += 1 | |
} | |
} | |
} | |
print("-------------") | |
// Finally, compute the probabilities of each note occuring. | |
for row in 0..<matrix.count { | |
for col in 0..<matrix[row].count { | |
// The Magic Magics | |
var probability = matrix[row][col] / noteProbability[row] | |
// Remove pesky garbage | |
if (probability.isNaN) { | |
probability = 0 | |
} | |
// Assign it back | |
matrix[row][col] = probability | |
} | |
} | |
// Print it all out! | |
for row in matrix { | |
print(row) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment