C++ 提出了一种对大尺寸物体数组进行排序的算法;有人能告诉我这个算法叫什么吗?(在谷歌上找不到)

C++ 提出了一种对大尺寸物体数组进行排序的算法;有人能告诉我这个算法叫什么吗?(在谷歌上找不到),c++,arrays,algorithm,sorting,C++,Arrays,Algorithm,Sorting,我需要对一系列大尺寸对象进行排序,这让我思考:有没有办法将交换数量降至最低 因此,我使用快速排序(但任何其他快速排序在这里也应该起作用)对数组中元素的索引进行排序;指数互换成本低。然后我使用这些索引将实际对象交换到它们的位置。不幸的是,这使用了O(n)个额外的空间来存储索引。下面的代码演示了该算法(我称之为IndexSort),在我的测试中,对于大型对象数组,它似乎比普通的快速排序更快 template <class Itr> void IndexSort(Itr begin, It

我需要对一系列大尺寸对象进行排序,这让我思考:有没有办法将交换数量降至最低

因此,我使用快速排序(但任何其他快速排序在这里也应该起作用)对数组中元素的索引进行排序;指数互换成本低。然后我使用这些索引将实际对象交换到它们的位置。不幸的是,这使用了O(n)个额外的空间来存储索引。下面的代码演示了该算法(我称之为IndexSort),在我的测试中,对于大型对象数组,它似乎比普通的快速排序更快

template <class Itr>
void IndexSort(Itr begin, Itr end)
{
    const size_t count = end - begin;

    // Create indices
    vector<size_t> ind(count);
    iota(ind.begin(), ind.end(), 0);

    // Sort indices
    sort(ind.begin(), ind.end(), [&begin] (const size_t i, const size_t j)
    {
        return begin[i] < begin[j];
    });

    // Create indices to indices. This provides
    // constant time search in the next step.
    vector<size_t> ind2(count);
    for(size_t i = 0; i < count; ++i)
        ind2[ind[i]] = i;

    // Swap the objects into their final places
    for(size_t i = 0; i < count; ++i)
    {
        if( ind[i] == i )
            continue;

        swap(begin[i], begin[ind[i]]);

        const size_t j = ind[i];

        swap(ind[i], ind[ind2[i]]);
        swap(ind2[i], ind2[j]);
    }
}
模板
无效索引排序(Itr开始,Itr结束)
{
常量大小\u t计数=结束-开始;
//创建索引
向量ind(计数);
物联网(ind.begin()、ind.end()、0);
//排序索引
排序(ind.begin(),ind.end(),[&begin](常量大小i,常量大小j)
{
返回开始[i]
现在我已经测量了quicksort和IndexSort所做的交换(大型对象的交换),发现quicksort所做的交换数量要多得多。所以我知道为什么IndexSort可以更快

但是,有更具学术背景的人能解释一下这个算法实际工作的原因/方式吗?(这对我来说不是直觉,尽管我不知怎么想到了)

谢谢

编辑:以下代码用于验证IndexSort的结果

// A class whose objects will be large
struct A
{
    int id;
    char data[1024];

    // Use the id to compare less than ordering (for simplicity)
    bool operator < (const A &other) const
    {
        return id < other.id;
    }

    // Copy assign all data from another object
    void operator = (const A &other)
    {
        memcpy(this, &other, sizeof(A));
    }
};

int main()
{
    const size_t arrSize = 1000000;

    // Create an array of objects to be sorted
    vector<A> randArray(arrSize);
    for( auto &item: randArray )
        item.id = rand();

    // arr1 will be sorted using quicksort
    vector<A> arr1(arrSize);
    copy(randArray.begin(), randArray.end(), arr1.begin());

    // arr2 will be sorted using IndexSort
    vector<A> arr2(arrSize);
    copy(randArray.begin(), randArray.end(), arr2.begin());

    {
        // Measure time for this
        sort(arr1.begin(), arr1.end());
    }

    {
        // Measure time for this
        IndexSort(arr2.begin(), arr2.end());
    }

    // Check if IndexSort yielded the same result as quicksort
    if( memcmp(arr1.data(), arr2.data(), sizeof(A) * arr1.size()) != 0 )
        cout << "sort failed" << endl;

    return 0;
}
//对象较大的类
结构A
{
int-id;
字符数据[1024];
//使用id比较小于排序(为简单起见)
布尔运算符<(常数A和其他)常数
{
返回id
例如,流行的递归快速排序算法在具有足够RAM的情况下提供了相当合理的性能,但由于它以递归方式复制阵列的一部分,因此当阵列不适合RAM时,它就变得不太实用,因为它可能会导致大量缓慢的磁盘复制或移动操作该算法可能更可取,即使它需要更多的总体比较

解决这个问题的一种方法是在数组中创建一个索引,然后对索引进行排序,而不是对整个数组进行排序,这种方法在复杂记录(如关系数据库中)按相对较小的键字段排序时效果很好。(然后,可以通过一次传递生成整个数组的排序版本,从索引中读取,但这通常是不必要的,因为具有排序索引就足够了。)因为索引比整个阵列小得多,所以它可以很容易地放入整个阵列无法放入的内存中,从而有效地消除了磁盘交换问题。此过程有时称为“标记排序”

如上所述,标记排序可用于对无法放入内存的大型数据数组进行排序。但是,即使可以放入内存,对于大型对象数组仍需要较少的内存读写操作,如您的解决方案所示,因为每次都不会复制整个对象

实现细节:虽然您的实现仅对索引进行排序,并在进行比较时通过索引引用原始对象数组,但另一种实现方法是将索引/排序键对存储在排序缓冲区中,使用排序键进行比较。这意味着您可以在不使用整个arr的情况下进行排序一次在内存中创建一组对象

标记排序的一个示例是.NET中的排序算法:

排序有点灵活,因为它允许您提供比较委托。但是,它不允许您提供交换委托。在许多情况下,这是可以的。但是,如果您对大型结构(值类型)进行排序,或者如果您希望进行间接排序(通常称为标记排序),交换委托是一件非常有用的事情。例如,LINQ到对象排序算法在内部使用标记排序。您可以通过检查源来验证这一点,该源在.NET引用源中可用。允许您传递交换委托将使事情更加灵活


与其说是一种算法,不如说是一种间接寻址

对较大对象进行较少交换的原因是,您有已排序的索引(最终结果,意味着没有多余的中间交换)。如果您除了对象交换之外还计算了索引交换的数量,那么您将通过索引排序获得更多的交换总数

尽管如此,你不一定总是被算法的复杂性所束缚。花费昂贵的排序时间交换廉价的小索引节省的时间比成本要多

因此,使用索引排序的总交换次数较多,但大部分交换次数较便宜,而且对原始对象进行的昂贵交换次数要少得多

它之所以更快,是因为原始对象比索引大,但可能不适合移动构造函数(不一定存储动态分配的数据)

在这个水平上,成本