マガジン

重さの違う石を並べ替えるゲーム:比較だけで順番を決める

遊びをつくる

執筆にClaudeを使用しています。

迷路の一角から出口までを段階的に照らす三つの灯りと、小さな旅人の駒
記事のテーマをもとに制作したAI生成イラスト。

見た目がまったく同じ5つの石が並んでいて、重さの違いは天秤で2つを比べたときの「どちらが重いか」でしかわからない場面を考えてみます。数字は見えません。この条件で軽い順に並べるには、天秤を何回使えばよいのでしょうか。1回ごとに回数が増える遊びにすると、答えは理屈よりも手の動きで見えてきます。 この記事は、その問いを自分で確かめるための実験の設計書です。単独で動く小さなHTMLを使い、「戦略を決めずに並べる」場合と「決めた手順で並べる」場合の比較回数を記録して比べます。結果は書きません。数え方、記録の取り方、結果の読み方を示しますので、実際の数字は手元で出してください。 そのまま遊びとして成立するかどうかも、同時に確かめられます。比較回数に上限をつけると、「ひらめき」と「手順」のどちらが効くかが見えるからです。

この記事の目次
  1. 先に知っておく数学:最低でも何回か
  2. 準備するもの:石の天秤
  3. 手順:2つの条件で各10回
  4. 記録の取り方と結果の読み方
  5. 起こりやすい誤り
  6. 次の行動

先に知っておく数学:最低でも何回か

比較1回の答えは「重い」「軽い」の2通りです。k回の比較で区別できる並びの数は、多くても2のk乗通りです。5個の石の並びは5×4×3×2×1で120通りあります。2の6乗は64で足りず、2の7乗は128なので、どんな並びでも確実に当てるには、最悪の場合で7回以上の比較が必要です。

同じ考え方で、個数ごとの下限は次のとおりです。

  • 3、並びの数:6、最悪の場合に必要な比較回数の下限:3
  • 4、並びの数:24、最悪の場合に必要な比較回数の下限:5
  • 5、並びの数:120、最悪の場合に必要な比較回数の下限:7
  • 6、並びの数:720、最悪の場合に必要な比較回数の下限:10

この下限は「最悪の並びでも確実に仕上げる」ための数字です。運よく少ない回数で正解になることは起こりえますが、それは手順の良さではありません。実験では、この区別を何度も意識することになります。

比較する手順の目安も挙げておきます。

  • 総当たり:すべての組を比べると、5個で10回です。
  • 挿入:並べ終えた列に1つずつ石を足していきます。端から順に比べると、最悪で総当たりと同じ10回です。
  • 二分挿入:新しい石をすでに並んだ列の真ん中の石と比べ、残りの候補を半分ずつに絞ります。5個では、最悪で1+2+2+3=8回です。

下限の7回と二分挿入の8回の差は1回です。この差が手で並べる遊びでどう感じられるかも、観察の対象になります。

準備するもの:石の天秤

必要なのはブラウザ、筆記具、記録用の紙(または表計算)です。次のコードを stones.html として保存し、ブラウザで開きます。外部ライブラリは使わず、通信もしません。重さは画面に表示されず、「くらべる」を押したときの「重い/軽い」だけが手がかりです。

html
<!doctype html>
<meta charset="utf-8">
<title>石の天秤</title>
<p>石を2つ選んで「くらべる」。軽い順に左から並べ、「判定」は最後に1回だけ押します。</p>
<div id="row"></div>
<p>
<button id="cmp">くらべる</button>
<button id="swap">入れ替える</button>
<button id="chk">判定</button>
<button id="new">新しい石</button>
</p>
<p id="log"></p>
<script>
const N = 5;
let w, order, sel, count;
const name = id => "石" + "ABCDEFGH"[id];
const say = t => document.getElementById("log").textContent = t;
function start() {
  const pool = [...Array(40).keys()].map(i => i + 1);
  pool.sort(() => Math.random() - 0.5);
  w = pool.slice(0, N);
  order = [...Array(N).keys()];
  sel = []; count = 0;
  draw(); say("比較 0 回");
}
function draw() {
  const row = document.getElementById("row");
  row.innerHTML = "";
  order.forEach((id, pos) => {
    const b = document.createElement("button");
    b.textContent = name(id);
    b.style.cssText = "margin:4px;padding:16px";
    if (sel.includes(pos)) b.style.outline = "3px solid #07c";
    b.onclick = () => {
      sel = sel.includes(pos) ? sel.filter(p => p !== pos) : [...sel, pos].slice(-2);
      draw();
    };
    row.appendChild(b);
  });
}
document.getElementById("cmp").onclick = () => {
  if (sel.length < 2) return say("石を2つ選んでください");
  const [a, b] = sel.map(p => order[p]);
  count++;
  say(`比較 ${count} 回:${name(a)}は${name(b)}より${w[a] > w[b] ? "重い" : "軽い"}`);
};
document.getElementById("swap").onclick = () => {
  if (sel.length < 2) return;
  const [p, q] = sel;
  [order[p], order[q]] = [order[q], order[p]];
  sel = []; draw();
};
document.getElementById("chk").onclick = () => {
  const ok = order.every((id, i) => i === 0 || w[order[i - 1]] < w[id]);
  say(`${ok ? "正解" : "不正解"}(比較 ${count} 回)`);
};
document.getElementById("new").onclick = start;
start();
</script>

