2017-04-16 17 views
6

C++標準ライブラリでは、コンパレータをstd::sortに渡します。しかし、私はTオブジェクトのリストを関数fでソートしたいという私のコードに多くの場合があります。このようなコンパレータは有効なオプションです:std :: sort by unary mapping

bool compare(const T& a, const T& b) { 
    return f(a) < f(b); 
} 

これは最適ではありません。 fは評価が遅くなりますが、同じTオブジェクトを持つすべてのコールで同じ値が返されます。ですから、範囲内のすべてのオブジェクトに対してfを1回計算し、それらの結果をソートすることです。

template <typename IterT, typename Transformation> 
void sort(IterT left, IterT right, Transformation f) { /* ? */ } 

ように、このコールの後、シーケンスleft内のすべてのiterためf(*iter) <= f(*std::next(iter))rightへ:

私の目標は、(私がすることができていない)、この関数を記述することです。

さらに、関数がこれらの要件を満たす必要があります。

  • はタイプTのいずれかの追加オブジェクトを割り当てません。
  • f正確にはstd::distance(left, right)と評価されます。
  • O(n log n)の全体的な複雑さを維持します。
  • std :: sortに関して実装する必要があります。もちろん、私は自分のマージソートを実装することでこの問題を解決することができますが、それは避けたいものです。

(C++ 11が好ましく、C++ 14もOKです)

+1

オブジェクト 'a'の中に' f(a) 'の結果を格納し、オブジェクトの状態が変化したときにのみ計算してみましょう。 –

+0

"memoization"を使って 'f'の結果をキャッシュすることができます。 'unordered_map'を使用します。 'T '型のオブジェクトをマップキーとして使うことができない場合、複雑になります。' f'が使う 'T'のデータのサブセットがありますか?あるいは、実際のオブジェクトへのポインタ配列をソートできますか? –

+1

'f(* iter)'は '* iter'または' iter'のみに依存していますか? –

答えて

3

あなたが望むのは、Schwartzian transformのC++実装です。私はコードのいくつかの行で簡単な解決策はありませんが、私はSchwartzian transform utilityをC++ 14の私のライブラリに実装しました。残念ながら、それはstd::sort(少なくともRanges TSまでではない)によって処理されないプロキシイテレータに頼っていますが、代わりにライブラリの他のソータを使うことができます。このように呼ばれると、

#include <cpp-sort/adapters/schwartz_adapter.h> 
#include <cpp-sort/sorters/default_sorter.h> 

template <typename IterT, typename Transformation> 
void sort(IterT left, IterT right, Transformation f) 
{ 
    using sorter = cppsort::schwartz_adapter<cppsort::default_sorter>; 
    sorter{}(left, right, f); 
} 

ソーターは[left, right)を渡り、f(*it)にイテレータitを関連付けるstd::distance(left, right)ペアを作成します。ここでは、あなたの質問に言及したsort関数を書くことができるかです。次に、渡されたソーター(上記の例ではdefault_sorter、書面ではpattern-defeating quicksort)を使用して、ペアのコレクションをソートします。フードの下でProxy iteratorsが使用されるため、ペアが交換されると元のコレクションの要素が交換されます。

私はそれが非常に簡単だとは言いませんが、あなたの問題を解決するはずです。外部のライブラリに依存したくない場合でも、the soure codeからインスピレーションを得ることができます。許可されたライセンスの下にあるので、要素を使用する必要がある場合は、それを使用したいと思っていることをすべて実行することができます。

とにかく、それは主にsatifies要件のようになります。

  • (それはfの戻り値を格納するのでfTの新しいインスタンスを返さない限り)それはTの追加のインスタンスを割り当てません。
  • 実際にソートする前に、f正確にはstd::distance(left, right)が適用されます。
  • O(n log n)ソーターで使用すると、O(n log n)の全体的な複雑さが維持されます。
  • std::sortは今日の時点では十分にスマートではないため、std::sortは使用されませんが、独自のコードを書くことなく同等のアルゴリズムを使用することができます。
+0

ありがとう、また、用語シュワルツァーン変換を指摘します。私の質問には、 ::ソートしますが、ユースケースに応じて、あなたはb eはより適切なので、私は受け入れられたとマークします。 –

+0

@ArereasTそれは本当です。それはおそらくもっと簡単な解決策であり、RaymondChenがこの種の並べ替えを実行する優れた一連の記事を書いて以来、特に使用可能です。インデックスをベースにしたソリューションは、ソート*、*順列を実行するため、おそらく時間がかかりますが、それをアサートする前に時間をとることになります:p – Morwenn

+1

@Morwennオブジェクトをスワップすることは整数をスワップするよりもインデックスベースのバージョンが高速ですソートは整数O(n log n)回スワップするため、オブジェクトをn回スワップします。 Schwartzian変換はオブジェクトO(n log n)回スワップします。 –

0

あなただけTのすべてのインスタンスのために、あなたの関数の値をキャッシュするコンパレータを書くstd::sortに固執します。

例:https://gist.github.com/PandarinDev/ee75b095c4cc256a88496f1985bf57ba

この方法でevaluate(const Foo&);(あなたのケースでf(T))のみN = the number of unique instances of Foo、N回実行されます:あなたがここに完全な例を見つけることができます

struct Foo { 
    int value; 
}; 

// Replace with your CPU time intensive f() function 
int evaluate(const Foo& foo) { 
    std::cout << "Evaluating complex function on an instance of Foo..." << std::endl; 
    return foo.value; 
} 

bool compare(const Foo& left, const Foo& right) { 
    static std::unordered_map<Foo, int> cache; 
    auto leftIt = cache.find(left); 
    auto rightIt = cache.find(right); 
    if (leftIt == cache.end()) 
     leftIt = cache.emplace(left, evaluate(left)).first; 
    if (rightIt == cache.end()) 
     rightIt = cache.emplace(right, evaluate(right)).first; 
    return (*leftIt).second < (*rightIt).second; 
} 

編集:マップにTインスタンスのコピーはあなたに問題がある場合、あなたはユニークな識別子を使用することができ、コメントで後述するように - 代わりのキーとして - など、あなたのオブジェクトのアドレスなどをオブジェクトそのもの。

+1

これにより、シーケンス内のすべてのオブジェクトがコピーされるか、間違っていますか? –

+0

@AndreasT - 問題が 'cache'マップの' Foo'オブジェクトのコピーである場合、 'Foo'キーへのポインタを持つ' cache'マップを使うことができます: 'static std :: unordered_map cache;';検索は 'auto leftIt = cache.find(&left); auto rightIt = cache.find(&right);' ;; cache.emplace(&left、evaluate(left))の挿入を最初に行い、 'cache.emplace最初に; – max66

+0

はい、 'T'オブジェクトをマップにコピーします(まだ存在していない場合)。オブジェクトをマップにコピーしたくない場合は、それぞれに固有の識別子を使用してくださいインスタンスをキーとして使用 – PandarinDev

関連する問題