优于O(n!)算法的无序元组比较

时间:2014-03-15 14:57:22

标签: c++11

我正在写一个小数学库(为了兴趣,没有特别的目标),我遇到了一些困难。我有一个Addition类看起来像:

    template <class ... Functions>
    struct Addition
    {
        std::tuple<Functions...> functions;
        //...
    };

我希望为此课程实施operator==。简单地返回functions == o.functions并不好,因为例如,实例化Addition<Sine, Cosine>Addition<Cosine, Sine>不会相等,但我希望它们能够。

目前对我创建的功能施加的限制是:

  1. 函数对象必须与任何其他函数对象相当。这是通过添加

    来实现的

    template <class F> bool operator==(F && f) const { return false; }

    到每个函数类的定义。

  2. 只有两个类属于同一类时才相等。换句话说,有两个类是相同的类来进行比较是必要但不充分的。

  3. 我现在的解决方案是对两个元组进行有序比较,然后置换其中一个元组并尝试另一个有序比较,并保持置换和比较直到一个返回true。此方法在O(n!)时间内运行,需要O(n!)模板实例化。

    我想要做的只是找到比较相等的元素然后删除它们并继续比较两个子元组。不幸的是,由于比较具有运行时代码(通常),您无法知道在编译时要删除哪些元素。像这样的任何解决方案都会以O(n!)模板实例化结束,这也是不可取的。

    那么,有没有办法对两个元组进行无序比较(上面两点,但如果你不使用它们,可以获得奖励积分),它们的运行时间小于O(n!)小于O(n!)模板实例化。

2 个答案:

答案 0 :(得分:2)

最简单的选择是使用boost fusion set

与std :: tuple相比,另一个优点是你获得了boost融合容器的算法,因此你可以使用fold检查融合集中的每个Function对象是否包含在其他融合集中:

    template <class ... Functions>
    struct Addition
    {
        boost::fusion::set<Functions...> functions;
        //...


        struct Equal
        {

            typedef boost::fusion::set<Functions...> Functions;
            Functions& functions;

            template<typename T>
            std::enable_if<boost::fusion::has_key<Functions, T>::value, bool> 
            operator()(bool res, T& t) const
            {
                  return res && boost::fusion::at_key<T>(functions) == t;
            }

            template<typename T>
            std::enable_if<!boost::fusion::has_key<Functions, T>::value, bool> 
            operator()(bool res, T& t) const
            {
                return false;
            }

        };

        template <class ... Functions1>
        bool operator==(Addition<Functions1...> that)
        {
            return boost::fusion::fold(true, that.functions, Equal<Functions...>{this.functions});
        };

        ...
    };

答案 1 :(得分:0)

我解决了这个问题已经有一段时间了,但我想我会在这里分享我的解决方案以防其他任何人决定尝试这个疯狂的事情,他们可能会觉得这很有帮助。此外,也许在发帖时,有人可能会提出一个比我发现的问题更简单的解决方案。

我最后提出了dyp的建议,即为我的班级提供类似UUID的对象。
这比所有函数的数字标识符要困难得多,因此对于容器函数(加法,减法,乘法等等),UUID是它们的特定标识符,后跟其组件函数的UUID的有序列表。

例如:

    struct Sine { typedef int_<0> UUID;};
    struct Cosine { typedef int_<1> UUID;};

    template <class ... Functions>
    struct Addition { typedef list<int_<2>, list<Functions::UUID...> UUID;}

可重新排序的容器(相反,比如,矩阵对象的乘法)总是被重新排序以按其UUID排序,以便容器函数的UUID对于由相同函数组成的类是相同的,否则不同。

    auto f1 = Simplify(Add(Sine{}, Cos{}));
    auto f2 = Simplify(Add(Cosine{}, Sine{}));
    static_assert(std::is_same<decltype(f1), decltype(f2)>::value, "");
    assert(f1 == f2);

这也意味着UUID对于具有相同功能但是顺序不同的非阿贝尔功能容器会有所不同,这是一个奖励(不,我不在这里解释如何我实际上表示操作是非阿贝尔的。)

然后,这些容器函数的比较非常简单,我可以functions == o.functions