プログラミング言語 Standard ML 入門 (問題の解答例)
13.2 配列のソートアルゴリズム
問 13.1
sort 関数中にコメントで記述されている 配列の2つの要素を入れ替える関数 swap を定義せよ.
解答例 問13.3の解答を参照。
問 13.2
以上の方針に従い,sort 関数中にコメントで記述されている pivot の設定処理を書け.
解答例 問13.3の解答を参照。
問 13.3
partition 関数の中にコメントで記述されている scanRight と scanLeft 関数を作成し, ArrayQuickSortストラクチャを完成させよ.
解答例 ArrayQuickSortストラクチャ全体のコード例を以下に示す。
signature SORT = sig
val sort : ’a array * (’a * ’a -> order) -> unit
end
structure ArrayQuickSort : SORT =
struct
local
open Array
in
fun sort (array,comp) =
let
fun swap (i,j) =
let val temp = sub(array,i)
in (update(array,i,sub(array,j)); update(array,j,temp))
end
fun getPivot (i,j) =
let
val delta = (j-i) div 4
val i = i + delta
val j = i + delta * 3
val mid = i + (j-i) div 2
val ie = sub(array,i)
val je = sub(array,j)
val me = sub(array,mid)
in
case (comp(ie,me),comp(me, je))
of (LESS, LESS) => (mid,me)
| (LESS, _) => (case comp(ie, je) of LESS => (j,je) | _ => (i,ie))
| (_, GREATER) => (mid,me)
| _ => (case comp(ie, je) of LESS => (i,ie) | _ => (j,je))
end
fun qsort (i,j) =
if j <= i+1 then ()
else
let
val pivot =
let val (pi,pivot) = getPivot(i,j-1)
in update(array,pi,sub(array,i));
update(array,i,pivot);
pivot
end
fun partition (a,b) =
if b < a then (a - 1)
else
let
fun scanRight a =
if a > b then a
else
case comp(sub(array,a),pivot) of
GREATER => a
| _ => scanRight (a+1)
val a = scanRight a
fun scanLeft b =
if b < a then b
else
case comp(sub(array,b),pivot) of
GREATER => scanLeft (b - 1)
| _ => b
val b = scanLeft b
in
if b < a then (a - 1)
else (swap(a,b);partition (a+1,b-1))
end
val k = partition (i+1,j-1)
val _ = swap(i,k)
in
(qsort (i,k); qsort (k+1,j))
end
in
qsort (0,Array.length array)
end
end
end
問 13.4
以下の改良を加えよ.
-
1.
配列の大きさが小さい場合,クイックソートアルゴリズムは最適ではない. qsort 関数の冒頭で部分配列の大きさを判定し, 大きさがとの場合を特別に処理するように変更せよ.
-
2.
pivot要素と先頭要素の入れ替え処理を伴うpivotの選択処理は,配列の大きさ が小さいときかえって効率を低下させる. 配列の大きさが30以下の場合,先頭要素をpivotとするように変更せよ.
解答例 以下に例を示す。この例では、pivotの選択も多少の洗練を試みている。 ただし、効果があるかは計算実行環境にもより、一様に最適なものがあるかは、 今後の探求によるところである。
structure ArrayQuickSortOpt : SORT =
struct
local
open Array
in
fun sort (array,comp) =
let
fun swap (i,j) =
let val temp = sub(array,i)
in (update(array,i,sub(array,j)); update(array,j,temp))
end
fun sort3 i =
case comp(sub(array,i),sub(array,i+1))
of GREATER =>
(case comp(sub(array,i+1),sub(array,i+2))
of GREATER => (* 3 2 1 *)
swap(i,i+2)
| _ => (case comp(sub(array,i),sub(array,i+2))
of GREATER => (* 3 1 2 *)
let val ei = sub(array,i) in
(update(array,i,sub(array,i+1));
update(array,i+1,sub(array,i+2));
update(array,i+2,ei))
end
| _ => (* 2 1 3 *)
(swap(i,i+1))
)
)
| _ =>
(case comp(sub(array,i+1),sub(array,i+2))
of GREATER =>
(case comp(sub(array,i),sub(array,i+2))
of GREATER => (* 2 3 1 *)
let val ei = sub(array,i) in
(update(array,i,sub(array,i+2));
update(array,i+2,sub(array,i+1));
update(array,i+1,ei))
end
| _ => (* 1 3 2 *)
(swap(i+1,i+2))
)
| _ => (* 1 2 3 *)
())
fun getPivot (i,j) =
let
val delta = (j-i) div 4
val i = i + delta
val j = i + delta * 3
val mid = i + (j-i) div 2
val ie = sub(array,i)
val je = sub(array,j)
val me = sub(array,mid)
in
case (comp(ie,me),comp(me, je))
of (LESS, LESS) => (mid,me)
| (LESS, _) => (case comp(ie, je) of LESS => (j,je) | _ => (i,ie))
| (_, GREATER) => (mid,me)
| _ => (case comp(ie, je) of LESS => (i,ie) | _ => (j,je))
end
fun qsort (i,j) =
if j < i+2 then ()
else if j = i+2 then case (comp(sub(array,i),sub(array,i+1)))
of GREATER => swap(i,i+1)
| _ => ()
else if j = i + 3 then sort3 i
else
let
val pivot = if (j-i) < 30 then sub(array,i)
else
let val (pi,pivot) = getPivot(i,j-1)
in update(array,pi,sub(array,i));
update(array,i,pivot);
pivot
end
fun partition (a,b) =
if b < a then a
else
let
fun scanRight a =
if a > b then a
else
case comp(sub(array,a),pivot)
of GREATER => a
| _ => scanRight (a+1)
val a = scanRight a
fun scanLeft b =
if b < a then b
else
case comp(sub(array,b),pivot)
of GREATER => scanLeft (b - 1)
| _ => b
val b = scanLeft b
in
if b < a then a
else (swap(a,b);
partition (a+1,b-1))
end
val a = partition (i+1,j-1)
val _ = swap(i,a-1)
in
(qsort (i,a-1); qsort (a,j))
end
in
qsort (0,Array.length array)
end
end
end