画面に残る比較結果は直近の1件だけです。これは仕様として意図しています。過去の比較結果は紙に書き取る必要があり、書き取った結果から順序を推理する作業が、そのまま実験の中身になります。

手順:2つの条件で各10回

条件は2つ用意します。石の数は5個のまま変えないでください。数を変えると回数が比べられなくなります。

条件A:戦略を決めずに並べる

  1. 「新しい石」を押して、画面を開き直した状態にします。
  2. 気の向くまま2つを選んで「くらべる」を押し、結果を紙に書きます(例:「A>C」のように、重い側を左に書きます)。
  3. 紙の記録と、頭の中で成り立つ推理だけを頼りに、石を「入れ替える」で並べます。
  4. 並べ終えて自信が持てたら「判定」を1回だけ押し、表示された比較回数と正誤を記録します。

条件B:二分挿入で並べる

  1. 最初の2つを比べて、軽い方を左に置きます。
  2. 3つ目の石は、すでに並んだ列の真ん中の石と比べます。軽ければ左半分、重ければ右半分へ絞り、さらに比べて位置を決めます。
  3. 4つ目、5つ目も同じく、並んだ列の真ん中から比べて位置を決めます。
  4. 全部並べたら「判定」を1回だけ押して、比較回数と正誤を記録します。

どちらの条件も、判定は1試行につき1回までです。不正解でも押し直さず、その試行は「不正解」として記録します。判定を試行錯誤の道具にすると、比較回数が意味を失うためです。

記録の取り方と結果の読み方

記録は1試行を1行にします。条件ごとに10回以上行ってください。

  • 1、条件:A、比較回数:、同じ組の再比較:、判定:
  • 2、条件:A、比較回数:、同じ組の再比較:、判定:

「同じ組の再比較」は、すでに比べた組をもう一度比べた回数です。紙のログと見比べて数えます。これが多い試行は、記録を見返していない可能性があります。

読み方の判断基準は次のとおりです。

  • 条件Bの最大値が8を超える:二分挿入になっていない可能性があります。どの試行で、どの石と比べたかを紙のログからたどり直してください。
  • 条件Aの回数が10を超える:同じ組を重ねて比べているか、「AがBより重く、BがCより重ければ、AはCより重い」という推移を使えていません。再比較の欄を見てください。
  • 7回未満で正解になった試行がある:下限は最悪の場合の話ですので、運で当たっただけかもしれません。確かめるには、その試行のログだけから順序が一意に決まるかを紙の上で検算します。決まらないなら、推測で並べて当たった試行です。
  • 判定が不正解の試行がある:比較が足りないまま並べたと考えられます。ログから、確定していない組がどこに残っていたかを探してください。

平均だけでなく、最大値と「正解にならなかった回数」も見ます。遊びとして成立するかどうかは、平均より「運が悪い試行」の体験で決まるからです。

起こりやすい誤り

  • 条件ごとに石の数を変える:下限も手順の回数も変わるため、比べられなくなります。
  • ログを書かずに頭だけで進める:画面には直近の1件しか出ないので、推移の取りこぼしが起きます。書くこと自体が手順の一部です。
  • 判定を探り用に使う:正誤がわかると、そこから逆算できてしまいます。1試行1回を守ってください。
  • 10回の結果だけで「この手順は常に8回で済む」と断定する:10回では最悪の並びを引かない可能性があります。「この範囲ではこうだった」にとどめます。
  • ランダム生成の癖を気にしすぎる:このコードの並べ方は厳密に均等とはいえません。差を調べたいなら、試行数を増やしてください。

次の行動

まず石の数5・条件A・10回から始め、紙のログの量を体感してください。そのあと条件Bに切り替えて、同じ回数を行います。

ゲームにするなら、比較回数に上限を設ける案が自然です。たとえば7〜8回を超えたら失敗とする、あるいは回数に応じて評価を変えるなどです。上限を決めると、遊ぶ人が戦略を意識するようになります。どの上限なら「悩めるが詰まらない」かは、手元の記録を見ながら決めてください。

形になったら、index.html として単独で動く形にまとめ、アソバースに投稿できます。HTMLの直接アップロードは20MiB以内です。入力、表示の確認、公開の3段階で進み、公開にはメールアドレスの確認が必要です。数字を順にそろえる「かたちのあいだ」は、同じ「並べ替え」でも操作の性質が異なります。作品を開いて、手の動かし方を比べてみてください。