使用sort函数和递归比较器

时间:2016-03-20 17:38:13

标签: c++ sorting recursion boost boost-multi-array

我在c ++中处理大数据和程序。例如,我需要创建大小为[7 x 128 ^ 3 x 5 x 5]等的4维数组。我将不得不创建更多的数组作为不同属性的中间数据结构。在研究了很多之后,我最终选择了boost的multi_array来实现我的数据结构。我选择multi_array有两个原因: (1)它处理异常,例如数组索引越界,这对我来说非常重要。 (2)它处理更高维度的数组(其他选项,如stl的多维数组,对3维及更高维数的数组有很多问题)

问题示例。

如果我用一个例子解释问题就变得容易了。说,我有以下3-D阵列。

[(1,2,3), (4,5,6), (7,8,9)] 
[(1,2,3), (1,3,3), (4,1,1)]
[(8,9,9), (6,9,7), (17,2,3)]
[(1,2,3), (1,3,2), (2,8,1)]
[(1,2,3), (1,3,3), (4,2,1)]

我想对这些行进行排序,以便排序必须基于column_1,然后是column_2,然后是column_3。排序后我会有

[(1,2,3), (1,3,2), (2,8,1)]
[(1,2,3), (1,3,3), (4,1,1)]
[(1,2,3), (1,3,3), (4,2,1)]
[(1,2,3), (4,5,6), (7,8,9)]
[(8,9,9), (6,9,7), (17,2,3)]

您会看到column_1已排序。对于具有相同column_1的每两行,则对column_2进行排序,依此类推。

我尝试了什么。

我能够用普通的3-D数组解决这个问题并编写递归比较器并调用库的sort函数(使用比较器)。但是,在我改为boost的multi_array后,我无法解决问题。我搜索了很多提升文档我找不到任何解决方案。我不知道如何编写递归比较器来对boost multi_array进行排序。

问题。

有人能为我提供boost multi_array的递归比较器的确切工作语法来对multi_array进行排序吗?代码不得使用依赖于特定编译器/操作系统的API。假设multi_array A的维数为n1 x n2 x ... x nd。

感谢。

回复Yakk。

递归比较器是这样的:

struct comparebyID
{
    bool operator()(celltuple const &t, celltuple const &u)
    {
        return comparator(0, t, u);
    }
    bool comparator(int i, celltuple const &t, celltuple const &u)
    {
        if (i == (levels - 1))
            return t.childIDs[i] < u.childIDs[i];

        if (t.childIDs[i] == u.childIDs[i])
            return comparator(i + 1, t, u);
        else
            return t.childIDs[i] < u.childIDs[i];
    }
};

使用递归比较器的sort函数是这样的:

sort(cellset, cellset + count, comparebyID());

多维数组是这样的:

struct celltuple
{
    cell c[MAX_SIZE];
    unsigned long long IDs[MAX_IDS];
    int childIDs[MAX_IDS];
    int prevChildIDs[MAX_IDS];
    unsigned long long prevIDs[MAX_IDS];
}cellset[MAX_CELLTUPLES];

我没有包含许多关于每个参数代表什么的其他细节,因为它变得混乱(因为它试图做许多其他事情),但核心思想如示例所述。

我想要做的是为multi_array编写一个递归比较器,如下所示。

boost::multi_array<int, 3> cell_tuple;

我不能像compareByID那样简单地编写比较器,因为当对象是multi_array时,我不知道如何将参数传递给比较器函数。

这有帮助吗?

回复sehe。

出色的解决方案。万分感谢。看来你是使用boost和c ++的天才。它完全奏效了。你用于交换和比较器功能的想法是惊人的。我不知道这些函数调用(例如lexicographical_compare()等)甚至存在。非常感谢你。

我有两个相关的问题:

(1)说,我们为所有维度排序multi_array A.我们希望将相同的交换/交换/转换应用于multi_array B.我们可以用你给出的想法来做到这一点吗?

我知道我们可以通过编写一个单独的自定义排序器来解决这个问题(当我们交换时,我们可以在A和B中交换组件)。但我很好奇是否可以用比较器概念解决这个问题,因为当我们使用它来排序时,比较器对multi_array B一无所知。如何解决这个问题?

