web-dev-qa-db-ja.com

配列内の極小値を見つける

整数の配列が与えられたら、極小値を見つけます。要素A [i]は、A [i-1]> A [i]およびA [i] <A [i + 1]の場合、i = 1 ... n-2の場合、極小値として定義されます。境界要素の場合、数値は隣接する数値よりも小さくなければなりません。

局所最小値が1つしかない場合は、修正されたバイナリ検索で解決できます。しかし、配列に複数の極小値が存在することがわかっている場合、O(log n)時間で解決できますか?

27
devsda

配列要素が明確であることが保証されていない場合、O(log n)時間でこれを行うことはできません。その理由は次のとおりです。n> 1の値がすべて同じである配列があるとします。この場合、隣接する要素よりも小さい要素はないため、どの要素も局所的な最小値にできません。ただし、すべての値が同じであることを判断するには、すべての配列要素を確認する必要があります。これには、O(n)時間を使用します。O(n) time、必ずしもすべての配列要素を見ることができるわけではありません。

一方、配列要素が明確であることが保証されている場合、次の観測を使用してO(log n)時間でこれを解決できます。

  1. 要素が1つだけの場合、それは局所的な最小値であることが保証されています。
  2. 複数の要素がある場合は、中央の要素を見てください。それがローカルの最小値であれば、完了です。それ以外の場合、その隣の要素の少なくとも1つはそれより小さくなければなりません。ここで、小さい要素の1つから開始し、中央の要素から離れる方向に配列の端の1つに向かって徐々に移動するとどうなるかを想像してください。各ステップで、次の要素は前の要素よりも小さいか、大きくなります。最終的には、この方法でアレイの最後に到達するか、ローカルミニマムに到達します。これは、couldを実行してローカル最小値を見つけることを意味することに注意してください。ただし、実際にはそうするつもりはありません。代わりに、配列の半分を破棄する理由として、配列の半分に局所的な最小値が存在するという事実を使用します。残りの部分では、ローカルミニマムを見つけることが保証されています。

したがって、次の再帰的アルゴリズムを構築できます。

  1. 配列要素が1つだけの場合、それは最小値です。
  2. 2つの配列要素がある場合は、それぞれを確認します。 1つはローカルミニマムでなければなりません。
  3. それ以外の場合は、配列の中央の要素を見てください。極小値の場合、それを返します。それ以外の場合、少なくとも1つの隣接する値はこの値より小さくなければなりません。その小さな要素を含む配列の半分で再帰します(中央ではありません)。

これには再帰関係があることに注意してください

T(1)≤1

T(2)≤1

T(n)≤T(n/2)+ 1

マスター定理を使用すると、必要に応じてこのアルゴリズムが時間O(log n)で実行されることを示すことができます。

お役に立てれば!

また、このアルゴリズムが機能するのは、隣接する要素よりも小さい場合に配列のエッジがローカルミニマムとしてカウントされる場合のみであることに注意してください。

59
templatetypedef

極小値の数は_n/2_になります。すべてをO(log n)時間で列挙することはできません。

7
foxcub

分割統治アルゴリズムを使用します。 m = n/2とし、値A [m](つまり、配列の中央の要素)を調べます。

ケース1:A [m-1] <A [m]。次に、配列の左半分にローカルミニマムが含まれている必要があるため、左半分を再帰します。これは矛盾によって示すことができます:A [i]は各0≤i <mの局所最小値ではないと仮定します。この場合、A [m-1]は局所的な最小値ではなく、A [m-2] <A [m-1]であることを意味します。同様に、A [m -3] <A [m -2]。このように続けて、A [0] <A [1]を取得します。しかし、A [0]は最初の仮定に反して、極小値です。

ケース2:A [m + 1]> A [m]。その場合、配列の右半分には局所的な最小値が含まれている必要があるため、右半分で再帰します。これは、ケース1と対称的です。

ケース3:A [m − 1]> A [m]およびA [m + 1] <A [m]。その場合、A [m]は極小値なので、それを返します。実行時間の繰り返しはT(n) = T(n/2) +Θ(1)、これによりT(n) =Θ(log n)。

5
Elad

実際、以前のアルゴリズムを変更して、O(log n)時間ですべての最大値を取得できます。提供されたすべての入力に対してうまく機能することをテストしました。あなたのフィードバックを教えてください

