IT1-CODE-POCKET

交換法(バブルソート)シミュレーター

交換法は、隣どうしを比べて順序が逆なら入れ替える整列です。 値が入れ替わる瞬間と、1パスごとに右端が確定していく様子を1ステップずつ止めて確認できます。

動きを1ステップずつ確認する

要素数(個)
並び
配列 Nums
    • 比較中
    • 入れ替え
    • 確定
    • 未確定

    j▼ と右隣のマスをペアで比べます。

    i(パス)
    j
    比較 0
    交換 0
    STEP 1 / 1 読み込み中です。

    パソコンではキーボードでも操作できます: 次へ / 前へ / スペース 再生・一時停止

    数値を自分で入力する

    カンマ区切りで 0〜99 の整数を入力します。同じ値を混ぜても動きます。

    実行ログを見る(ここまでの処理)

      ALGORITHM

      交換法とは何をしているか

      交換法(バブルソート)は、隣り合う2つの値を比べ、順序が逆なら入れ替えるという操作を繰り返す整列です。 左から順に隣どうしを比べていくと、大きい値が次々と後ろへ押し出され、 1回のパスが終わるたびに、未確定部分でいちばん大きい値が右端に確定します。

      水中の泡(bubble)が少しずつ上へ浮かんでいく様子に似ているため、バブルソートと呼ばれます。 シミュレーターでは、確定したマスが緑になり、右から順に緑が増えていく様子が確認できます。

      temp がなければ値が消える

      入れ替えは3行で書きます。この temp一時的に値を置いておく変数で、これがないと入れ替えは成り立ちません。

      temp = Nums[j]          ← 左の値を退避
      Nums[j] = Nums[j + 1]   ← 左に右の値を入れる
      Nums[j + 1] = temp      ← 右に退避した値を入れる

      もし1行目を書かずに Nums[j] = Nums[j + 1] から始めると、その瞬間に Nums[j] の値が上書きされて消えます。 すると3行目で戻すべき値がもう手元にありません。結果として同じ値が2つ並ぶことになります。 「なぜ temp が必要か」は説明を求められることがあるので、この流れで覚えておくと確実です。

      比べる範囲がだんだん狭くなる

      内側の繰り返しは 要素数 - 2 - i までです。- i があるのは、 すでに確定した右側をもう一度比べる必要がないからです。 1回目のパスでは右端の1個が確定し、2回目のパスでは右端の2個が確定しているので、その分だけ範囲を狭められます。

      確認方法:シミュレーターで「次へ」を押していくと、パスの終わりに 「いちばん大きい○が添字△に確定しました」と表示され、そのマスが緑になります。 次のパスでは j の動く範囲が1つ短くなることを、j▼ の動きで確認してください。

      比較回数は一定、交換回数はデータ次第

      基本の書き方では、比較回数は並び方に関係なく一定です。要素数 n のとき (n-1) + (n-2) + … + 1 = n(n-1)/2 回になります。 一方、交換回数はデータによって変わります。すでに整列済みなら0回、逆順なら比較のたびに交換が起きて最大になります。

      並び(要素数5) 比較回数 交換回数
      整列済み [1,2,3,4,5] 10 回 0 回
      ばらばら [5,2,4,1,3] 10 回 7 回
      逆順 [5,4,3,2,1] 10 回 10 回(最大)

      表の3つは、シミュレーターの「整列済み」「ばらばら」「逆順」でそのまま確認できます。 比較回数はどれも10回で同じなのに、交換回数だけが変わることを目で確かめてください。

      早期終了という工夫

      整列済みのデータでも最後まで比べ続けるのは無駄です。そこで「このパスで交換が1回も起きなかったら、すでに整列済みだから抜ける」という判定を足すことがあります。 条件で「交換がなければ抜ける(早期終了)」を選ぶと、整列済みのデータが1パスだけで終わる様子が確認できます。 ただし逆順のような最悪の場合は最後まで回るので、最悪の計算量は O(n²) のままです。

      CODE

      3つの書き方で見る交換法

      擬似言語(DNCL風)・Python・JavaScript で並べました。変数名と処理の順番はすべて揃えてあります。

      Nums = [5, 2, 4, 1]
      swap = 0
      
      i を 0 から 要素数(Nums) - 2 まで 1 ずつ増やしながら繰り返す:
      |j を 0 から 要素数(Nums) - 2 - i まで 1 ずつ増やしながら繰り返す:
      ||もし Nums[j] > Nums[j + 1] ならば:
      |||temp = Nums[j]
      |||Nums[j] = Nums[j + 1]
      |||Nums[j + 1] = temp
      ⎿⎿⎿ swap = swap + 1
      
      表示する(Nums, swap)
      範囲の書き方の違い:擬似言語は「0 から 要素数 - 2 - i まで」と終わりの値を書きますが、 Python の range() と JavaScript の <その値の直前までという意味です。 そのため擬似言語の - 2 - i が、Python・JavaScript では - 1 - i になります。 指す範囲は同じで、書き方の約束が違うだけです。

      COST

      比較回数と計算量

      要素数 比較回数 交換回数(最大) 計算量
      4 個 6 回 6 回 O(n²)
      5 個 10 回 10 回 O(n²)
      10 個 45 回 45 回 O(n²)
      n 個 n(n-1)/2 回 n(n-1)/2 回 O(n²)

      要素数が2倍になると、手間はおよそ4倍になります。これが O(n²)(要素数の2乗に比例)の意味です。 探索の 二分探索 が O(log n) だったのと比べると、整列はずっと重い処理だと分かります。 だからこそ「1回探すだけなら整列せずに 線形探索」という判断が成り立ちます。

      なお、要素数10個で「逆順」を選ぶと45回の交換が起きるため、ステップ数もかなり多くなります。 全体の流れをつかみたいときは「自動再生」+「はやい」で通して見るのがおすすめです。

      EXAM POINT

      共通テスト「情報Ⅰ」での出題ポイント

      1. 入れ替えの3行:temp を使った入れ替えの順番。3行のうち1行が空欄になる形が定番です。
      2. 内側の範囲:要素数 - 2 - i- i の部分。「なぜ範囲が狭くなるのか」を説明させる問題もあります。
      3. 1パス後の配列:「1回目のパスが終わった時点の配列を答えよ」という問題。途中の状態を書けることが大切です。
      4. 交換回数:与えられた配列で交換が何回起きるかを数える問題。比較回数と混同しないよう注意します。
      5. 比較演算子の向き:>< にすると大きい順(降順)に並びます。「降順にするにはどこを変えるか」という形で問われます。
      1パス後の状態を答える問題のコツ:シミュレーターでパスの区切り(緑のマスが増えるところ)で止め、 そのときの配列を書き写してみてください。自分で予測してから答え合わせをすると、途中状態を追う力がつきます。

      MISTAKES

      よくある間違い

      間違い 何が起きるか 正しくは
      temp を使わずに入れ替える 元の値が上書きされて消え、同じ値が2つ並ぶ 先に temp へ退避してから入れ替える
      temp に戻す順番を間違える 入れ替わらず、値が壊れる 退避 → 左へ代入 → 右へ temp の順に書く
      内側の範囲を要素数 - 1 までにする Nums[j + 1] が範囲外を参照する 要素数 - 2 - i まで(j + 1 が最後の添字に収まる)
      比較回数と交換回数を混同する 整列済みでも交換回数を n(n-1)/2 と答えてしまう 比較は常に一定、交換は条件が成り立った回数だけ
      1パスで整列が終わると思う 1パス後の配列を最終結果と答えてしまう 1パスで確定するのは右端の1個だけ

      CHECK

      理解度チェック

      まず自分で考え、答えを開く前に上のシミュレーターで確かめてみてください。

      Q1. Nums = [5, 2, 4, 1] で、1回目のパスが終わった時点の配列はどうなりますか。
      [2, 4, 1, 5]。 5と2を入れ替えて [2,5,4,1]、5と4を入れ替えて [2,4,5,1]、5と1を入れ替えて [2,4,1,5] です。 いちばん大きい 5 が右端に確定しますが、残りはまだ整列していません。
      Q2. すでに整列済みの [1, 2, 3, 4, 5] を基本の書き方で処理すると、比較回数と交換回数はいくつですか。
      比較 10 回、交換 0 回。 条件 Nums[j] > Nums[j + 1] が一度も成り立たないので交換は起きませんが、 基本の書き方では比較そのものは最後まで行われます。 「交換がなければ抜ける」を選ぶと、比較は4回で終わります。
      Q3. 要素数5の配列で、交換回数が最大になるのはどんな並びですか。また何回ですか。
      逆順 [5, 4, 3, 2, 1] のときで、10回。 すべての比較で必ず入れ替えが起きるため、交換回数が比較回数と同じ n(n-1)/2 = 10 回になります。 シミュレーターの「逆順」で確認できます。
      Q4. 条件を Nums[j] < Nums[j + 1] に変えると、どう並びますか。
      大きい順(降順)に並びます。 「左が右より小さければ入れ替える」ことになるので、小さい値が後ろへ送られます。 比較演算子の向きを変えるだけで昇順と降順が切り替わるのは、穴埋めでよく問われるポイントです。

      次に確認したいアルゴリズム