Carillon peals
List every different order a carillon can ring its bells when two bells share a pitch, counting each audible peal exactly once.
A carillon rings all of its bells once, in some order. Two bells cast to the same pitch sound the same, so swapping them changes nothing a listener could hear.
The problem
The tower holds a fixed set of bells, each recorded by its pitch number. A peal uses every bell exactly once, in some order, and is identified by the sequence of pitches it sounds rather than by which physical bell rang.
Write out every peal the tower can ring, each one once. The peals may be listed in any order, but no two of them may sound the same.
Input. bells — a list of integers, the pitch of each bell in the tower.
Pitches repeat when two bells were cast alike.
Output. A list of peals, each a list of pitches with the same length as
bells, with no two peals identical.
Example.
bells = [3, 3, 7] -> [3,3,7], [3,7,3], [7,3,3]
Three peals, not six. The two bells at pitch 3 can be swapped in every one of the six orderings, and each swap leaves the sound unchanged.
A second example, where nothing is shared:
bells = [1, 2, 3] -> [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]
With all pitches distinct, the count is the full 3! = 6. Going the other way,
bells = [4, 4, 4] gives the single peal [4,4,4].
Constraints.
1 <= len(bells) <= 81 <= bells[i] <= 50- Every bell must appear in every peal.
Hints
Hint 1
Build a peal position by position. At each position you choose one bell that has not rung yet, then solve the smaller problem for the positions after it.
Hint 2
Sort the pitches so equal ones are adjacent, then decide which of two equal bells is allowed to ring first. Fixing an order between them removes the duplicate.
Hint 3
The rule is about the previous equal bell: refuse to place a bell when its left-hand twin is still unused. That forces equal bells to be spent left to right.
Approach
Brute force
Generate all n! orderings with a used-flag search and drop repeats with a set
of tuples. Eight bells give 40,320 orderings; with six of them sharing a pitch,
that is hundreds of copies of the same peal, nearly all discarded.
The insight
Sort the pitches, and refuse to ring a bell whose identical left-hand neighbour is still unused — equal bells are then always spent left to right, so each audible peal is built exactly once.
Every duplicate peal comes from permuting a group of equal pitches among
themselves. Of the many index orders that produce one peal, exactly one spends
each group left to right, and the rule keeps precisely that one. Sorting is the
precondition: the check pitches[i] == pitches[i-1] and not used[i-1] only sees
a group when equal values are adjacent.
Algorithm
- Sort the pitches and create a
usedflag per index. - If the working peal is full, record a copy and return.
- Loop over the indices. Skip index
iif it is used. - Skip it also if
pitches[i] == pitches[i-1]and indexi-1is unused. - Mark
iused, append its pitch, recurse, then pop and unmark.
Complexity
Time O(n · P) where P is the number of distinct peals — the search never
enters a branch that leads to a duplicate, and each finished peal costs n to
copy. Space O(n) for the flags, the working peal and the recursion depth.
Solution
"""Carillon peals — distinct orderings of a multiset of bells, by backtracking."""
def solve(bells):
pitches = sorted(bells) # equal pitches adjacent, so the skip rule can see them
used = [False] * len(pitches)
peals, current = [], []
def extend():
if len(current) == len(pitches):
peals.append(list(current))
return
for i, pitch in enumerate(pitches):
if used[i]:
continue
# invariant: among equal pitches, only the leftmost unused one may be rung next,
# so equal bells are always consumed left to right and no ordering repeats
if i > 0 and pitch == pitches[i - 1] and not used[i - 1]:
continue
used[i] = True
current.append(pitch)
extend()
current.pop()
used[i] = False # the bell is free again for the next branch
extend()
return pealsThe cases that ran
TESTS = [
(([3, 3, 7],), [[3, 3, 7], [3, 7, 3], [7, 3, 3]]),
(([1, 2, 3],), [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]),
(([4, 4, 4],), [[4, 4, 4]]),
(([2, 2, 5, 5],), [[2, 2, 5, 5], [2, 5, 2, 5], [2, 5, 5, 2],
[5, 2, 2, 5], [5, 2, 5, 2], [5, 5, 2, 2]]),
(([6],), [[6]]),
]Pitfalls
- Skipping when
used[i-1]is true rather than false keeps the mirror image of the intended rule. It still cuts duplicates, but it also drops valid peals when the group is split across positions. - Not sorting first leaves equal pitches apart, so the neighbour test never
matches and
[3,3,7]comes out twice. - Recording the working list instead of a copy leaves every peal in the output pointing at the same list, which ends up empty after the last undo.
- Forgetting to unmark
used[i]after the recursive call loses whole branches, and for[1,2,3]you get one peal instead of six.
Variants
- Donation hampers — the same duplicate skip on selections, where the test is positional instead of flag-based.
- Bead kit bracelets — building a sequence slot by slot when the choices are independent and nothing is consumed.