11

ベクトル比較関数の C++ でのマイクロ最適化についてアドバイスが必要です。2 つのベクトルが等しいかどうかを比較し、要素の順序は関係ありません。

template <class T>
static bool compareVectors(const vector<T> &a, const vector<T> &b)
{
  int n = a.size();
  std::vector<bool> free(n, true);
  for (int i = 0; i < n; i++) {
    bool matchFound = false;
    for (int j = 0; j < n; j++) {
      if (free[j] && a[i] == b[j]) {
        matchFound = true;
        free[j] = false;
        break;
      }
    }
    if (!matchFound) return false;
  }
  return true;
}

この関数は頻繁に使用され、最適化する方法を考えています。いくつか提案をお願いできますか?ちなみに私はC++11を使っています。

ありがとう

4

7 に答える 7

14

このコードは一種の「セット等価性」チェックのみを行うことに気付きました (そして今、あなたが実際にそう言っていることがわかりました。私はなんてお粗末な読者なのでしょう!)。これははるかに簡単に実現できます

template <class T>
static bool compareVectors(vector<T> a, vector<T> b)
{
    std::sort(a.begin(), a.end());
    std::sort(b.begin(), b.end());
    return (a == b);
}

ヘッダーを含める必要がありますalgorithm

ベクトルが常に同じサイズである場合は、メソッドの先頭にアサーションを追加することをお勧めします。

assert(a.size() == b.size());

これは、この操作を誤って長さが等しくない場合に、プログラムのデバッグに役立ちます。

それ以外の場合、長さが等しくない場合、ベクトルを同じにすることはできないため、追加するだけです

if ( a.size() != b.size() )
{
   return false;
}

ソート指示の前。これにより、多くの時間を節約できます。

これの技術的な複雑さは、O(n*log(n))(通常) その複雑さのソートに主に依存しているためです。これはあなたのO(n^2)アプローチよりも優れていますが、必要なコピーのために悪化する可能性があります. 元のベクトルがソートされている可能性がある場合、これは関係ありません。


あなたのアプローチに固執したいが、微調整したい場合は、これに関する私の考えを次に示します。

これに使用できますstd::find

template <class T>
static bool compareVectors(const vector<T> &a, const vector<T> &b)
{
  const size_t n = a.size(); // make it const and unsigned!
  std::vector<bool> free(n, true);
  for ( size_t i = 0; i < n; ++i )
  {
      bool matchFound = false;
      auto start = b.cbegin();
      while ( true )
      {
          const auto position = std::find(start, b.cend(), a[i]);
          if ( position == b.cend() )
          {
              break; // nothing found
          }
          const auto index = position - b.cbegin();
          if ( free[index] )
          {
             // free pair found
             free[index] = false;
             matchFound = true;
             break;
          }
          else
          {
             start = position + 1; // search in the rest
          }
      }
      if ( !matchFound )
      {
         return false;
      }
   }
   return true;
}

もう 1 つの可能性は、自由な位置を格納する構造を置き換えることです。使用したインデックスをベクターに保存するstd::bitsetか、単に保存して、そのインデックスベクターに一致がないかどうかを確認することができます。この関数の結果が非常に頻繁に同じである場合 (つまり、ほぼ true またはほぼ false)、それを反映するようにデータ構造を最適化できます。たとえば、ほんの一握りのインデックスのみを保存する必要があるため、結果が通常 false の場合は、使用済みインデックスのリストを使用します。

この方法の複雑さは、アプローチと同じです。std::find を使用して物事を検索すると、手動で検索するよりも優れている場合があります。(たとえば、データがソートされていて、コンパイラがそれを認識している場合、これはバイナリ検索になります)。

于 2013-06-30T19:56:52.903 に答える
14

O(n) 内の 2 つの並べ替えられていないベクトル (u,v) を確率的に比較できます。

計算:

U= xor(h(u[0]), h(u[1]), ..., h(u[n-1]))
V= xor(h(v[0]), h(v[1]), ..., h(v[n-1]))

U==V の場合、ベクトルはおそらく等しいです。

h(x) は、MurmurHash などの非暗号化ハッシュ関数です。(暗号化機能も同様に機能しますが、通常は遅くなります)。

(これはハッシュしなくても機能しますが、値の範囲が比較的狭い場合は堅牢性が大幅に低下します)。

多くの実用的なアプリケーションでは、128 ビットのハッシュ関数で十分です。

于 2013-06-30T21:08:08.283 に答える
1

他の人が示唆したように、ベクトルを事前にソートするとパフォーマンスが向上します。

追加の最適化として、ベクトルからヒープを作成して比較することができます (O(n*log(n) でソートするのではなく、複雑さ O(n) で)。

その後、不一致が発生するまで、両方のヒープ (複雑さ O(log(n))) から要素をポップできます。

これには、ベクトルが等しくない場合にベクトルをソートする代わりにヒープ化するだけであるという利点があります。

以下はコードサンプルです。本当に最速のものを知るには、ユースケースのサンプル データを使用して測定する必要があります。

#include <algorithm>

typedef std::vector<int> myvector;

bool compare(myvector& l, myvector& r)
{
   bool possibly_equal=l.size()==r.size();
   if(possibly_equal)
     {
       std::make_heap(l.begin(),l.end());
       std::make_heap(r.begin(),r.end());
       for(int i=l.size();i!=0;--i)
         {
           possibly_equal=l.front()==r.front();
           if(!possibly_equal)
             break;
           std::pop_heap(l.begin(),l.begin()+i);
           std::pop_heap(r.begin(),r.begin()+i);
         }
     }
  return possibly_equal;
}
于 2013-07-01T13:37:11.283 に答える
0

この関数を同じベクトルで頻繁に使用する場合は、比較のために並べ替えたコピーを保持する方がよい場合があります。

理論的には、ベクトルを並べ替えて、それぞれが一度だけ比較される場合、並べ替えられたベクトルを比較する方が良いかもしれません (並べ替えは O(n*log(n)) で、並べ替えられたベクトル O(n) を比較し、関数は O( n^2).しかし、同じベクトルを頻繁に比較しないと、ソートされたベクトルにメモリを割り当てるのに費やされる時間が理論上の利益を小さくすると思います。

すべての最適化と同様に、プロファイリングが確認する唯一の方法です。いくつかstd::sort/ std::equalコンボを試してみます。

于 2013-06-30T20:14:09.283 に答える
-1

別の可能な解決策 (すべての要素が一意である場合にのみ実行可能) は、@stefan の解決策をいくらか改善するはずです (複雑さは O(NlogN) に残りますが) は次のとおりです。

template <class T>
static bool compareVectors(vector<T> a, const vector<T> & b)
{
    // You should probably check this outside as it can 
    // avoid you the copy of a
    if (a.size() != b.size()) return false;

    std::sort(a.begin(), a.end());
    for (const auto & v : b)
        if ( !std::binary_search(a.begin(), a.end(), v) ) return false;
    return true;
}

O(NlogN)これは、ソートb( O(NlogN)) してから両方のベクトルを検索( ) するのではなく、操作として直接検索を実行するため、高速になるはずO(N)です。

于 2016-04-19T09:40:17.517 に答える