Skip to content

Instantly share code, notes, and snippets.

@peterkos
Created April 21, 2019 07:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peterkos/910bbf468e07feb6d74344e1c50cf463 to your computer and use it in GitHub Desktop.
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".
//
// 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