具有虚拟内存和写入组合的并行基数排序

时间:2015-03-03 16:44:36

标签: algorithm sorting caching radix-sort

我正在尝试实现http://arxiv.org/pdf/1008.2849v2.pdf(算法2)中描述的并行基数排序变体,但我的C ++实现(基数10中的4位数)包含一个我无法找到的错误。

出于调试目的,我没有使用并行性,但代码仍应正确排序。

例如,行arr.at(i) = item在以下

中访问其边界之外的索引
std::vector<int> v = {4612, 4598};
radix_sort2(v);

我的实施如下

#include <set>
#include <array>
#include <vector>

void radix_sort2(std::vector<int>& arr) {
    std::array<std::set<int>, 10> buckets3;

    for (const int item : arr) {
        int d = item / 1000;
        buckets3.at(d).insert(item);
    }

    //Prefix sum
    std::array<int, 10> outputIndices;
    outputIndices.at(0) = 0;
    for (int i = 1; i < 10; ++i) {
        outputIndices.at(i) = outputIndices.at(i - 1) +
            buckets3.at(i - 1).size();
    }

    for (const auto& bucket3 : buckets3) {
        std::array<std::set<int>, 10> buckets0, buckets1;
        std::array<int, 10> histogram2 = {};

        for (const int item : bucket3) {
            int d = item % 10;
            buckets0.at(d).insert(item);
        }
        for (const auto& bucket0 : buckets0) {
            for (const int item : bucket0) {
                int d = (item / 10) % 10;
                buckets1.at(d).insert(item);

                int d2 = (item / 100) % 10;
                ++histogram2.at(d2);
            }
        }

        for (const auto& bucket1 : buckets1) {
            for (const int item : bucket1) {
                int d = (item / 100) % 10;
                int i = outputIndices.at(d) + histogram2.at(d);
                ++histogram2.at(d);
                arr.at(i) = item;
            }
        }
    }
}

有人能发现我的错误吗?

1 个答案:

答案 0 :(得分:1)

我看了你链接的那篇论文。你没有犯过任何错误,我没有看到任何错误。事实上,根据我的估计,你纠正了算法中的错误。

我写出了算法并最终得到了与你完全相同的问题。在复习算法2后,要么我错误地理解它应该如何工作,要么就是有缺陷的。该算法至少存在一些问题,特别是围绕outputIndiceshistogram2

查看算法,项目的最终索引由outputIndices中存储的计数排序确定。 (现在让我们忽略直方图)。 如果你有一个数字的初始数组{0100, 0103, 0102, 0101}那么前缀的总和就是4。 该算法没有表明我可以确定将结果滞后1.可以说,为了使算法以他们想要的方式工作,它确实必须滞后,因此,继续前进。 现在,前缀总和为0, 4, 4...。该算法不使用MSD作为outputIndices数组的索引,它使用“MSD - 1”;因此,将1作为数组的索引,没有直方图的第一项的起始索引是4!在第一次尝试之外的数组。 outputIndices是使用MSD构建的,MSD可以访问它。

此外,即使您正确调整算法以将MSD用于outputIndices,它仍然无法正确排序。使用初始输入(交换){4598, 4612},它们将保持该顺序。它们(本地)排序,就好像它们是2位数字一样。如果将其增加为其他数字不以4开头,则它们将全局排序,但本地排序永远不会完成。 根据该论文,目标是使用直方图来做到这一点,但我没有看到这种情况发生。

最终,我假设,你想要的是一种按照描述的方式工作的算法。我修改了算法,保持了使用MSD进行全局排序的纸张的总体规定目标,以及反向LSD的其余数字。 我认为这些改变不会影响您对并行功能的愿望。

void radix_sort2(std::vector<int>& arr)
{
    std::array<std::vector<int>, 10> buckets3;

    for (const int item : arr)
    {
        int d = item / 1000;
        buckets3.at(d).push_back(item);
    }

    //Prefix sum
    std::array<int, 10> outputIndices;
    outputIndices.at(0) = 0;

    for (int i = 1; i < 10; ++i)
    {
        outputIndices.at(i) = outputIndices.at(i - 1) + buckets3.at(i - 1).size();
    }

    for (const auto& bucket3 : buckets3)
    {       
        if (bucket3.size() <= 0)
            continue;

        std::array<std::vector<int>, 10> buckets0, buckets1, buckets2;

        for (const int item : bucket3)
            buckets0.at(item % 10).push_back(item);

        for (const auto& bucket0 : buckets0)
            for (const int item : bucket0)
                buckets1.at((item / 10) % 10).push_back(item);

        for (const auto& bucket1 : buckets1)
            for (const int item : bucket1)
                buckets2.at((item / 100) % 10).push_back(item);

        int count = 0;

        for (const auto& bucket2 : buckets2)
        {
            for (const int item : bucket2)
            {
                int d = (item / 1000) % 10;
                int i = outputIndices.at(d) + count;
                ++count;
                arr.at(i) = item;
            }
        }
    }
}

对于可扩展性,创建一个执行本地排序的辅助函数可能是有意义的。您应该能够扩展它以处理任意数量的数字。