C++ 如何根据某个范围值的某些转换对其进行排序?

C++ 如何根据某个范围值的某些转换对其进行排序?,c++,performance,sorting,boost,transform,C++,Performance,Sorting,Boost,Transform,设arr是某种类型的数组T和func(const&T)某种计算代价高昂的函数。要根据func(T)的值对arr进行排序,我们可以天真地编写 std::sort(arr.begin(), arr.end(), [](const T& a, const T&b) { return func(a) < func(b); } 排序(arr.begin(),arr.end(),[](常数T&a,常数T&b){ 返回函数(a)秒; 返回备忘。插入(std::make_pair

arr
是某种类型的数组
T
func(const&T)
某种计算代价高昂的函数。要根据
func(T)
的值对
arr
进行排序,我们可以天真地编写

std::sort(arr.begin(), arr.end(), [](const T& a, const T&b) {
    return func(a) < func(b);
}
排序(arr.begin(),arr.end(),[](常数T&a,常数T&b){ 返回函数(a) 但是,这将平均调用
func
O(nlogn)次。显然,我们只需要n次调用。有什么惯用的简洁方法可以做到这一点吗

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

  • T
    s和
    func
    的返回值打包到一个结构中并进行排序。这在计算上是最优的,但它也会在排序之前对初始数组进行一次完整复制,然后再进行另一次完整复制,以将值放回
    arr

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

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

第一个非通用解决方案 你需要的是函数结果的记忆。如果你的函数
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);
}
#包括
模板
类调用者{
公众:
常数U&call(常数T&T)常数{
自动查找=已记忆。查找(t);
if(found!=memonized.end())
返回找到->秒;
返回备忘。插入(std::make_pair(t,fun(t))。第一->第二;
}
私人:
可变std::无序的_图已存储;
};
其用法如下所示:

#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; 排序(arr.begin(),arr.end(),[](常数T&a,常数T&b){ 返回c.call(a) 更通用的解决方案 在制定了第一个解决方案之后,我做了一些比较通用的、符合C++17的解决方案。它应该适用于每一个具有一个可coypable和hashable参数的函数。请看:

#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>{};
#包括
国际金融情报中心(国际){
返回0;
}
模板
类记忆器{
公众:
常数自动和呼叫(常数T&T)常数{
自动查找=已记忆。查找(t);
if(found!=memonized.end())
返回找到->秒;
return memonized.insert(std::make_pair(t,fun(t)))
.第一->第二;
}
私人:
可变std::无序的_图已存储;
};
自动记忆_fii=记忆器{};

这很好。唯一的要求是T是可散列的,对吗?@patatahooligan它必须是可复制的才能将其添加到map AFAIK,结果也是如此。您可以将
常量&
返回到map中的值,并使用
std::move(result)
将其移入以避免2个副本。