非标准排序算法,用于随机独特整数

Non-standard sorting algorithm for random unique integers

本文关键字:整数 随机 用于 排序 算法 非标准      更新时间:2023-10-16

我的数组至少为2000个随机唯一整数,每个整数在范围0&lt中;N<65000。

我必须对其进行排序,然后在数组中获取随机值的索引。这些操作中的每一个都必须尽可能快。搜索二进制搜索似乎很好。

用于排序,我使用了标准快速排序算法(QSORT),但是有人告诉我,使用给定的信息,标准排序算法将不是最有效的。因此,问题很简单 - 使用给定的信息将最有效的方法对数组进行分类的最有效方法是什么?完全被这个困惑。

我不知道为什么告诉你那个人会如此神秘的人,但实际上 qsort并不是最有效的在C 中对整数(或任何内容)进行排序的方法。改用std::sort

可能的,您可以改进所述特殊情况的实现的std::sort(2000个不同的随机整数在0-65K中),但是您不太可能做得更好,而且它不太可能做得多几乎肯定不会付出努力。我能想到的事情可能会有所帮助:

  • 使用QuickSort,但使用不同的枢轴选择或不同的阈值以切换到从sort的实现中插入排序。这基本上是修补。

  • 使用某种并行的类型。2000个元素是如此之小,以至于我可疑创建其他线程的时间会立即杀死任何提高性能的希望。但是,如果您做很多事情,那么您可以平均在所有这些线程中创建线程的成本,并且只担心线程同步的开销而不是线程创建。

也就是说,如果您生成和排序数组,则只需查找其中的一个值,然后生成一个新数组,您将每次对整个数组进行排序,从而浪费努力。您只需在数组中运行,计算小于目标值的值的数量:此计数是它的索引。使用std::count_if或短循环。

这些操作中的每一个都必须尽可能快。

这不是合法的软件工程标准。几乎可以通过足够数月或数年的工程努力使任何事情都变得更快地缩小 - 从来没有什么复杂的"尽可能快",即使您也无法证明它不能更快,并且即使您可以在某个地方有新的硬件,或者很快就会发明最快的解决方案。除非您打算在这项任务上度过一生并最终失败,否则要实现更现实的目标; - )

用于对均匀分布的随机整数排序排序通常是最快的算法,它的速度比QuickSort的速度更快。但是,很难找到对此的优化实现,快速排序更加无处不在。radix排序和快速排序都可能具有非常糟糕的情况性能,例如O(n^2),因此,如果最糟糕的情况表现很重要,则必须查看其他地方,也许您选择Introsort,这类似于STD :: sort in c in c 。

对于阵列查找哈希表是迄今为止禁食的方法。如果您不希望其他数据结构,则可以随时选择二进制搜索。如果您具有统一分布的数字插值搜索可能是最有效的方法(最佳平均性能)。

QuickSort的复杂性是O(n*log(n)),其中n = 2000在您的情况下。log(2000) = 10.965784

您可以使用以下算法之一在O(n)中排序:

  • 计数排序
  • radix排序
  • 存储桶排序

我已经将std::sort()N = 100000000的计数排序进行了比较:

#include <iostream>
#include <vector>
#include <algorithm>
#include <time.h>
#include <string.h>
using namespace std;
void countSort(int t[], int o[], int c[], int n, int k) 
{
    // Count the number of each number in t[] and place that value into c[].
    for (int i = 0; i < n; i++)
        c[t[i]]++;
    // Place the number of elements less than each value at i into c[].  
    for (int i = 1; i <= k; i++)
        c[i] += c[i - 1];
    // Place each element of t[] into its correct sorted position in the output o[].
    for (int i = n - 1; i >= 0; i--) 
    {
        o[c[t[i]] - 1] = t[i];
        --c[t[i]];
    }
}
void init(int t[], int n, int max)
{
    for (int i = 0; i < n; i++)
        t[i] = rand() % max;
}
double getSeconds(clock_t start)
{
    return (double) (clock() - start) / CLOCKS_PER_SEC;
}
void print(int t[], int n)
{
    for (int i = 0; i < n; i++)
        cout << t[i] << " ";
    cout << endl;
}
int main()
{
    const int N = 100000000;
    const int MAX = 65000;
    int *t = new int[N];
    init(t, N, MAX);
    //print(t, N);
    clock_t start = clock();
    sort(t, t + N);  
    cout << "std::sort " << getSeconds(start) << endl;
    //print(t, N);
    init(t, N, MAX);
    //print(t, N);
    // o[] holds the sorted output.
    int *o = new int[N];
    // c[] holds counters.
    int *c = new int[MAX + 1];
    // Set counters to zero.
    memset(c, 0, (MAX + 1) * sizeof(*c));
    start = clock();
    countSort(t, o, c, N, MAX);
    cout << "countSort " << getSeconds(start) << endl;
    //print(o, N);
    delete[] t;
    delete[] o;
    delete[] c;
    return 0;
}

结果(以秒为单位):

std::sort 28.6
countSort 10.97

N = 2000这两种算法都给出了0时间。

标准排序算法以及标准几乎所有内容,都是很好的通用目的解决方案。如果您对数据一无所知,如果它真正由"随机唯一整数"组成,那么您不妨使用标准实现之一。

另一方面,大多数编程问题都出现在讲述数据的上下文中,而其他信息通常会导致更有效的特定于问题的解决方案。

例如,您的数据是否一次或块出现?如果它零碎,则可以通过将增量分类(例如Dual-Pivot QuickSort)与数据获取相结合来加快速度。

由于您的数字域是如此之小,因此您可以创建65000个条目的数组,将您看到的数字的索引设置为一个,然后收集所有设置为一个作为您的排序阵列。这将完全是67000(假设数组的初始化无需成本)。

由于列表包含2000个条目,因此O(n*log(n))可能会更快。我没有想到其他O(n)算法,所以我想您最好使用通用算法。