簡體   English   中英

檢查數組是否已排序的最快方法

[英]Fastest way to check if an array is sorted

考慮到有一個從非常大的函數返回的數組。

測試數組是否已排序的fastest方法是什么?

一個最簡單的方法是:

/// <summary>
/// Determines if int array is sorted from 0 -> Max
/// </summary>
public static bool IsSorted(int[] arr)
{
for (int i = 1; i < arr.Length; i++)
{
    if (arr[i - 1] > arr[i])
    {
    return false;
    }
}
return true;
}

您必須訪問數組的每個元素以查看是否有未排序的內容。

你的 O(n) 方法是盡可能快的,沒有任何關於數組可能狀態的特殊知識。

您的代碼專門測試數組是否在較低的索引處使用較小的值進行排序。 如果這不是你想要的,你的if會變得稍微復雜一些。 您的代碼注釋確實表明這就是您所追求的。

如果您對可能的狀態有特殊的了解(例如,您知道它通常已排序但新數據可能會添加到末尾),您可以優化訪問數組元素的順序,以便在出現以下情況時測試更快地失敗數組未排序。

您可以利用硬件體系結構的知識通過對陣列進行分區來並行檢查陣列的多個部分,首先比較分區的邊界(快速檢查失敗),然后在單獨的線程上為每個內核運行一個陣列分區(不超過每個 CPU 內核 1 個線程)。 但請注意,如果數組分區遠小於緩存行的大小,線程將傾向於相互競爭以訪問包含該數組的內存。 多線程只會對相當大的數組非常有效。

更快的方法,平台目標:任何 CPU,首選 32 位。
一個包含 512 個元素的排序數組:快 25%。

static bool isSorted(int[] a)
{
    int j = a.Length - 1;
    if (j < 1) return true;
    int ai = a[0], i = 1;
    while (i <= j && ai <= (ai = a[i])) i++;
    return i > j;
}

目標:x64,相同的陣列:快 40%。

static bool isSorted(int[] a)
{
    int i = a.Length - 1;
    if (i <= 0) return true;
    if ((i & 1) > 0) { if (a[i] < a[i - 1]) return false; i--; }
    for (int ai = a[i]; i > 0; i -= 2)
        if (ai < (ai = a[i - 1]) || ai < (ai = a[i - 2])) return false;
    return a[0] <= a[1];
}

忘記了,比我的第一個代碼塊慢一點。

static bool isSorted(int[] a)
{
    int i = a.Length - 1; if (i < 1) return true;
    int ai = a[i--]; while (i >= 0 && ai >= (ai = a[i])) i--;
    return i < 0;
}

測量它(見灰胡子的評論)。

using System;                                  //  ????????? DEBUG ?????????
using sw = System.Diagnostics.Stopwatch;       //  static bool abc()    
class Program                                  //  {   // a <= b <= c ?  
{                                              //      int a=4,b=7,c=9;  
    static void Main()                         //      int i = 1;  
    {                                          //      if (a <= (a = b))  
        //abc();                               //      {  
        int i = 512;                           //          i++;  
        int[] a = new int[i--];                //          if (a <= (a = c))
        while (i > 0) a[i] = i--;              //          {    
        sw sw = sw.StartNew();                 //              i++;  
        for (i = 10000000; i > 0; i--)         //          }  
            isSorted(a);                       //      }  
        sw.Stop();                             //      return i > 2;  
        Console.Write(sw.ElapsedMilliseconds); //  }  
        Console.Read();                        //  static bool ABC();
    }                                          //  {
                                               //      int[]a={4,7,9};    
    static bool isSorted(int[] a) // OP Cannon //      int i=1,j=2,ai=a[0]; 
    {                                          //  L0: if(i<=j)    
        for (int i = 1; i < a.Length; i++)     //        if(ai<=(ai=a[i]))  
            if (a[i - 1] > a[i]) return false; //          {i++;goto L0;}  
        return true;                           //      return i > j;  
    }                                          //  }  
}

目標:x64。 四核/線程。 具有 100,000 個元素的排序數組:~55%。

