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:

Palindromic scale intervals

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