塔とか大砲とかってなんかいいよね
バベルの塔 とか
塔の上のゲフンゲフン(著作権怖い) とか
結晶塔の帝王 ENTEI(個人的にはSUIKUN派) とか
ネオアームストロングサイクロンジェットアームストロング砲(完成度たけーなオイ) とか
面倒くさいので画面パタメータをListでもらった後のFunctionだけ
ちなみに引数のListを作ってる箇所はこっちの記事に書いてあるYO!!
hanoi.js
// [問題文(原文)]
// ハノイの塔というパズルがあります。
//
// 3つの杭があり、左から順にA,B,Cの杭とします。
// 杭Aに円盤が下から大きい順に n 個重なって置かれています。
// この円盤は必ず1つ上の円盤は下の円盤より小さくなくてはいけないルールがあります。
//
// このルールを守りながらAの杭からCの杭へ円盤を動かす操作をするパズルです。
//
// 例えば3つの円盤が入力として与えられる場合、その時の最短手順は以下の通りになります。
// この時、円盤の数に寄らず最短手順は常に一意に決まります。
//
// 円盤の数 n が与えられます。1つも動かしていない状態を t = 0 とし、円盤を動かした回数を t とします。
// 円盤の数 n と、円盤を動かした回数 t が与えられるので n 個の円盤を最短手順で動かしていった時に円盤を動かした回数 t の状態を出力してください。
function hanoi(lines) {
// 入力は以下のフォーマットで与えられます。
//
// n t
const [n, t] = lines[0].split(' ').map(Number);
// 配列の初期化
// 最初の棒に円盤セット
const rods = [Array.from({ length: n }, (_, i) => i + 1).reverse(), [], []];
// 移動直後の状態
const steps = [];
// 移動回数
let currentStep = 0;
// 再帰用
const moveDisks = (numDisks, fromRod, toRod, auxRod) => {
// 円盤がない、指定回数に達したら何もしない
if (numDisks === 0 || currentStep >= t)
return;
moveDisks(numDisks - 1, fromRod, auxRod, toRod);
if (currentStep < t) {
rods[toRod].push(rods[fromRod].pop());
steps.push(JSON.parse(JSON.stringify(rods)));
currentStep++;
}
if (currentStep >= t)
return;
moveDisks(numDisks - 1, auxRod, toRod, fromRod);
}
// 最初は左から右へ移動
moveDisks(n, 0, 2, 1);
// n 個の円盤を最短手順で動かして行った時、 t 回動かした時点の各杭に積み上がっている円盤を出力してください。
//
// 1行目を杭A、2行目を杭B、3行目を杭Cとし、各行の一番左を杭の一番下として円盤の大きさを 1 から n の数字でスペース区切りで出力して下さい。
// 1つも円盤がない杭の行には「-」と出力して下さい。
steps[t - 1].forEach(rod => console.log(rod.length ? rod.join(" ") : "-"))
}
module.exports = {
hanoi
};
再帰でやってること
- n-1枚の円盤を元のロッド(fromRod)から補助のロッド(auxRod)に移動
- 最も大きな円盤(n番目の円盤)を元のロッド(fromRod)から目的のロッド(toRod)に移動
- 補助のロッド(auxRod)に移動したn-1枚の円盤を目的のロッド(toRod)に移動
ちなみに
元サイトの問題文の下に条件式ってところに書いてあったんだけど、
これの最短回答って「$2^n$ - 1」らしいよ。
2枚:$2^2$ - 1 = 3回
3枚:$2^3$ - 1 = 7回
4枚:$2^4$ - 1 = 15回
16枚:$2^1$$^6$ - 1 = 65535回
※16は問題の最大のNの数
なお16枚の12345、54321回動かした結果は以下
16 12345
16 15 12 11 10 9 8 7
3 2
14 13 6 5 4 1
16 54321
14 11 6 5
13 10 9 8 7 4 3 2
16 15 12 1