static readonly object _locker = new object();
static bool isSorted(int[] a)  // a.Length > 3
{
    bool b = true;
    Parallel.For(0, 4, k =>
    {
        int i = 0, j = a.Length, ai = 0;
        if (k == 0) { j /= 4; ai = a[0]; }                        // 0 1
        if (k == 1) { j /= 2; i = j / 2; ai = a[i]; }             // 1 2
        if (k == 2) { i = j - 1; ai = a[i]; j = j / 2 + j / 4; }  // 4 3
        if (k == 3) { i = j - j / 4; ai = a[i]; j = j / 2; }      // 3 2
        if (k < 2)
            while (b && i <= j)
            {
                if (ai <= (ai = a[i + 1]) && ai <= (ai = a[i + 2])) i += 2;
                else lock (_locker) b = false;
            }
        else
            while (b && i >= j)
            {
                if (ai >= (ai = a[i - 1]) && ai >= (ai = a[i - 2])) i -= 2;
                else lock (_locker) b = false;
            }
    });
    return b;
}

1,000,000 件物品?

if (k < 2)
    while (b && i < j)
        if (ai <= (ai = a[i + 1]) && ai <= (ai = a[i + 2]) &&
            ai <= (ai = a[i + 3]) && ai <= (ai = a[i + 4])) i += 4;
        else lock (_locker) b = false;
else
    while (b && i > j)
        if (ai >= (ai = a[i - 1]) && ai >= (ai = a[i - 2]) &&
            ai >= (ai = a[i - 3]) && ai >= (ai = a[i - 4])) i -= 4;
        else lock (_locker) b = false;

讓我們忘記百分比。
原始:0.77 ns/項,現在:0.22 ns/項。
2,000,000 件物品? 四核:快 4 倍。

林克解決方案。

public static bool IsSorted<T>(IEnumerable<T> list) where T:IComparable<T>
{
    var y = list.First();
    return list.Skip(1).All(x =>
    {
        bool b = y.CompareTo(x) < 0;
        y = x;
        return b;
    });
}

這是我的 IsSorted 函數版本

public static bool IsSorted(int[] arr)
{               
    int last = arr.Length - 1;
    if (last < 1) return true;

    int i = 0;

    while(i < last && arr[i] <= arr[i + 1])
        i++;

    return i == last;
}

雖然這個函數比問題中的要快一點,但它的分配和比較比迄今為止發布的任何東西都要少。 在最壞的情況下,它會進行 2n+1 次比較。 如果您可以對數據的性質做出合理的假設,例如最小數據大小或數組包含偶數個元素,它仍然可以改進。

我能想到的唯一改進是同時檢查數組的兩端,這個小小的改變將在半場時間完成......

public static bool IsSorted(int[] arr)
{
int l = arr.Length;
for (int i = 1; i < l/2 + 1 ; i++)
{
    if (arr[i - 1] > arr[i] || arr[l-i] < arr[l-i-1])
    {
    return false;
    }
}
return true;
}

這就是我想出來的,發現效果更好,尤其是對於更大尺寸的數組。 該函數是遞歸的,將在第一次被調用,比如在這樣的 while 循環中

while( isSorted( yourArray, 0 )

if 語句檢查是否已達到數組的邊界。

else if 語句將遞歸調用自身並在條件變為假時隨時中斷

 public static bool IsSorted(int[] arr, int index)
    {
        if (index >= arr.Length - 1)
        {
            return true;
        }
        else if ((arr[index] <= arr[ index + 1]) && IsSorted(arr, index + 1))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

如果順序無關緊要(降序或升序)。

private bool IsSorted<T>(T[] values) where T:IComparable<T>
{
    if (values == null || values.Length == 0) return true;

    int sortOrder = 0;

    for (int i = 0; i < values.Length - 1; i++)
    {
        int newSortOrder = values[i].CompareTo(values[i + 1]);

        if (sortOrder == 0) sortOrder = newSortOrder;

        if (newSortOrder != 0 && sortOrder != newSortOrder) return false;
    }

    return true;
}

我想到的問題是“為什么”?

是為了避免重新排序已經排序的列表嗎? 如果是,只需使用 Timsort(Python 和 Java 中的標准)。 它非常擅長利用已經排序或幾乎排序的數組/列表。 盡管 Timsort 在這方面做得很好,但最好不要在循環內排序。

另一種選擇是使用先天排序的數據結構,如收獲樹、紅黑樹或 AVL 樹。 這些是在循環內排序的不錯選擇。

這可能不是最快的,但它是完整的解決方案。 索引低於 i 的每個值都根據i處的當前值進行檢查。 這是用 php 編寫的,但可以很容易地翻譯成 c# 或 javascript

for ($i = 1; $i < $tot; $i++) {
        for ($j = 0; $j <= $i; $j++) {
            //Check all previous values with indexes lower than $i
            if ($chekASCSort[$i - $j] > $chekASCSort[$i]) {
                return false;
            }
        }
    }

暫無
暫無

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

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