(2)我们在my_comp中有多个重载函数真的有必要吗?我们不能为此目的拥有一个完全通用的功能吗? (对不起,我是multi_array,sub_array概念的新手。)

1 个答案:

答案 0 :(得分:1)

您不仅需要比较器,还需要其他concepts required for std::sort to work。具体做法是:

  
      
  • RandomIt必须符合ValueSwappableRandomAccessIterator
  • 的要求   

因此,我攻击了通用swap实现。请注意它使用实现细节:

namespace boost { namespace detail { namespace multi_array {
    template <typename T, size_t dims>
    static void swap(sub_array<T, dims> a, sub_array<T, dims> b) {
        using std::swap;
        for (auto ai = a.begin(), bi = b.begin(); ai != a.end() && bi != b.end(); ++ai, ++bi) {
            swap(*ai, *bi);
        }
    }
} } }

比较器可以类似地直接进行,看起来像:

struct my_comp {
    template <typename T, size_t dims>
    bool operator()(sub_array<T, dims> const& a, sub_array<T, dims> const& b) const {
        return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), *this);
    }

    // ... some technical overloads omitted

    template <typename T>
    bool operator()(T a, T b) const {
        return std::less<T>()(a,b);
    }
};

在所有情况下,我们只需遵循标准库算法来完成工作,递归地传递*this作为比较器!

全面演示: Live On Coliru

#include <boost/multi_array.hpp>
#include <iostream>

namespace ba = boost::multi_array_types;

using Arr = boost::multi_array<int, 3>;

static std::ostream& operator<<(std::ostream& os, Arr const& arr) {
    for(auto const& row : arr) {
        for (auto const& col : row) {
            for (auto const& cell : col) os << cell << " ";
            os << ";";
        }
        os << "\n";
    }
    return os;
}

struct my_comp {
    // short hand only:
    template <typename T, size_t dims> using sub_array = boost::detail::multi_array::sub_array<T, dims>;

    template <typename T, size_t dims>
    bool operator()(sub_array<T, dims> const& a, sub_array<T, dims> const& b) const {
        return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), *this);
    }

    template <typename T, size_t dims>
    bool operator()(boost::multi_array<T, dims> const& a, sub_array<T, dims> const& b) const {
        return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), *this);
    }

    template <typename T, size_t dims>
    bool operator()(sub_array<T, dims> const& a, boost::multi_array<T, dims> const& b) const {
        return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(), *this);
    }

    template <typename T>
    bool operator()(T a, T b) const {
        return std::less<T>()(a,b);
    }
};

namespace boost { namespace detail { namespace multi_array {
    template <typename T, size_t dims>
    static void swap(sub_array<T, dims> a, sub_array<T, dims> b) {
        using std::swap;
        for (auto ai = a.begin(), bi = b.begin(); ai != a.end() && bi != b.end(); ++ai, ++bi) {
            swap(*ai, *bi);
        }
    }
} } }

using boost::detail::multi_array::swap;

#include <boost/range/algorithm.hpp>
int main() {
    Arr arr(boost::extents[5][3][3]);

    auto initial = {
        1,2,3, 4,5,6, 7,8,9,
        1,2,3, 1,3,3, 4,1,1,
        8,9,9, 6,9,7, 17,2,3,
        1,2,3, 1,3,2, 2,8,1,
        1,2,3, 1,3,3, 4,2,1,
    };

    boost::copy(initial, arr.origin());
    std::cout << arr;

    std::sort(arr.begin(), arr.end(), my_comp{});
    std::cout << "After sort\n" << arr;
}

印刷:

1 2 3 ;4 5 6 ;7 8 9 ;
1 2 3 ;1 3 3 ;4 1 1 ;
8 9 9 ;6 9 7 ;17 2 3 ;
1 2 3 ;1 3 2 ;2 8 1 ;
1 2 3 ;1 3 3 ;4 2 1 ;
After sort
1 2 3 ;1 3 2 ;2 8 1 ;
1 2 3 ;1 3 3 ;4 1 1 ;
1 2 3 ;1 3 3 ;4 2 1 ;
1 2 3 ;4 5 6 ;7 8 9 ;
8 9 9 ;6 9 7 ;17 2 3 ;