I thought four-peg Hanoi was easy
· algorithms
Three-peg Hanoi is 2 T_3(n-1)+1. Four pegs I thought was the same idea. I looked it up. One extra peg, decades to prove.
The problem
AcWing 96, Strange Hanoi. Four pegs: , , , . distinct disks, all on at the start, increasing size from top to bottom. Move the whole stack from to .
Rules: one disk at a time. A disk may go on an empty peg or onto a larger disk.
For each from to , the minimum number of moves. No input. Twelve lines of output, line for .
Sample output:
13591317253341496581Three pegs
Write for the minimum moves with three pegs and disks. The recurrence is
You can read the moves off the formula. Goal: disks from src to dest, the leftover peg aux.
- Move the top from
srctoaux. Same problem, fewer disks. - Move the largest disk from
srctodest. One move. - Move the from
auxtodest.
int min_moves_three_pegs(int disk_count, int src_peg, int dest_peg, int aux_peg, std::vector<Move>* moves) { if (disk_count == 1) { record_move(moves, src_peg, dest_peg); return 1; }
int move_count = 0; move_count += min_moves_three_pegs(disk_count - 1, src_peg, aux_peg, dest_peg, moves); record_move(moves, src_peg, dest_peg); ++move_count; move_count += min_moves_three_pegs(disk_count - 1, aux_peg, dest_peg, src_peg, moves); return move_count;}Four pegs
Write for four pegs. I thought this would be easy. I was wrong. It is a hard problem. I looked at the answer — poke at it yourself first, then look:
Pegs: src, aux1, aux2, dest. The min is over ways to split. For each :
- Move the top disks from
srctoaux1. Four pegs, fewer disks. - Move the remaining from
srctodest. Those are all larger than the sitting onaux1, soaux1is off limits. What is left is three pegs:src,dest,aux2. - Move the from
aux1todest. Four pegs again.
Try every , keep the best.
int min_moves_four_pegs(int disk_count, int src_peg, int dest_peg, int aux_peg1, int aux_peg2, std::vector<Move>* moves) { if (disk_count == 1) { record_move(moves, src_peg, dest_peg); return 1; }
int min_move_count = 0; int best_split = 1; for (int split = 1; split < disk_count; ++split) { int trial_move_count = 0; trial_move_count += min_moves_four_pegs(split, src_peg, aux_peg1, dest_peg, aux_peg2, nullptr); trial_move_count += min_moves_three_pegs(disk_count - split, src_peg, dest_peg, aux_peg2, nullptr); trial_move_count += min_moves_four_pegs(split, aux_peg1, dest_peg, src_peg, aux_peg2, nullptr);
if (split == 1 || trial_move_count < min_move_count) { min_move_count = trial_move_count; best_split = split; } }
if (moves != nullptr) { min_moves_four_pegs(best_split, src_peg, aux_peg1, dest_peg, aux_peg2, moves); min_moves_three_pegs(disk_count - best_split, src_peg, dest_peg, aux_peg2, moves); min_moves_four_pegs(best_split, aux_peg1, dest_peg, src_peg, aux_peg2, moves); }
return min_move_count;}The first loop only counts: moves is nullptr. Then, if you want the actual sequence, replay the winning split.
From writing down this recurrence for the optimal number of moves, to a proof, took decades. The proof is Bousch [1]. Maybe some day I will be able to read it.
Discrete math is often like that. Change one parameter — three pegs to four — and the whole problem changes. Nature and hardness both.
Five pegs I am not ready for. Four took until 2014. For five or more, whether this kind of recurrence is actually optimal is still open [2].
References
[1] T. Bousch, La quatrième tour de Hanoï, Bull. Belg. Math. Soc. Simon Stevin 21 (2014), no. 5, 895–912. https://doi.org/10.36045/bbms/1420071861
[2] C. Grosu, A new lower bound for the Towers of Hanoi problem, Electron. J. Combin. 23 (2016), no. 1, Paper 1.22. https://doi.org/10.37236/5503