Pythonでアルゴリズム「シェル・ソート」(6):取り出す、挿入する。それだけだ!
はい、こんにちは!本日も、やはりPythonでシェルソートの続きをやります!前回記事では、一番外側のループを解説しました。グループ化するときの要素の間隔をループの都度、2分の1にするのでしたね。
では、今回は、真ん中のループに取り組みます。実際に処理することは「挿入ソート」なわけですが、単純な「挿入ソート」と比べて小難しいです。
「やだな~😑。」
いやいや、丁寧に分析すればきっと分かるはず(と自分にも言い聞かせています😅)。さあ、「シェルソート」の「中ボス」、真ん中のループにジャンプ・イン!
「真ん中のループ」で挿入ソート!?
まず、コードの全体像を再掲します。今回取り組むのは、赤枠内、即ち、for文のブロックである「真ん中のループ」内の一連の処理です。
コードの詳細の分析に先立ち、このループの目標を確認しましょう。何か気づくことはありますか?
「これは、、、『挿入ソート』に似てますな🤔」
そうですね。似てますよね(打ち合わせどおりの回答ありがとうございます🧡)。以前紹介した「挿入ソート」のコードは、次の通りです。
細かいところが異なりますが、基本的な処理の流れは、だいたい同じです。そう、この「真ん中のループ」は、挿入ソートの処理を束ねたものなのです!
よし、これで目的は分かりました!
初期値がGapで始まるRange関数の謎
では、コードの詳細に取り組みます。For文の一行目、つまりループの条件式ですね。今回も、Range関数が使われています。
Range関数と言えば、「第1引数以上、第2引数未満」の連番を作ってくれる関数でした。とすると、カウンタ変数は、どう変化しますか?
連番の先頭の値は、Gap、つまり「2」ですね。要素数の半分ですからね。連番の終了値は、「リストの要素数未満」です。要素数は4ですから、「3」になります。
ということは、カウンタ変数「i」は、2→3と変化するのですね。
でも、なんでGapから始まるのかしらん?それは、次のパートで分かります。
未整列エリアの値を、仮の箱に保管せよ
では、次の行です。Tmpという変数にi番目の値を代入していますね。tmpと言えば、仮に値を格納する変数です(好きに付けた名前ですが、慣習的にそうです)。
ループ一回目では、i番目の値は、Gapの値が代入されます。Gap番目の値「2」を上書きされないように逃がしたんでしょうね。イメージで表すと次の通りです。
そうです。挿入ソートで、いずれ挿入する値を、未整列エリア(といっても値が一つですが)から退避させているんですね。ここまでOKだ😄!
退避したあとどうしますか?
はい、「しかるべきところ」に挿入します。その「しかるべきところ」を決める処理は、さらに内側にあるループで行います。この後、「j=i」という処理がありますが、これは、内側ループのカウンタ変数「j」を初期化しているので、内側ループのときにお話ししましょう。
この行を含めて、内側ループはスキップします。
退避した値を挿入
では、真ん中のループ最後の処理を確認します。下図の赤枠内ですね。これは何をしていますか?
そう、退避したTmpの値を挿入しています。挿入場所は、「j」番目です。「j」という値は、最終的には「挿入される場所の番号」なんだな~ということを頭に入れておくと、内側ループを理解するときに役立つでしょう。
絵にすると次の通りです。
ということで、真ん中のループで行ったことは、
1)挿入する値を取り出す
2)挿入場所を決める(内側ループ)
3)決めた場所に値を挿入する
ということになります。
これで、「真ん中のループ」の全貌が見えました~😉。中ボスを倒しましたね!次回は、ラスボス、内側ループをやっつけましょう!
では、ビーダゼーン!
※私のやる気アップとブログの品質向上につながりますので、記事が気に入られた方は、「ポチっ」と好きボタンを押してくださったり、フォローいただけますと幸いです🙇。