再帰的な方法を用いてプログラムを組み立ててみよう。
Rubyでは、配列にsortメソッドが備わっているので、
配列に入っているデータを並べ換えてもらうことができる。最近の多くの言語で
は、言語処理系の方に標準で並べ換え用のメソッドが用意されているので
たくさんのデータを並べ換える具体的な手順を知らなくてもよくなっている。
しかしここでは再帰処理を理解する上での題材として、並べ換え手法の ひとつであるクイックソートを自分の手で作ってみよう。
クイックソートはたくさんあるデータ要素を次のような手順で 並べ換える方法である。
大きな箱 Xに任意個のデータが入っているものとする。 クイックソートは以下の手順で行なう。
クイックソートをカードを並べ換える方法に適用して体験して理解し よう。トランプを8枚用意しよう。といっても、すぐには用意できないので 使用済みの紙切れを8枚に切って、それぞれに好きな数字を書こう。
数字を書いた8枚のカード(もっと多くてもよい)が準備できたら、よくシャッ フルする。そうしたら以下の手順で並べ換えよう。
「クイックソートによるカードの並べ換え」
- 並べ換えるべき枚数が1枚以下なら終了、それ以上なら次に進む
- まず1枚のカードを机の真中に置く
- 手に残ったカードから1枚ずつ引いていき
ことをカードがなくなるまで繰り返す(このとき左右に置く カードはでたらめに並べるか重ねておいてよい)
- 真中に置いたカードの数字より小さかったら左側に置く
- そうでなければ右側に置く
- 左側に置いた山を左側の机スペースでクイックソートする
- 右側に置いた山を右側の机スペースでクイックソートする
- おしまい
最初に選ぶカードに何を選んでもクイックソートが成功することを確認しよう
クイックソートを行なうメソッド qsort を定義しよう。
次のような設計とする。
qsort メソッドは引数として配列を受け取る
left、
配列rightを利用する
left, right に対して再帰的に qsort する
left, 真中の値, right を並べて完了
これを実際にメソッドにしてみよう。いきなりメソッドにするのは難しいの で、ことばで骨子を考える。
def qsort(data) # dataは数値の入った配列
if データが1個以下なら
dataそのものを返して並べ換え完了
else
left(左側配列)を新規作成
right(右側配列)を新規作成
変数dataからデータを1個取り出す
これを k とする
残ったdata全てに対して以下を繰り返す
if k より小さかったら
leftに追加
else
rightに追加
end
繰り返し終わり
「leftをqsortしたもの」と k と「rightをqsortしたもの」
をその順に結合したものを結果として並べ換え完了
end
end
※思い出しておこう※
第5回に学習した配列処理のためのメソッドを 忘れていると次に説明する完成プログラムがさっぱり分からない。 以下の配列に関するメソッドを思い出しておくこと。
上記の「ことば」で定義した qsort メソッドを
Rubyプログラムに直そう。
def qsort(data) # 配列を受け取る
if data.length <= 1 # データが1個以下なら
data # そのものを返して並べ換え完了
else
left = Array.new # left(左側配列)を新規作成
right = Array.new # right(右側配列)を新規作成
k = data.shift # 変数dataの先頭を取り出してkにする
for x in data # 残ったdata全てに対して繰り返す
if x < k # kより小さかったら
left << x # 左側に追加
else
right << x # 右側に追加
end
end # 繰り返し終わり
# qsort(left)の末尾にkを足し、さらにqsort(right)を結合
qsort(left).push(k) + qsort(right)
end
end
この qsort メソッドを使って、並べ換えできるか
確認しよう。そのために、配列を読み込んだり、画面に表示したりするメソッド
を作っておこう。
標準入力から順次数値を読み取り配列にするメソッド
def readNumArray()
ary = []
while line=gets # 入力が続く間
datum = line.chomp!.to_i # 整数に直して
ary.push(datum) # aryに追加していく
end
ary # 最後にaryを呼び主に返す
end
配列を受け取り、各要素をカンマで区切って表示する。
これには join
が利用できる。
def printArray(a)
printf("[%s]\n", a.join(", "))
end
これらのメソッドを組み合わせて、プログラムは完成する。
#!/usr/koeki/bin/ruby
# coding: utf-8
def qsort(data) # 配列を受け取る
if data.length <= 1 # データが1個以下なら
data # そのものを返して並べ換え完了
else
left = Array.new # left(左側配列)を新規作成
right = Array.new # right(右側配列)を新規作成
k = data.shift # 変数dataの先頭を取り出してkにする
for x in data # 残ったdata全てに対して繰り返す
if x < k # kより小さかったら
left << x # 左側に追加
else
right << x # 右側に追加
end
end # 繰り返し終わり
# qsort(left)の末尾にkを足し、さらにqsort(right)を結合
qsort(left).push(k) + qsort(right)
end
end
def readNumArray()
ary = []
while line=gets # 入力が続く間
datum = line.chomp!.to_i # 整数に直して
ary.push(datum) # aryに追加していく
end
ary # 最後にaryを呼び主に返す
end
def printArray(a)
printf("[%s]\n", a.join(", "))
end
ary = readNumArray()
puts "整列前:"
printArray(ary)
puts "整列後:"
printArray(qsort(ary))
上記のプログラム、qsort.rb を
保存して実行し、1行に1つずつデータを入れて小さい順に並べ換えできることを
確認せよ。
実行例:
% ./qsort.rb
45
58
5
9
45
20
[C-d]
※問題※
qsortメソッドを書き変えて、数値の大きい順に
並べ換えるようにせよ。実際に実行してそれを確かめよ。