简体   繁体   English

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

[英]How to sort a range based on the value of some transformation of its values?

Let arr be an array of some type T and func(const &T) some computationally expensive function.arr是某种类型T的数组,而func(const &T)某个计算量大的函数。 To sort arr by the value of func(T) , we could naively write.要按func(T)的值对arr进行排序,我们可以天真地写。

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

However this will invoke func O(nlogn) times on average.然而,这将平均调用func O(nlogn) 次。 Obviously, we need no more than exactly n invocations.显然,我们只需要 n 次调用。 Is there any idiomatic and concise way to do this?有没有任何惯用和简洁的方法来做到这一点?

I am aware of the following two solutions but I would like to find one that is better.我知道以下两种解决方案,但我想找到一种更好的解决方案。

  • Packing the T s and the return value of func in a struct and sorting that.Tfunc的返回值打包在一个结构中并对其进行排序。 This is optimal computationally but it also does one full copy of the initial array before sorting and another full copy afterwards to put the values back into arr这在计算上是最佳的,但它也会在排序之前完成初始数组的一个完整副本,然后再执行另一个完整副本以将值放回arr

  • Creating a second array and sorting the arrays in parallel.创建第二个数组并并行排序数组。 As far as I know there is no idiomatic way to sort two arrays that way.据我所知,没有惯用的方式来对两个数组进行排序。 It could be done by writing custom iterators and a swap function.可以通过编写自定义迭代器和交换函数来完成。 That's good enough but it also requires a bit more boilerplate code than ideal.这已经足够了,但它也需要比理想情况更多的样板代码。

The ideal solution would be STL-based with as little boilerplate code as possible, but all contributions are welcome.理想的解决方案是基于 STL 的,尽可能少的样板代码,但欢迎所有贡献。

First non-generic solution第一个非通用解决方案

What you need is kind of memoization of function result.您需要的是对函数结果的一种记忆。 If your function fun does not have side effects and from using it in std::sort I infer it does not I would think of something like this:如果您的函数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;
};

Its usage would look like this:它的用法如下所示:

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);
}

More generic solution更通用的解决方案

After making first solution I played a bit and made a bit more generic, C++17 compliant one.在制作第一个解决方案后,我玩了一些,并制作了一个更通用的、符合 C++17 的解决方案。 It should work with every function taking one argument that is coypable and hashable.它应该适用于每个带有一个可复制和可散列参数的函数。 Take a look:看一看:

#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>{};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM