Teaser 3224: Put your finger on it
From The Sunday Times, 7th July 2024 [link] [link]
On a particular stringed instrument, a fingering pattern of 2, 2, 3, 1 means that the pitches of strings 1 to 4 are raised by that many steps respectively from the “open strings” (i.e. their pitches when not fingered); this gives a “chord” of pitches C, E, G, A♯ in some order. The fingering pattern 4, 1, 0, x (for some whole number x from 0 to 11 inclusive) would give pitches F, A, C, D in some order, if these were all shifted by some fixed amount.
[Pitches range through C, C♯, D, D♯, E, F, F♯, G, G♯, A, A♯, B, which then repeat].
What are the pitches of “open strings” 1 to 4, and what is the value of x?
[teaser3224]




Jim Randell 4:46 pm on 5 July 2024 Permalink |
A simple exhaustive search of the problem space finds the answer fairly quickly. So it will do for now – I might refine it a bit later.
This Python program runs in 99ms. (Internal runtime is 8.3ms).
from enigma import (irange, subsets, join, printf) # format a set of notes fmt = lambda ns: join(ns, sep=" ", enc="()") # the order of notes notes = "C C# D D# E F F# G G# A A# B".split() n = len(notes) # finger strings <ss> at frets <fs> def finger(ss, fs): return list(notes[(notes.index(x) + y) % n] for (x, y) in zip(ss, fs)) # target chords tgt1 = sorted("C E G A#".split()) tgt2 = sorted("F A C D".split()) # choose an ordering for the first target chord for ns1 in subsets(tgt1, size=4, select='P'): # and fret it down to open strings ss = finger(ns1, [-2, -2, -3, -1]) # choose a value for x and a transposition for (x, t) in subsets(irange(0, 11), size=2, select='M'): ns2 = finger(ss, [4 + t, 1 + t, 0 + t, x + t]) # check for the target chord if sorted(ns2) == tgt2: printf("{ss} @[2, 2, 3, 1] = {ns1}; @[4, 1, 0, {x}] + {t} = {ns2}", ss=fmt(ss), ns1=fmt(ns1), ns2=fmt(ns2))Solution: The open strings are [D, G♯, E, B]. And the value of x is 2.
Fretting @[2, 2, 3, 1] gives [E, A♯, G, C].
And, shifting up by 8 frets (e.g. using a capo) gives [A♯, E, C, G].
Then fretting @[4, 1, 0, 2] (above the capo) gives [D, F, C, A].
LikeLike
Ruud 7:31 am on 6 July 2024 Permalink |
Here’s my brute force solution using sets:
from itertools import product pitches = "C C# D D# E F F# G G# A A# B".split() def index_set(s): return {pitches.index(n) for n in s} def iadd(i, n): return (i + n) % 12 for istrings in product(range(12), repeat=4): if {iadd(istrings[0], 2), iadd(istrings[1], 2), iadd(istrings[2], 3), iadd(istrings[3], 1)} == index_set({"C", "E", "G", "A#"}): for shift in range(12): for x in range(12): if {iadd(istrings[0], 4 + shift), iadd(istrings[1], 1 + shift), iadd(istrings[2], 0 + shift), iadd(istrings[3], x + shift)} == index_set( {"F", "A", "C", "D"} ): print(f"open strings are {' '.join(pitches[i] for i in istrings)}") print(f"x = {x}")LikeLike