- ベストアンサー
C++でベクターの値の比較を効率化する方法
- C++で2つのベクターの値を網羅的に比較する関数を作成中です。
- 現在の実装ではループを使用して値を比較していますが、要素数が多い場合に処理時間がかかる問題があります。
- 効率的な方法として、ベクターの要素をソートしてから二分探索を行う方法があります。これにより処理速度を向上させることができます。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
事前に、両ベクターをソートしておいて、小さい値からチェックしていけば、余分な比較をせずにすむとか。 ex) vctr_1の100.0で範囲外だったvctr_2の50.0と70.2は、vctr_1の200.5でも範囲外なのが明らか。 ソートするための計算量も含めれば、O(n*log n)でいけるかと。 ベクターの出現順の安定性を求められなければですが。 // rubyで書いているのは、C++慣れてないから。
- 参考URL:
- http://ideone.com/R4HQX
その他の回答 (3)
- ki073
- ベストアンサー率77% (491/634)
No.3です。 >3)i7-920. コア数. 4. スレッド数. 8 になります。ご指摘のようにboostのマルチスレッドの利用も将来的には考えていました。 boostは名前しか知らないんですが、openMPの方が簡単かもしれません。基本的にプログラム構造を変更する必要がないので。 ところで、質問欄と同じような総当たりのアルゴリズムでプログラムを書いてベンチマークをとってみました。 それぞれ100個のデータです。C++ではなくNo.2と同じRubyです。narrayという行列計算用のライブラリを使っています。(実はcompare_doubles_with_tolは1行で書けるのです) compare_doubles_with_tolに相当する部分だけの時間ですが、1回あたり0.14msecになります。(実際には1000回実行して0.140秒です)CPUも同じCore i7-920(2.66GHz)です。C++で書けばこれよりも更に速いはずです。 この結果を遅いとみるか、十分な速さと見るかは何回くらい実行するかで決りますが、全体のプログラムができたらプロファイラで各部分の実行時間を計ってみることをお勧めします。ファイルの読み込みにものすごく時間がかかっていて、計算の部分をいくら速くしても効果がないこともよくありますので。
お礼
>全体のプログラムができたらプロファイラで各部分の実行時間を計ってみることをお勧めします たしかに実際のプログラムでは他にも色々な処理も関与しているのでプロファイラでの計測は必要ですね。いままで使ったことはなかったのですがこの機会に試してみようと思います
- ki073
- ベストアンサー率77% (491/634)
ちょっと確認ですが、 1) vctr_1, vctr_2の個数はどれくらいか(両方とも100前後か、片方が多い、少ない)vctr_2をソートするための時間と効果のバランス。 2) vctr_2の値が毎回違うのか、何度か同じものを利用しているのなら事前にソートすることができる。 3) CPUのCore数だけ簡単に並列化可能ですが、Core数はいくつで利用可能か。ある程度まで最適化して後並列化すれば並列化分だけ速くなる。 それとソートしたもの調べるときに、頭から順番に調べるのではなく、二分探索などを使うと速くなります。
補足
1)vctr_1, vctr_2の要素は両方とも100前後になります。両方ともだいたい同じ数になります。 2) vctr_2の値は毎回異なります。 3)i7-920. コア数. 4. スレッド数. 8 になります。ご指摘のようにboostのマルチスレッドの利用も将来的には考えていました。 二分探索ははじめて聞いたのですが、確かに有効なアプローチですね。
- kmee
- ベストアンサー率55% (1857/3366)
「v1の要素しか返さない」 「v_matchの値は重複しない」のなら ・ v_match.push_back(*it1); した時点で残りのv2と比較する必要無し ※ もとのプログラムでは、vctr_2: {100.1, 100.1 , 100.1 }だったらmatched: {100,100,100}になります。 v1,v2ともに昇順ソートしてあれば ・*it1 + tol <= *it2 になった時点で それ以降のv2は調べる必要無し ・*it1 -tol >= *v2.end()になった時点で、それ以降のv1は調べる必要無し ・*it1 +tol <= *v2.begin()なら、その*itについて調べる必要無し ・毎回v2の先頭から比較するのではなく、 [前回の最後の*it2] <= *it1 - tol なら、それより前にマッチするものは無いので、そのままit2を続ける。 そうでないなら、 *it2 <= *it1 - tol になるまで--it2で前へ戻す。 ※ 上記の「v1のみ、重複無し」なら、この時点での判定→v_match.push_back→次のit1も可能
お礼
ご指摘のように論理的に処理することでかなり無駄が省けそうですね。思っていたより複雑な処理になりそうですがアドバイスを参考にさせていただいてなんとか書いてみようと思います。 ありがとうございました。
お礼
お礼が遅くなってしましたが、コードありがとうございます。 rubyは使ったことはないのですが、書いてあることはほぼ読み取れますのでC++に書き直してみようと思います。とても助かりました。