[英]The fastest and most efficient way to find the number of distinct elements of a 1D array
[英]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.