【问题标题】:How to sort a range based on the value of some transformation of its values?如何根据其值的某些转换的值对范围进行排序?
【发布时间】:2019-09-04 04:56:18
【问题描述】:

arr 成为某种类型的数组Tfunc(const &T) 一些计算量很大的函数。要按照func(T) 的值对arr 进行排序,我们可以天真地写。

std::sort(arr.begin(), arr.end(), [](const T& a, const T&b) {
    return func(a) < func(b);
}

但是,这将平均调用func O(nlogn) 次。显然,我们只需要 n 次调用。有没有任何惯用和简洁的方法来做到这一点?

我知道以下两种解决方案,但我想找到一个更好的解决方案。

  • Ts 和func 的返回值打包到一个结构中并对其进行排序。这在计算上是最优的,但它还在排序之前对初始数组进行了一个完整副本,然后再进行另一个完整副本以将值放回arr

  • 创建第二个数组并对数组进行并行排序。据我所知,没有惯用的方法来以这种方式对两个数组进行排序。这可以通过编写自定义迭代器和交换函数来完成。这已经足够了,但它也需要比理想更多的样板代码。

理想的解决方案是基于 STL 并尽可能少的样板代码,但欢迎所有贡献。

【问题讨论】:

    标签: c++ performance sorting boost transform


    【解决方案1】:

    第一个非泛型解决方案

    你需要的是函数结果的一种记忆。如果您的函数fun 没有副作用,并且在std::sort 中使用它,我推断它没有我会想到这样的事情:

    #include <unordered_map>
    
    template<class T, class U>
    class caller{
    public:
        const U &call(const T& t) const{
            auto found = memoized.find(t);
            if (found != memoized.end())
                return found->second;
    
            return memoized.insert(std::make_pair(t, fun(t))).first->second;
        }
    private:
        mutable std::unordered_map<T, U> memoized;
    };
    

    它的用法如下所示:

    caller<T, decltype(func(std::declval<T>()))> c;
    std::sort(arr.begin(), arr.end(), [](const T& a, const T&b) {
        return c.call(a) < c.call(b);
    }
    

    更通用的解决方案

    在做出第一个解决方案后,我玩了一下,并做了一个更通用的、符合 C++17 的解决方案。它应该适用于采用一个可复制和可散列的参数的每个函数。看看:

    #include <unordered_map>
    
    int fii(int){
        return 0;
    }
    
    template<class T, auto fun>
    class memoizer{
    public:
        const auto &call(const T& t) const{
            auto found = memoized.find(t);
            if (found != memoized.end())
                return found->second;
    
            return memoized.insert(std::make_pair(t,  fun(t)))
                .first->second;
        }
    private:
        mutable std::unordered_map<T, decltype(fun(T()))> memoized;
    };
    
    auto memoized_fii = memoizer<int, fii>{};
    

    【讨论】:

    • 这个不错。唯一的要求是 T 是可散列的,对吗?
    • @patatahooligan 它必须是可复制的才能将其添加到地图 AFAIK,结果相同
    • 您可以将const&amp;返回到地图中的值并使用std::move(result)将其移入以避免2个副本。
    猜你喜欢
    • 2021-08-21
    • 2022-08-20
    • 2023-03-26
    • 1970-01-01
    • 2014-01-14
    • 1970-01-01
    • 2015-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多