1 2 3 |
bit_count = lambda x: bin(x).count('1', 2) |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# iterate through all twelve tone scales looking for those with six notes # including one in the leftmost scale position # bits 123456789012 0b1000000000000 == 4096 for i in range(0b100000000000, 0b1000000000000): #12 bits with first bit = 1 # Extract bits of i into a list b = [ 1 if i & 2048 >> j else 0 for j in range(12)] # Are there 6 notes? if sum(b) == 6: # Look for notes separated by 1 and 5 steps # neg to pos indexing equivalents i - 11 -> i + 1 and i - 7 -> i + 5 # -12 -> 0; -11 -> 1; -10 -> 2 .... -1 -> 11 (highest index of b) c1 = sum(b[i] and b[i - 11] for i in range(12)) c5 = sum(b[i] and b[i - 7] for i in range(12)) # If note separation criteria satisfied: print scale if c1 == 2 and c5 == 1: print( * b, "is Skaredahora's favourite scale.") print("found after", i - 2047, "iterations") break |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# count the bits set in an integer with 32 or less bits def bit_cnt32(v): v = v - ((v >> 1) & 0x55555555) v = (v & 0x33333333) + ((v >> 2) & 0x33333333) return (((v + (v >> 4)) & 0x0F0F0F0F) * 0x1010101) >> 24 v = 0b100000011111 while True: if (bit_cnt32(v & (v >> 1)) + (v & 1) in {2, 258, 514, 770} and bit_cnt32(v & ((v >> 5) | (v << 7))) in {1, 257, 513, 769}): print(bin(v)[2:]) break t = (v | (v - 1)) + 1 v = t | ((((t & -t) // (v & -v)) >> 1) - 1) |

I checked quite a few bit count functions and yours seems to be the fastest.

]]>In line 21 (b2 right shift 5) is always the same as b.

If I understand correctly in line 20 you also could have used left shift iso right shift?

I find your first solution above has very clear code and is nicely presented, making it easy to follow.

I also prefer your output, using spaces between the notes of the scale to higlight individual notes.

Without the spaces, the output, in my view, looks more like a binary number – although we know the ‘1’s are the white notes and the ‘0’s are the black notes.

The twelve notes in the starting scale could be, for the piano:

C, C#, D, D#, E, F, F#, G, G#, A, A#, B

(1 0 1 0 1 1 0 1 0 1 0 1)

You are right about the speed as it is more than twice as fast but it does not require more lines of code:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# generate list of all possible twelve tone scales with six notes # with first note for i in range(2048, 4096): b = bin(i)[2:] if b.count('1') == 6: # look for notes separated by 1 and 5 steps c1 = sum(n == b[i - 11] == '1' for i, n in enumerate(b)) c5 = sum(n == b[i - 7] == '1' for i, n in enumerate(b)) # if note separation criteria satisfied: print scale if c1 == 2 and c5 == 1: print("Skaredahora's favourite scale is [", *b, "]") break |

Neat indexing, John, apologies for the earlier mistranslation.

]]>It would probably be faster to generate and test scales on the fly rather than generating all possible scales and then testing them but it would require more lines of code.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# generate list of all possible twelve tone scales with six notes # with first note scales = [bin(i)[2:] for i in range(2048, 4096) if bin(i).count('1') == 6] for ts in scales: # look for notes separated by 1 and 5 steps c1 = sum(n == ts[i - 11] == '1' for i, n in enumerate(ts)) c5 = sum(n == ts[i - 7] == '1' for i, n in enumerate(ts)) # if note separation criteria satisfied: print scale if c1 == 2 and c5 == 1: print("Scale is", *ts) break |