繁体   English   中英

排序优化

[英]Sorting Optimization

我目前正在学习算法课程,因此决定实施一些排序算法并进行比较是一种很好的做法。 我实现了归类排序和快速排序,然后比较了它们的运行时间以及std :: sort:我的计算机不是最快的,但经过200次尝试,平均得到了1000000个元素:

  1. std :: sort-> 0.620342秒
  2. quickSort-> 2.2692
  3. mergeSort-> 2.19048

我想问一下关于如何改进和优化代码实现的意见。

void quickSort(std::vector<int>& nums, int s, int e, std::function<bool(int,int)> comparator = defaultComparator){
if(s >= e)
    return;

int pivot;
int a = s + (rand() % (e-s));
int b = s + (rand() % (e-s));
int c = s + (rand() % (e-s));

//find median of the 3 random pivots
int min = std::min(std::min(nums[a],nums[b]),nums[c]);
int max = std::max(std::max(nums[a],nums[b]),nums[c]);
if(nums[a] < max && nums[a] > min)
    pivot = a;
else if(nums[b] < max && nums[b] > min)
    pivot = b;
else
    pivot = c;

int temp = nums[s];
nums[s] = nums[pivot];
nums[pivot] = temp;

//partition
int i = s + 1, j = s + 1;
for(; j < e; j++){
    if(comparator(nums[j] , nums[s])){
        temp = nums[i];
        nums[i++] = nums[j];
        nums[j] = temp;
    }
}
temp = nums[i-1];
nums[i-1] = nums[s];
nums[s] = temp;

//sort left and right of partition
quickSort(nums,s,i-1,comparator);
quickSort(nums,i,e,comparator);

s是第一个元素的索引,e是最后一个元素之后的索引。 defaultComparator只是以下lambda函数:

自动defaultComparator = [](int a,int b){返回a <= b; };

std::vector<int> mergeSort(std::vector<int>& nums, int s, int e, std::function<bool(int,int)> comparator = defaultComparator){
std::vector<int> sorted(e-s);
if(s == e)
    return sorted;
int mid = (s+e)/2;
if(s == mid){
    sorted[0] = nums[s];
    return sorted;
}
std::vector<int> left = mergeSort(nums, s, mid);
std::vector<int> right = mergeSort(nums, mid, e);

unsigned int i = 0, j = 0;
unsigned int c = 0;
while(i < left.size() || j < right.size()){
    if(i == left.size()){
        sorted[c++] = right[j++];
    }
    else if(j == right.size()){
        sorted[c++] = left[i++];
    }
    else{
        if(comparator(left[i],right[j]))
            sorted[c++] = left[i++];
        else
            sorted[c++] = right[j++];
    }
}
return sorted;

谢谢你们

我首先看到的是,您正在传递一个std::function<> ,它涉及一个虚拟调用,这是最昂贵的调用策略之一。 尝试使用仅模板T(可能是函数)进行尝试-结果将是直接函数调用。

第二件事,在优化时以及在存在就地变量时,切勿执行此result-in-local-container( vector<int> sorted; )。 做就地排序。 客户应意识到您在短接其向量; 如果愿意,可以提前进行复印。 您使用非常量引用是有原因的。 [1]

第三,与rand()有关的成本远非微不足道。 除非您确定需要quicksort()的随机变体(及其对“没有太糟糕的序列”的好处),否则仅将第一个元素用作枢轴。 还是中间。

使用std::swap()交换两个元素。 很有可能,它被转换为xchg(在x86 / x64上)或同等版本,很难被击败。 可以从程序集输出中验证优化器是否确定您打算在这些位置进行交换而没有明确显示。

您发现三个元素的中位数的方式充满了条件移动/分支。 它只是nums[a] + nums[b] + nums[c] - max - min ; 但是同时获得nums[...]minmax也可以进一步优化。

追求速度时避免使用i++ 虽然大多数优化器通常会创建良好的代码,但它不太理想的可能性很小。 优化时要明确(交换后为++i ),但要_only_when_optimizing_。

但是最重​​要的一个:valgrind / callgrind / kcachegrind。 个人资料,个人资料,个人资料。 只优化真正慢的东西。

[1]此规则有一个例外:从非const容器构建的const容器。 这些通常是内部类型,并且在多个线程之间共享,因此最好在需要修改时将它们保留为const&copy。 在这种情况下,您将在函数中分配一个新容器(无论是否为const),但为了用户方便使用API​​,可能会保留const一个。

为了快速排序,请使用类似于分区方案的Hoare。

http://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme

中位数3仅需要3 if / swap语句(实际上是冒泡排序)。 不需要最小或最大检查。

    if(nums[a] > nums[b])
        std::swap(nums[a], nums[b]);
    if(nums[b] > nums[c])
        std::swap(nums[b], nums[c]);
    if(nums[a] > nums[b])
        std::swap(nums[a], nums[b]);
    // use nums[b] as pivot value

对于合并排序,请使用一次创建工作向量的入口函数,然后将该向量通过引用传递给实际的合并排序函数。 对于自上而下的合并排序,索引确定每个子向量的开始,中间和结尾。

如果使用自上而下的合并排序,则代码可以根据递归级别通过改变合并方向来避免复制数据。 这可以使用两个相互递归的函数来完成,第一个函数的结果以原始向量结尾,第二个函数的结果以工作向量结尾。 第一个调用第二个,两次,然后从工作向量合并回到原始向量,反之亦然。 对于第二个,如果size == 1,则需要将1个元素从原始向量复制到工作向量。 两个函数的替代方法是传递一个布尔值以合并哪个方向。

如果使用自下而上的合并排序(速度会更快一些),则每次传递都会交换向量。 所需的通过次数是预先确定的,在奇数次通过的情况下,第一个通过会交换到位,以便在完成所有合并通过之后,数据最终以原始向量结尾。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM