DAKPPOの日記

技術ブログの体を装った日記

MIT OCW 6.006 Lecture 1: Peak-finder ノート

ocw.mit.edu

Lecture 1で紹介された問題「Peak-finder」に関するノートと考えたことのまとめ。

One-dimensinal version

Problem

Find a peak if it exists.

1 2 3 4 5 6 7 8 9
a b c d e f g h i

a~i: integer
表記: {1}=a, {2}=b, ... , {9}=i
  • Position 2 is a peak if and only if b >= a and b >= c.
  • Position 9 is a peak if and only if i >= h.

Argue:Peakは常に存在するか?

Yes.

  • 素数が1の場合はその要素がPeak
  • 素数が2以上で、Peakではない要素のみからなる配列が存在すると仮定し、その構築を試みる。
    • {1}はPeakではない。よって{1} < {2}
    • {2}も同様。よって{1} < {2} < {3}
    • ...
    • {1} < {2} < ... < {N} (Nは配列の要素数)
    • このとき{N}はPeakになり、仮定に反する。

Solution:Straightforward Algorithm

左から順に要素がPeakであるか確認する。

計算量:O(N)

  • 最悪のケースは単調増加数列。すべての要素を調べることになる。
  • 平均的には N/2 個の要素を調べることになる。

左から順ではなく中央の要素から確認するようにした場合、計算量は依然O(N)だが、調べる要素の個数は最悪でもN/2個、平均でN/4個になる。

Solution: Divide & Conquer

以下の要領で探索範囲の大きさを半分に限定していく。

  • {n/2} < {n/2-1} の場合、 {1} ~ {n/2-1} にPeakがある。
  • {n/2} < {n/2+1} の場合、 {n/2+1} ~ {n} にPeakがある。
  • いずれでもない場合は、{n/2}がPeak

計算量:O(lgN)

  • T(N)を計算量とする。
    • T(N)
    • = T(n/2) + O(1)
    • = T(n/4) + O(1) + O(1)
    • = ...
    • = O(1) + ... + O(1) (lgN times)
    • = O(lgN)

Argue: Is That Algorithm Correct?

  • 探索範囲 {s} ~ {e} が、常に {s} < {s+1} と {e} < {e-1} を満たす場合:
    • 探索範囲に含まれる要素の数が1 or 2のとき、この条件は満たせない。
    • 探索範囲に含まれる要素の数が3のとき、この条件を満たすのは {s} < {s+1} > {s+2 = e} のケースのみなので次のステップで見つかる
    • 探索範囲に含まれる要素の数が3より大きくても、探索範囲を狭めていくといずれ要素数が3になって見つかる

{1} >= {2} or {N} >= {N-1} の場合上記の前提が満たされないが、この場合は最悪のケースでも探索範囲が{1} ~ {N} => {1} ~ {N/2} => ... {1} ~ {2} などとなるのでlgNステップで見つかる。

Two-dimensional version

Problem

Find a 2D-peak if it exists.

_ c _ _
b a d _
_ e _ _
_ _ _ _
N rows, M columns

a~d, _: integer
表記: {1,2}=c, {2,1}=b, {2,2}=a, {2,3}=d, {3,2}=e
  • a is a 2D-peak if a>=b, a>=c, a>=d, a>=e.

Attempt: Extend 1D Divide and Conquer to 2D

    j=M/2
  _ * _ _
i * * * *
  _ * _ _
  _ * _ _
  • 中央の列j=m/2をとり、1D-peakを見つける。(行iに見つけたとする)
  • 行iの中で1D-peakを見つける。これを2D-peakとする。

問題点:2D-peakが行iにあるとは限らない

うまくいかない例:
__ __ 10 __
14 13 12 __
15  9 11 __
16 17 19 20

Attempt:

  • 中央の列j=m/2をとる。
  • 列j内の最大値を見つける。(見つけた場所を(i,j)とする)
  • (i,j) < (i,j-1)なら左側を残す。
  • (i,j) < (i,j+1)なら右側を残す。
  • どちらでもないなら(i,j)は2D-peak。
  • 残り1列になったら、列内の最大値が2D-peak。

計算量:O(NlgM)

  • T(N,M)を計算量とする。
    • T(N,M)
    • = T(N,M/2) + O(N)
    • = T(N,M/4) + O(N) + O(N)
    • = ...
    • = O(N) + ... + O(N) (lgM times)
    • = O(NlgM)

Is That Algorithm Right?

たとえばこういうケース

2 7
3 8
4 7
5 6

列1の最大値は{4,1}=5。{4,1}<{4,2}なので探索範囲を列2に絞るが、この時点で{列1の任意の値} <= {列2の最大値}が確定する。

このように、探索範囲内に残った両端の列の最大値は、隣接する探索範囲外の列の値に対して常に2D-peakの条件を満たす。したがって、最後の1列になるまで探索範囲を限定し、列内で最大値をとればそれは2D-peak。

ちなみに、列内の最大値をとる代わりに列内の1D-peakを使用した場合は上記の性質が失われるので、たとえば以下のケースで2D-peak検出に失敗する。

2 3
3 4
2 5
9 6

2D-peakは{1,4}=9のみだが、{2,4}=6が検出される!