簡體   English   中英

在 C 中的數組中找到 N 個最大整數的最有效(最快)方法是什么?

[英]What is the most efficient (fastest) way to find an N number of the largest integers in an array in C?

讓我們有一個大小為 8 的數組讓我們讓 N 為 3

使用數組:1 3 2 17 19 23 0 2

我們的輸出應該是:23, 19, 17

說明:數組中最大的三個數字,按降序排列。

我試過這個:

int array[8];
int largest[N] = {0, 0, 0};

for (int i = 1; i < N; i++) {
    for (int j = 0; j < SIZE_OF_ARRAY; j++) {
        if (largest[i] > array[j]) {
            largest[i] = array[j];
            array[j] = 0;
        }
    }
}

此外,讓約束如下:

數組中的整數應該是 0 <= i <= 1 000

N 應該是 1 <= N <= SIZE_OF_ARRAY - 1

SIZE_OF_ARRAY 應該是 2 <= SIZE_OF_ARRAY <= 1 000 000

我的實現方式非常低效,因為它會擦洗整個數組 N 次。 對於大型陣列,這可能需要幾分鍾才能完成。

在 C 中實現它的最快和最有效的方法是什么?

您應該查看直方圖算法。 由於值必須介於 0 和 1000 之間,因此您只需為每個值分配一個數組:

#define MAX_VALUE 1000
int occurrences[MAX_VALUE+1];
int largest[N];
int i, j;

for (i=0; i<N; i++)
    largest[N] = -1;

for (i=0; i<=MAX_VALUE; i++)
    occurrences[i] = 0;

for (i=0; i<SIZE_OF_ARRAY; i++)
    occurrences[array[i]]++;

// Step through the occurrences array backward to find the N largest values.
for (i=MAX_VALUE, j=0, i; i>=0 && j<N; i--)
    if (occurrences[i] > 0)
        largest[j++] = i;

請注意,對於每個唯一值,這只會產生一個largest元素。 如果您希望所有匹配項都出現在largest . 因此,如果沒有足夠的唯一大數來填充largest數組,則某些元素的值可能為 -1。 最后,將largest的結果從大到小排序。 如果您願意,這將很容易解決:只需從右到左填充largest數組。

我能想到的一種方法是對數組進行排序並返回前 N 個數字。 由於數組已排序,因此我們返回的 N 數將是數組的 N 個最大數。 此方法將花費O(nlogn)的時間復雜度,其中n是我們在給定數組中擁有的元素數。 我認為這可能是您在解決此問題時可以獲得的非常好的時間復雜度。

另一種具有類似時間復雜度的方法是使用max-heap 從給定的數組中形成 max-heap 並連續N次,使用pop() (或extract 或您稱之為的任何名稱)來獲取最頂部的元素,這將是每次pop之后堆中剩余的最大元素。

這種方法的時間復雜度可以被認為比第一種方法更好 - O(n + Nlogn) ,其中n是數組中的元素數,N 是要找到的最大元素數。 這里,需要O(n)來構建堆並彈出最頂部的元素,我們需要O(logn)次 N 次,總和為 - O(n + Nlogn) ,略好於O(nlogn)

最快的方法是識別數據不只是出現(它要么在編譯時存在;要么通過 IO 到達——來自文件、網絡等); 因此,您可以在創建數據時找到 3 個最高值(在編譯時;或者在解析和完整性檢查然后存儲 IO 接收的數據時 - 從文件、網絡等)。 這可能是最快的方法(因為您要么在運行時什么都不做,要么避免第二次查看所有數據的需要)。

然而; 在這種情況下,如果數據在創建后被修改,那么您需要在修改數據的同時更新“3 個最高值”; 如果較低的值被較高的值替換(您只需檢查新值是否成為 3 個最高值之一),這很容易,但涉及搜索“以前最高”的值是否被替換為較低的值。

如果您需要搜索; 那么它可以用一個循環來完成,比如:

    firstHighest = INT_MIN;
    secondHighest = INT_MIN;
    thirdHighest = INT_MIN;

    for (int i = 1; i < N; i++) {
        if(array[i] > thirdHighest) {
            if(array[i] > secondHighest) {
                if(array[i] > firstHighest) {
                    thirdHighest = secondHighest;
                    secondHighest = firstHighest;
                    firstHighest = array[i];
                } else {
                    thirdHighest = secondHighest;
                    secondHighest = array[i];
                }
            } else {
                thirdHighest = array[i];
            }
        }
    }

注意:確切的代碼將取決於您想對重復項做什么(您可能需要將if(array[j] > secondHighest) {替換為if(array[j] >= secondHighest) {if(array[j] > firstHighest) { with if(array[j] >= firstHighest) {如果你想讓數字 1, 2, 3, 4, 4, 4, 4 給出答案 4, 4, 4 而不是 2, 3, 4 )。

對於大量數據,可以使用 SIMD 和/或多線程進行加速。 例如; 如果 SIMD 可以執行“8 個整數的捆綁”並且您有 4 個 CPU(和 4 個線程); 然后你可以把它分成幾個季度,然后將每個季度視為 8 個元素的列; 在每個季度的每一列中找到最高的 3 個值; 然后從“每個季度每列中的最高 3 個值”中確定最高的 3 個值。 在這種情況下,您可能希望在數組末尾添加填充(設置為INT_MIN虛擬值),以確保數組的總大小是 SIMD 寬度和 CPU 數量的倍數。

對於少量數據,設置 SIMD 和/或協調多個線程的額外開銷將比節省的成本更高; 並且“簡單循環”版本可能會盡可能快。

對於未知/可變數量的數據,您可以提供多種替代方案(簡單循環、單線程 SIMD 和具有可變線程數的 SIMD)並決定在運行時使用哪種方法(以及使用多少線程)數據量。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM