これを行うには 2 つの方法があります。reduce
最初の を使用した方がはるかに慣用的だと思いますが、同じ数の比較を使用していますが、トーナメント スタイルの構造を行っていません。トーナメント スタイルの構造を行う 2 つ目は、実際には、一般化されたマージ ソートの特殊なケースにすぎません。比較回数が同じなのは、トーナメント形式の比較では、
最小(a、b、c、d) =最小(最小(a、b)、最小(c、d))
そしてreduce
定式化では、
min(a,b,c,d) = min(min(min(a,b),c),d)
どちらも最低レベルの min プロシージャを 3 回呼び出す必要があります。
ベースのreduce
アプローチ
この解決策は(私の経験では Lisp 言語でfold
より一般的に呼ばれます) を使用します。reduce
スキーム (R 5 RS) はインクルードもフォールドもしませんがreduce
、実装は簡単です:
(define (reduce function initial-value list)
(if (null? list)
initial-value
(reduce function (function initial-value (car list))
(cdr list))))
左結合の折り畳みは末尾再帰的で効率的です。二項関数f、初期値i、およびリスト [ x 1 ,…, x n ] を指定すると、 f(f(…f(f( i , x 1 ), x 2 )…), x n -が返されます。 1 )、x n )。
この場合、二項関数はmin2
です。R5R5 には実際にはすでにn項が含まれています(まあ、実際には少なくとも 1 つの引数が必要です。少なくとも 1つの項です) 。これは、バイナリ関数として既に機能することmin
を意味します。min
では、最初min
に行うだけです。(min a b c d)
したがって、完全を期すために、min2
正確に 2 つの引数を受け入れる a を次に示します。
(define (min2 a b)
(if (< a b)
a
b))
次に、n 項はmin*
単純にmin2
初期値とリストの縮小です。引数リストの表記を使用して.
、これを少なくとも 1 つの引数を必要とする可変引数関数にすることができます。(min* x) => x
これは、より典型的な多引数呼び出しに加えて、を実行できることを意味します。
(define (min* a . rest)
(reduce min2 a rest))
例えば:
(min* 4 2 1 3)
;=> 1
マージソートに基づく真のトーナメントスタイルのソリューション
適切なトーナメント スタイルは、実際にはマージ sortmin
と同形です。マージソートは、長さ 1 のリストが生成されるまで、リストを再帰的に半分に分割します (これは、リストを実際に新しいリストに分割するのではなく、元のリストのインデックスを使用してその場で行うことができます)。次に、隣接するリストがマージされます長さ 2 のリストを生成します。次に、隣接する長さ 2 のリストがマージされて、長さ 4 のリストが生成され、ソートされたリストが 1 つだけになるまで、というように繰り返されます。(入力リストの長さが 2 の累乗でない場合、ここでの数値は常に完全に機能するとは限りませんが、同じ原則が当てはまります。) マージ関数をパラメーターとして受け取るマージソートの実装を作成すると、次に、より小さい値を含む 1 つの要素リストを返すようにすることができます。
まず、リストを左右に分割する関数が必要です。
(define (split lst)
(let loop ((left '())
(right lst)
(len (/ (length lst) 2)))
(if (< len 1)
(list (reverse left) right)
(loop (cons (car right) left)
(cdr right)
(- len 1)))))
> (split '(1 2 3 4))
((1 2) (3 4))
> (split '(1))
(() (1))
> (split '(1 2 3))
((1) (2 3))
マージソートの実装は非常に簡単になりました:
(define (merge-sort list merge)
(if (or (null? list) (null? (cdr list)))
list
(let* ((sides (split list))
(left (car sides))
(right (cadr sides)))
(merge (merge-sort left merge)
(merge-sort right merge)))))
まだマージ手順が必要です。2 つのリストを取り、それらのソートされた要素のリストを返す標準的なものではなく、2 つのリストを取り、それぞれが最大 1 つの要素を持ち、リストの最大 1 つが空である可能性があるものを必要とします。いずれかのリストが空の場合、空でないリストが返されます。両方のリストが空でない場合は、要素が小さい方が返されます。私はそれを呼んだmin-list
。
(define (min-list l1 l2)
(cond
((null? l1) l2)
((null? l2) l1)
(else (if (< (car l1) (car l2))
l1
l2))))
この場合、マージ プロシージャが であるmin*
を呼び出すように定義できます。マージソートは 1 つの要素を含むリストを返すため、リストからその要素を取得する必要があります。merge-sort
min-list
car
(define (min* a . rest)
(car (merge-sort (cons a rest) min-list)))
(min* 7 2 3 6)
;=> 2