簡體   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