Enigmatic Code

Programming Enigma Puzzles

Enigma 1185: Interval music

From New Scientist #2341, 4th May 2002 [link]

The relative frequencies of the notes of the diatonic music scale are:

C = 24, D = 27, E = 30, F = 32, G = 36, A = 40, B = 45, C’ = 48.

I recently built a series of oscillators so that I could hear how it sounded, but something went wrong with the wiring, so that I ended with an eight-note keyboard where only two were in the correct places.

However, when I played each key in order from the left, I noticed that the only intervals between adjacent notes were fourths (frequency ratio 4/3 or 3/4), fifths (3/2 or 2/3), or sixths (5/3 or 3/5).

Further, the interval (frequency ratio) between the two notes I did get right was one of these.

(a) If I played the left-hand key, what note letter did it sound?

(b) Could the notes have been arranged according to the same rules and have none in the right place?

(c) Could the notes have been arranged according to the same rules and have had more than two in the right place?

[enigma1185]

Advertisements

5 responses to “Enigma 1185: Interval music

  1. Jim Randell 11 January 2016 at 8:24 am

    This Python program runs in 56ms.

    from enigma import diff, printf
    
    # possible frequencies
    fs = (24, 27, 30, 32, 36, 40, 45, 48)
    
    # check allowable intervals
    def check(a, b):
      if b < a: (a, b) = (b, a)
      return any(m * a == n * b for (n, m) in ((3, 4), (2, 3), (3, 5)))
    
    # map frequencies to possible adjacent frequencies
    adj = dict((f, tuple(x for x in fs if f != x and check(f, x))) for f in fs)
    
    # generate layouts of keys by extending <ks> with <n> more keys
    # such that adjacent keys are chosen from <adj>
    def generate(ks, n, adj):
      if n == 0:
        yield ks
      else:
        for f in adj[ks[-1]]:
          if f not in ks:
            yield from generate(ks + [f], n - 1, adj)
    
    # examine possible keyboard layouts
    for f in adj.keys():
      for ks in generate([f], 7, adj):
    
        # what notes are in the right place
        rs = list(f for (f, k) in zip(fs, ks) if f == k)
        printf("[ks={ks}, rs={rs}]")
    
        # (a) has two notes in the right place that are a valid interval
        if len(rs) == 2 and check(*rs):
          printf("(a) left-hand key = {ks[0]}, ks={ks}")
    
        # (b) has none in the right place
        if len(rs) == 0:
          printf("(b) yes, ks={ks}")
    
        # (c) has more than two in the right place
        if len(rs) > 2:
          printf("(c) yes, ks={ks}")
    

    Solution: (a) The left-hand key sounds note A; (b) Yes; (c) No.

    For (a) the keys are (40, 30, 45, 27, 36, 24, 32, 48) with 36 and 48 in the correct positions.
    The ratios between adjacent notes are (4/3, 2/3, 5/3, 3/4, 3/2, 3/4, 2/3).

    For (b) there are several possible arrangements (given below, along with the ratios):

    (30, 45, 27, 36, 48, 32, 24, 40), (2/3, 5/3, 3/4, 3/4, 3/2, 4/3, 3/5)
    (32, 48, 36, 27, 45, 30, 40, 24), (2/3, 4/3, 4/3, 3/5, 3/2, 3/4, 5/3)
    (45, 30, 40, 24, 32, 48, 36, 27), (3/2, 3/4, 5/3, 3/4, 2/3, 4/3, 4/3)
    (48, 32, 24, 36, 27, 45, 30, 40), (3/2, 4/3, 2/3, 4/3, 3/5, 3/2, 3/4)
    (48, 32, 24, 40, 30, 45, 27, 36), (3/2, 4/3, 3/5, 4/3, 2/3, 5/3, 3/4)

    For (c), there are no arrangements with more than two notes in the correct position (all layouts have 0, 1 or 2 notes in the correct position). Which means we don’t have to consider how to extend the condition stated as “the interval (frequency ratio) between the two notes I did get right was one of these” to more than two notes in the correct position.

  2. fisty frodo 11 January 2016 at 8:52 pm

    This is a little slow, but as short as I could make it easily.

    import itertools
    n=[24.0,27.0,30.0,32.0,36.0,40.0,45.0,48.0]
    def c(a,b): return a in[b*4/3,b*3/4,b*3/2,b*2/3,b*5/3,b*3/5]
    for r in itertools.permutations(n):
     o=[r[i]for i in range(8)if r[i]==n[i]]
     if sum(c(r[i],r[i+1])for i in range(7))>6and(len(o)<1or(len(o)>1and sum(c(o[i],o[i+1])for i in range(len(o)-1))>len(o)-2)):
      print "Number in correct position: "+str(len(o))
      print r
    

    #artcode #programmingasanartform #codeart

  3. liam 12 January 2016 at 12:30 pm
    numbers = [24.0, 27.0, 30.0, 32.0, 36.0, 40.0, 45.0, 48.0] 
    for num_in_rightOrder in [0,1,2,3,4,5,6,8]:
    	for a in range(len(numbers)):
    		for b in range(len(numbers)):
    			if b != a:
    				for c in range(len(numbers)):
    					if not(c == a or c == b):
    						for d in range(len(numbers)):
    							if d != a and (not d == b) and (not d == c):
    								for e in range(len(numbers)):
    									if e != a and (not e == b) and (not e == c) and (not e == d):
    										for f in range(len(numbers)):
    											if f != a and (not f == b) and (not f == c) and (not f == d) and (not f == e):
    												for g in range(len(numbers)):
    													if g != a and (not g == b) and (not g == c) and (not g == d) and (not g == e) and (not g == f):
    														for h in range(len(numbers)):
    															if h != a and (not h == b) and (not h == c) and (not h == d) and (not h == e) and (not h == f) and (not h == g):
    																Array = [numbers[a], numbers[b], numbers[c], numbers[d], numbers[e], numbers[f], numbers[g], numbers[h]]
    																ok = True
    																for i in range(len(Array) - 1):
    																	if Array[i] != ((4 * Array[i+1]) / 3):
    																		if Array[i] != ((3 * Array[i+1]) / 4):
    																			if Array[i] != ((3 * Array[i+1]) / 2):
    																				if Array[i] != ((2 * Array[i+1]) / 3):
    																					if Array[i] != ((5 * Array[i+1]) / 3):
    																						if Array[i] != ((3 * Array[i+1]) / 5):
    																							ok = False
    																rightOrdered = []
    																for i in range(len(Array)):
    																	if Array[i] == numbers[i]:
    																		rightOrdered.append(Array[i])
    																if len(rightOrdered) != num_in_rightOrder:
    																	ok = False
    																else:
    																	for i in range(len(rightOrdered) - 1):
    																		if rightOrdered[i] != (4 * rightOrdered[i+1]) / 3:
    																			if rightOrdered[i] != ((3 * rightOrdered[i+1]) / 4):
    																				if rightOrdered[i] != ((3 * rightOrdered[i+1]) / 2):
    																					if rightOrdered[i] != ((2 * rightOrdered[i+1]) / 3):
    																						if rightOrdered[i] != ((5 * rightOrdered[i+1]) / 3):
    																							if rightOrdered[i] != ((3 * rightOrdered[i+1]) / 5):
    																								ok = False
    																if ok:
    																	print num_in_rightOrder
    																	if len(Array)== 0:
    																		print "There are no solutions for this unfortunately"
    																	else:
    																		print Array
    

    The computer gives a result for (A) of 40, (B) a resounding yes and (C) a resounding NO! It only takes a few seconds to run, which is much faster than I could do it in my head! I tried to take some of your advice from the last question on board, I hope that’s evident in the efficiency of my code! Thanks again!

    • Jim Randell 14 January 2016 at 11:32 am

      @liam: I don’t know if you want any hints, but if you are happy to use routines from Python’s standard library you could save yourself some typing replacing lines 2-18 with a single statement:

      from itertools import permutations
      
      ...
      
        # replace lines 2-18 with this:
        for Array in permutations(numbers):
          ...
      

      See the Python documentation on the itertools package for more details. It’s got quite a few useful routines for solving Enigma type problems (permutations(), combinations(), product() and count() are the most useful).

      • geoffrounce 14 January 2016 at 4:15 pm

        I tried a permutation solution and initially got four solutions. Some further manual analysis, listed in the comments in the code below gave the same note sequence as Jim and the same answers for parts (a), (b) and (c), which are initially commented out in the code below:

        # Music Notes - looks like C Major Scale
        C, D, E, F, G, A, B, TC = 24, 27, 30, 32, 36, 40, 45, 48
        
        from itertools import permutations
        from fractions import Fraction as F
        
        # ratio of frequencies between notes
        note_frac = (3/4, 4/3, 2/3, 3/2, 5/3, 3/5)
        
        for p in permutations( (24, 27, 30, 32, 36, 40, 45, 48)):
            cnt = 0   # to count notes in correct positions
            c, d, e, f, g, a , b, tc = p
            
            # form fractions of adjacent note frequencies
            rf1 = F(c/d)
            if rf1 not in note_frac: continue
            rf2 = F(d/e)
            if rf2 not in note_frac: continue
            rf3 = F(e/f)
            if rf3 not in note_frac: continue
            rf4 = F(f/g)
            if rf4 not in note_frac: continue
            rf5 = F(g/a)
            if rf5 not in note_frac: continue
            rf6 = F(a/b)
            if rf6 not in note_frac: continue
            rf7 = F(b/tc)
            if rf7 not in note_frac: continue
            
            # check notes which are in correct positions
            # and check only two notes are in the correct position
            if c == C: cnt += 1
            if d == D: cnt += 1
            if e == E: cnt += 1
            if f == F: cnt == 1
            if g == G: cnt += 1
            if a == A: cnt += 1
            if b == B: cnt += 1
            if tc == TC: cnt += 1
            if cnt == 2:
                print(c,d,e,f,g,a,b,tc)
                
            # for checking other parts of the Enigma
            #if cnt == 0:
            #    print(c,d,e,f,g,a,b,tc)  # 6 solutions - part (b)
            #if cnt > 2:
            #    print(c,d,e,f,g,a,b,tc)  # no solutions - part (c)
                       
        # program outputs following solution for 2 notes in correct positions
        # 24 40 30 45 27 36 48 32
        # 36 27 45 30 40 24 32 48
        # 40 24 32 48 36 27 45 30
        # 40 30 45 27 36 24 32 48   <<< solution (see below)
        #  A  E  B  D  G  C  F TC  < notes for the solution (G and Top C correct)
        
        # ANALYSIS OF THE FOUR SOLUTIONS
        #1st solution
        #24 40 30 45 27 36 48 32  - 1st and 3rd notes in correct positions
        #interval between 1st and 3rd notes = 24/30 = 5/6 - incorrect ratio
        
        #2nd solution
        #36 27 45 30 40 24 32 48  - 2nd and 8th notes in correct position
        #interval between 2nd and 8th notes = 27/48 = 9/16 - incorrect ratio
        
        #3rd solution
        #40 24 32 48 36 27 45 30  - 5th and 7th notes in correct postions
        #interval between 5th and 7th notes = 36/45 = 4/5 - incorrect ratio
        
        #4th solution
        #40 30 45 27 36 24 32 48  - 5th and 8th notes in correct positions
        #interval between 5th and 8th notes = 36/48 = 3/4 - correct ratio !!
        
        

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: