优化一个函数,找到最小和最大元素

Optimization of a function to find min and max element

本文关键字:元素 函数 一个 优化      更新时间:2023-10-16

我有以下代码,但我需要优化它以获得更好的运行时间。这可能吗?这里minLR从左到右取出最小元素
minRL other从右到左。我不能使用标准库函数,因为我也必须在反向迭代。如果' I '是奇数,我必须将数组的最小元素从'左'增加到'右',如果' I '是偶数,我必须将数组的最小元素从'r'增加到'l '。M相当大。

int minLR(int *arr,int s){
    int ind=0;
    int min=arr[0];
    for(int i=1;i<s;i++){
        if(min>arr[i]){min=arr[i];ind=i;}
    }
    arr[ind]++;
    return ind;
}
int minRL(int *arr,int s){
    int ind=s-1;
    int min=arr[s-1];
    for(int i=s-2;i>=0;i--){
        if(min>arr[i]){ min=arr[i];ind=i;}
    }
    arr[ind]++;
    return ind;
}
for(int i=1;i<=m;i++){
    if(i%2==0){
        ind=minRL(arr,n);    
    }
    if(i%2!=0){
        ind=minLR(arr,n);
    }
}

求最小值或最大值或同时求最小值和最大值的复杂度总是O(n)。如果数组必须保持未排序,则无法改进这一点。

使用原始方法

通过巧妙地同时搜索最小值和最大值,使用本文中解释的稍微改进的算法,最终可以略微提高性能。

然后在每次最小/最大搜索中省去n/2-2比较。但这不会改变每次搜索O(n)的数量级。所以总的来说你的算法会保持在O(n²)

根据您的方法选择

另一种方法是使用影子排序数据结构,指向未排序表中的元素。下面是基于标准向量和算法的实现:

vector<int*> myshadow(n);  // sorted shadow structure 
transform(arr, arr+n, myshadow.begin(), [](int& x)->int* { return &x; });
for(int i = 0; i < n; i++){
    sort(myshadow.begin(), myshadow.end(), [](int* a, int*b)->bool { return *a < *b; }); 
    // now myshadow[0] holds the smallest element:  
    if(i % 2 == 0)
        minRLi(myshadow, n);
    else minLRi(myshadow, n);
}

结果与原始算法相同,使用以下函数以优化的方式复制左搜索和右搜索(循环只迭代几个最小的元素):

int* minLRi(vector<int *>arr, int s){
    int ind = 0;
    int* min = arr[0]; // min shall be the smallest element address of the same minimal value (leftmost)
    for(int i = 1; i<s && *arr[0]==*arr[i]; i++){
        if(min>arr[i]){ min = arr[i]; ind = i; }
    }
    (*arr[ind])++;
    return arr[ind]; 
}
int* minRLi(vector<int *>arr, int s){
    int ind = 0;
    int* min = arr[0]; // min shall be the largest element address of the same minimal value (rightmost)
    for(int i = 1; i<s && *arr[0] == *arr[i]; i++){
        if(min<arr[i]){ min = arr[i]; ind = i; }
    }
    (*arr[ind])++;
    return arr[ind];
}

进一步优化

现在我假设std::sort()在对一个几乎排序的向量重新排序时是非常有效的。实验表明,至少在我的MSVC2013上,这根本不是真的。性能结果是灾难性的。

所以我稍微修改了一下代码来优化诉诸。

优化后的代码可以在这里在线查看和试用

结论

我没有时间深入分析,但我认为复杂度应该在O(nlog(n))左右。

20万个随机元素的实际实验(MSVC 2013, release, i7处理器)显示:

  • 你最初的方法花了30秒(134.5百万次迭代!!)
  • 阴影选择耗时32毫秒(631千次迭代)

一个更有效的解决方案(假设n非常大)是首先将数据转录到std::set的std::map中,然后再处理它:

映射的键应该是在数组中出现一次或多次的值。映射的有效载荷将是该值在数组中出现的所有位置的集合。这是很容易和相对有效的构建。

然后很容易找到与方向无关的最小值(映射中的第一个元素),并从该最小集中取出左或右元素,并将其移动到下一个更高的最小集。

如果使用优先级队列而不是map,编程的复杂性会高一些,效率会好一些。

据我所知,标准优先级队列不能让您轻松访问第二个优先级元素(访问它,修改它,可能插入新的第二个优先级,而保持第一个优先级不变)。我通常发明我自己的优先级队列来获得像这样的额外功能,所以我不确定标准队列是否可以推到那么远,或者您是否需要自己发明。