Checking for Musical Palindromes using Queues
Continuing with the discussion on queues, one easy type of question is Valid Palindrome. We can check if a string is a palindrome using a queue. The palindrome is defined as a string that, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, reads the same forward and backward. We can check if a string has alphanumeric characters using isalnum() and we can convert a string to lowercase letters using lower(). Additionally, we can use the deque class of queues (double-ended queues) to literally check both ends of the string.
from collections import deque
def is_palindrome(s):
s_alnum_deque = deque()
for i in range(len(s)):
if s[i].isalnum():
s_alnum_deque.append(s[i].lower()) # lowercase and add to deque if alphanumeric
while len(s_alnum_deque) > 1:
if s_alnum_deque.popleft() == s_alnum_deque.pop():
pass
else:
return False
return True
Let’s hear a palindrome! This is just a regular C major that goes up and down the scale: [60, 62, 64, 65, 67, 65, 64, 62, 60]
What I was curious to explore are the common scales in general, to understand if there are palindromic elements to them. In particular, I looked at the pattern of the whole steps and half steps between each of the notes (i.e. intervals) to see if these intervals are palindromes. These are the scales that I explored in the key of C:
7-Note Heptatonic Scales
Major (Ionian): [60, 62, 64, 65, 67, 69, 71, 72]
Dorian: [60, 62, 63, 65, 67, 69, 70, 72]
Phrygian: [60, 61, 63, 65, 67, 68, 70, 72]
Lydian: [60, 62, 64, 66, 67, 69, 71, 72]
Mixolydian: [60, 62, 64, 65, 67, 69, 70, 72]
Natural Minor (Aeolian): [60, 62, 63, 65, 67, 68, 70, 72]
Locrian: [60, 61, 63, 65, 66, 68, 70, 72]
Harmonic Minor: [60, 62, 63, 65, 67, 68, 71, 72]
Melodic Minor: [60, 62, 63, 65, 67, 69, 71, 72]
Pentatonic Scales
Major Pentatonic: [60, 62, 64, 67, 69, 72]
Minor Pentatonic: [60, 63, 65, 67, 70, 72]
Blues: [60, 63, 65, 66, 67, 70, 72]
Symmetric Scales
Whole Tone: [60, 62, 64, 66, 68, 70, 72]
Diminished (Whole-half): [60, 62, 63, 65, 66, 68, 69, 71, 72]
Diminished (Half-whole): [60, 61, 63, 64, 66, 67, 69, 70, 72]
Chromatic: [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72]
def get_intervals(midi_notes):
"""Compute the intervals (semitone differences) between consecutive notes."""
return [midi_notes[i+1] - midi_notes[i] for i in range(len(midi_notes) - 1)]
def is_interval_palindrome(intervals):
"""Check if an interval list is a palindrome using a deque."""
dq = deque(intervals)
while len(dq) > 1:
if dq.popleft() != dq.pop():
return False
return True
Using these functions, we learn the following intervals are palindromes. Note that 2 is a whole step and 1 is a half step:

Finally, after following up on recursions, I wanted to know if there were Fibonacci sequences that were palindromic that I could play. Turns out there are! There is something called a Fibonacci word, which is a specific sequence of binary digits formed by repeated concatenation, in the same way that the Fibonacci numbers are formed by repeated addition. A cool fact is that suppressing the last two letters of a Fibonacci word creates a palindrome.
def fibonacci_word(n):
if n == 1:
return "0"
if n == 2:
return "01"
return fibonacci_word(n - 1) + fibonacci_word(n - 2) # concatenate, not add!
We can use the same is_palindrome() function to check if the Fibonacci word is a palindrome. If we go up to 11 digits:
And finally, we can play some of these (shortened) Fibonacci words!
Word: 0100101001001010010