public class LocalMaximas {

@Test
public void test () {
    System.out.println("maximas: please modify code to handle if array size is <= 2");

    int []a = {5,8,10,25,6,3,44,51,55,56,57,58,34,5,59,2};
    localMaximas(a);

    int []b = {9,7,2,8,5,6,3,4, 2}; //9,8,6,4
    localMaximas(b);

    int [] c= {15, 13, 12, 18, 19, 20, 7, 6, 5, 4, 3, 2, 1};//15,20
    localMaximas(c);
}

public  void localMaximas (int [] a) {
    System.out.println("\n\n");
    if(isMaxima(a,0)) {
        System.out.println(a[0]);
    }
    if(isMaxima(a,a.length-1)) {
        System.out.println(a[a.length-1]);
    }
    localMaximas(a,0,a.length-1);
}

int localMaximas(int []a,int low, int high) {
    int mid = (low+high)/2;
    if(high-low > 3) {     // more than 4 items in currently  divided array
        if(isMaxima(a,mid)) {
            System.out.println(a[mid]);
        }   
        localMaximas(a,low, mid);
        localMaximas(a,mid, high);
    }
    else if(high-low == 3){ //exactly 4 items in currently  divided array
        localMaximas(a,low, mid+1);
        localMaximas(a,mid, high);
    }   
    else if((high-low == 2) && (isMaxima(a,low+1))) {
        System.out.println(a[low+1]);
    }
    return 0;
}

int maxof(int []a, int i, int j) {
    if(a[i] <a[j]) {
        return j;
    }
    else {
        return i;
    }
}

boolean isMaxima(int []a ,int mid) {
    if(mid == 0) {
        if(maxof(a, mid, mid+1) == mid) {
            return true;
        }
        else {
            return false;
        }
    }
    else if(mid==a.length-1) {
        if(maxof(a,mid,mid-1) == mid) {
            return true;
        }
        else {
            return false;
        }
    }
    else {
        if((maxof(a, mid, mid+1) == mid) && (maxof(a, mid, mid-1) == mid)) {
            return true;
        }
        else {
            return false;
        }           
    }
}
}
0
Sohan

元の質問は完全ではありません。

配列内の極小値を見つける !で完全な質問と詳細な説明を見つけました。 -私のブログではありません

最初の2つの数値が減少し、最後の2つの数値が増加する一意の整数の配列が与えられた場合、配列内の極小値を見つけます。配列内の数値は、左右の数値よりも小さい場合、ローカルミニマムと呼ばれます。

たとえば、配列9,7,2,8,5,6,3,4では、2はその左と右の数字7および8よりも小さいため、極小値です。同様に5は8の間であるため、もう1つの極小値ですおよび6、両方とも5より大きい。

極小値のいずれかを見つける必要があります。

0
jeffery.yuan

この配列ではアルゴリズムは機能しません

15, 13, 12, 18, 19, 20, 7, 6, 5, 4, 3, 2, 1

ここで、ローカルミニマムは12.です。しかし、7である中間要素をチェックすると、アルゴリズムは左半分(ミニマムがある)を破棄し、右半分をチェックインします。したがって、動作しません

配列にA [1]≥A [2]かつA [n − 1]≤A [n]という特別なプロパティがある場合にのみ機能すると思います。

0
user2316569

O(log n)で機能するソリューションを次に示します。基本的に、これはマージソートアプローチ(分割および征服)で機能します。

public class LocalMaxima {
    int []a = {5,8,10,25,6,3,44,51,55,56,57,58,34,5,59};

    @Test 
    public  void localMaxima () {
        System.out.println((a[localMaxima(0,a.length-1)]));
    }

    int localMaxima(int low, int high) {
        if(high-low > 2) {
            int mid = (low+high)/2;
            return maxof(localMaxima(low,mid),localMaxima(mid+1, high));
        }
        else if(high-low == 1) {
            return maxof(high,low);
        }
        else if(high-low == 0) {
            return high;
        }
        if(high-low == 2) {
            return maxof(maxof(low, high),low+1);
        }
        return 0;
    }

    int maxof(int i, int j) {
        if(a[i] <a[j]) {
            return j;
        }
        else {
            return i;
        }
    }
}
0
Sohan