「 トランプのババ抜きやってて、一番右のカードを 真ん中らへんに入れるやつよ」
「 これを A, B, C, D, E に並び替えなおすのが ソート(Sort) なのよ」
「 じゃあ まず きふわらべソート を実装しようぜ? Python で」
cards = ['C', 'E', 'A', 'D', 'B']
print(f"(1) {cards}")
# in-placeなスワップ と呼ばれる技
temp = cards[2]
cards[2] = cards[1]
cards[1] = cards[0]
cards[0] = temp
print(f"(2) {cards}")
# in-placeなスワップ
temp = cards[4]
cards[4] = cards[3]
cards[3] = cards[2]
cards[2] = cards[1]
cards[1] = temp
print(f"(3) {cards}")
# in-placeなスワップ
temp = cards[4]
cards[4] = cards[3]
cards[3] = temp
print(f"(4) {cards}")
(1) ['C', 'E', 'A', 'D', 'B']
(2) ['A', 'C', 'E', 'D', 'B']
(3) ['A', 'B', 'C', 'E', 'D']
(4) ['A', 'B', 'C', 'D', 'E']
「 👆 r を2番目に置いてみましょう。
ひとまず、今 r 番目にあるカードを key と呼ぶとします」
「 👆 l が 0 のときは何もせず、key を l+1
の添え字のところに入れます」
「 👆 r が 1 増えます。
key が A になるのと、 l が r-1 になるのは、 r の動きと一緒に おまとめ されています」
「 👆 そのときは l の添え字のところにあるカードも見ます。
l のカードと、 r のカードを見比べて、 r のカードより l のカードの方が大きいときは、
並び替えを これから行います」
「 👆 添え字が l のカードを、添え字が l+1 のところへ上書きします」
「 l が 0 になると、 l は 減ろうとするのを止めます」
「 👆 key を l+1 の添え字のところに 上書きします」
「 👆 D と E はひっくり返っているので、上書きコピーが起こります」
「 A と E は ひっくり返っていないので 何も起きません」
「 そんなん繰り返しても 先頭の C が 真ん中にこなくないか?」
「 疑似コードが間違ってんだろ。 もうMITの教科書なんか信用しないぜ。 だいたい 分かったな」
📖 挿入ソート
# [挿入ソート](https://ja.wikipedia.org/wiki/%E6%8C%BF%E5%85%A5%E3%82%BD%E3%83%BC%E3%83%88)
cards = ['C', 'E', 'A', 'D', 'B']
print(f"(1) {cards}")
for i in range(2, len(cards)):
tmp = cards[i]
if cards[i-1] > tmp:
j = i
while True:
cards[j] = cards[j-1]
j -= 1
if j > 0 and cards[j-1] > tmp:
pass
else:
break
cards[j] = tmp
print(f"(2) {cards}")
print(f"(3) {cards}")
(1) ['C', 'E', 'A', 'D', 'B']
(2) ['A', 'C', 'E', 'D', 'B']
(2) ['A', 'C', 'D', 'E', 'B']
(2) ['A', 'B', 'C', 'D', 'E']
(3) ['A', 'B', 'C', 'D', 'E']
「 👆 2番目に r を置きます。 添え字が r のところにあるカードを tmp とします」
「 👆 左隣のカードと比較して、大小関係がひっくり返っていたら、入替処理を始めます」
「 👆 j-1 の添え字のところにあるカードを、 j の添え字のところに上書きします」
「 j が 0 の添え字のところにあるときは、入替動作を停止します」
「 👆 添え字が j のところに、 tmp のカードを上書きします」
「 揃っているだけで、次の1個が また挿入されてくるから、確定ではないそうだぜ」
「 プログラム・チャンネルで質問したら、疑似コードの添え字は 0から始まるのではなく、1から始まるそうよ」
# MIT教科書 挿入ソート
# 添え字は 1 から使用
A = [' ', 'E', 'D', 'C', 'B', 'A']
# Start
print(f"(S) {A}")
for j in range(2, len(A)):
key = A[j]
# A[j]をソート済みの列 A[1..j-1]に挿入する
i = j-1
while i > 0 and A[i] > key:
A[i+1] = A[i]
i = i - 1
A[i+1] = key
print(f"({j}) {A}")
(S) [' ', 'E', 'D', 'C', 'B', 'A']
(2) [' ', 'D', 'E', 'C', 'B', 'A']
(3) [' ', 'C', 'D', 'E', 'B', 'A']
(4) [' ', 'B', 'C', 'D', 'E', 'A']
(5) [' ', 'A', 'B', 'C', 'D', 'E']
「 👆 左端から2枚目を 右手へ、 その左隣を 左手へ 持つのが 初期配置だぜ」
「 👆 右手に持ってるのは、 スワップのtmp
だと思ってくれだぜ。
何のことか分からないかもしれないが ひとまず そう覚えておけだぜ」
「 👆 極端に色分けすると、 上図の 赤色が 整列されたカード、 緑色が 現在のカード、
水色が 残りのカード だぜ」
「 👆 これを情報処理では 赤色のカードの添え字を j-1、 緑色のカードの添え字を j、
水色のカードの添え字を j+1 .. n
と言ってるわけだな」
「 👆 赤色のところを ループ不変式 (loop invariant)と呼ぶらしいぜ」
「 プログラミング・チャンネルによると、 ループ不変条件 とでも訳した方がいいもので、
コンパイラから見ると、
ループしている間 ずっと値が変わらないから ループの外に出してもよくね?
というもの らしいわよ」
「 これから 緑色のカードを挿入するのだから、途中で 変わるだろ」
「 フーム。 なんか ループの前判定の条件文 のことを ループ不変式(loop invariant)と呼んでる気がするな」
「 右手の1枚のカードを 左手のカードの束に挿入しようとするとき、左手のカードの束は整列済みだ、
ということを 言ってる気がするぜ」
「 プログラミング・チャンネルによると、 ループ不変式 というのは、
ループの前、
ループの最中の末尾、
ループの後、
この3つの どこを取っても 条件式が真であることが 崩れないような、条件式らしいぜ」
「 プログラミング・チャンネルが 記事を書いてくれだぜ。お父んは黙れ」
「 ループの最中に 緑色のカードを挿入したら 整列していない状態が 一時的にできるんじゃないの?」
「 調べてみると 整列していない状態は 一時的にも出てこなかった。説明しよう」
B, C, D, A
↓
B, C, D, D
↓
B, C, C, D
↓
B, B, C, D
↓
A, B, C, D
「 同じカードが2枚被っていて Aが無い状態でも 整列されている と言っていいのかだぜ?」
「 ラーメン食べ郎が言っている 同じカードが2枚被っている とか、 Aが無い という状態は
気にすると 何も言えなくなるから、
コンピューター・サイエンスでは 何か言えるところ だけを言うようよ。
前者が 強い条件、 後者が 弱い条件」
Crieitは個人で開発中です。
興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!