在数组中查找局部最小值

Posted

技术标签:

【中文标题】在数组中查找局部最小值【英文标题】:Find local minima in an array 【发布时间】:2012-08-27 14:57:17 【问题描述】:

给定一个整数数组,求局部最小值。如果 A[i-1] > A[i] 且 A[i]

我知道如果只有一个局部最小值,那么我们可以通过修改后的二进制搜索来解决。 但是如果已知数组中存在多个局部最小值,是否可以在O(log n)时间解决?

【问题讨论】:

【参考方案1】:

如果不能保证数组元素是不同的,则不可能在 O(log n) 时间内做到这一点。原因如下:假设您有一个数组,其中所有 n > 1 值都相同。在这种情况下,没有一个元素可以是局部最小值,因为没有一个元素小于它的邻居。但是,为了确定所有值都相同,您必须查看所有数组元素,这需要 O(n) 时间。如果您使用的时间少于 O(n),则不一定能查看所有数组元素。

另一方面,如果保证数组元素是不同的,则可以使用以下观察在 O(log n) 时间内解决此问题:

    如果只有一个元素,则保证为局部最小值。 如果有多个元素,请查看中间元素。如果它是局部最小值,那么您就完成了。否则,它旁边的至少一个元素必须小于它。现在,想象一下,如果您从较小的元素之一开始,并沿着远离中间元素的方向逐渐向数组的一端移动,会发生什么情况。在每一步,要么下一个元素小于前一个元素,要么它会更大。最终,您要么以这种方式达到数组的末尾,要么达到局部最小值。请注意,这意味着您可以这样做以找到局部最小值。但是,我们实际上不会这样做。相反,我们将使用在数组的这一半中存在局部最小值这一事实作为丢弃一半数组的理由。在剩下的部分中,我们可以保证找到一个局部最小值。

因此,您可以构建以下递归算法:

    如果只有一个数组元素,则为局部最小值。 如果有两个数组元素,请检查每个元素。一个必须是局部最小值。 否则,请查看数组的中间元素。如果是局部最小值,则返回它。否则,至少有一个相邻值必须小于这个值。在包含该较小元素的数组的一半(但不是中间)中递归。

注意这有递归关系

T(1) ≤ 1

T(2) ≤ 1

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

使用主定理,您可以根据需要证明该算法在 O(log n) 时间内运行。

希望这会有所帮助!

还请注意,仅当数组的边小于相邻元素时,该算法才有效。

【讨论】:

@BorisStitnicky- 怎么会这样?在每次迭代中,我们都在做 O(1) 的工作并丢弃一半的数组。你能解释一下为什么这在 O(n) 时间内有效吗? 你写:“在每一步,要么下一个元素比前一个小,要么它会更大。最终,你要么这样打到数组的末尾,要么打到局部最小值”。也就是说,您正在查看下一个元素。如果它不是局部最小值,则必须查看下一次,一次又一次。我看不到你是如何在每次迭代中丢弃一半数组的。 @BorisStitnicky- 详细说明-我的答案的第一部分是关于建立直觉,以便该算法可以工作,而实际算法如下所示。 我已经阅读了你仓促的算法。请考虑反例。有时您是否能够通过此方法获得最小值并不重要。您必须能够始终如一地做到这一点。 @saury 这个算法不会找到 25,但它会找到 1,这也是一个局部最小值。有问题吗?【参考方案2】:

局部最小值的个数可以是n/2;您无法在O(log n) 时间内将它们全部枚举出来。

【讨论】:

我认为问题是你是否能在 O(log n) 时间内找到一个局部最小值,而不是你是否能找到所有的最小值。 这个问题在这一点上不清楚,应该改写恕我直言。【参考方案3】:

使用分而治之的算法。令 m = n/2,并检查值 A[m](即 是,数组中间的元素)。

案例 1:A[m−1]

案例 2:A[m + 1] > A[m]。那么数组的右半部分必须包含一个局部最小值,所以 在右半边递归。这与案例 1 对称。

案例 3:A[m − 1] > A[m] 和 A[m + 1]

【讨论】:

您的案例 2 和 3 的工作情况如何。例如,在情况 3 A[m] > A[m+1] 中,A[m] 如何成为局部最小值。它必须小于两个相邻点。不是吗? 此解决方案从this 链接逐字复制。问题编号 5。【参考方案4】:

你的算法不适用于这个数组

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

这里的局部最小值是 12.. 但是当我检查中间元素,即 7 时,您的算法将丢弃左半部分(具有最小值)并检查右半部分。因此它不起作用

我认为只有当数组具有 A[1] ≥ A[2] 和 A[n - 1] ≤ A[n]。

【讨论】:

【参考方案5】:

原题不完整。

刚刚在以下位置找到了完整的问题和详细的解释 Find local minima in an array! - 不是我的博客

给定一个唯一整数数组,其前两个数字正在减少,最后两个数字正在增加,请在数组中找到一个局部最小值。如果数组中的数字小于其左右数字,则称为局部最小值。

例如在数组 9,7,2,8,5,6,3,4 2 是一个局部最小值,因为它小于其左右数字 7 和 8。同样,5 是另一个局部最小值,因为它介于 8 和 6 之间,都大于 5。

你需要找到任何一个局部最小值。

【讨论】:

【参考方案6】:

这是一个适用于 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;
        
    

【讨论】:

【参考方案7】:

实际上我之前的算法可以修改为在 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;
                   
    


【讨论】:

以上是关于在数组中查找局部最小值的主要内容,如果未能解决你的问题,请参考以下文章

在 MATLAB/Octave 中查找 N 维数组中的所有局部最小值

Java算法 -- 二分查找:查找目标元素最左的位置和最右的位置局部最小值问题求解

Java算法 -- 二分查找:查找目标元素最左的位置和最右的位置局部最小值问题求解

Java算法 -- 二分查找:查找目标元素最左的位置和最右的位置局部最小值问题求解

Java算法 -- 二分查找:查找目标元素最左的位置和最右的位置局部最小值问题求解

局部最小值问题:在一个相邻位置的数不相等的数组中寻找任意一个局部最小值