web-dev-qa-db-ja.com

サイズ40、400、または4000のセットにない整数を効率的に見つける

古典的な問題に関連する 40億の整数ではない整数を見つける しかし、まったく同じではない。

明確にするために、integersで本当に意味するのは、その数学的な定義のサブセットにすぎません。つまり、整数の数が有限であると仮定します。 C++では、[INT_MIN, INT_MAX]の範囲のintです。

std::vector<int>(重複なし)またはstd::unordered_set<int>が与えられ、そのサイズは40、400、4000程度ですが、大きすぎません。与えられたもの?

オーバーフローの心配がない場合は、ゼロ以外のすべてを1で乗算し、積に1を加算できます。しかし、あります。攻撃者のテストケースには、意図的にINT_MAXを含めることができます。

私は、単純で非ランダムなアプローチを支持しています。何かありますか?

ありがとうございました!

更新:あいまいさを解消するために、重複がないことが保証されている未ソートのstd::vector<int>を考えてみましょう。だから、私はO(n log(n))よりも良いものがあるかどうか尋ねています。また、テストケースにはINT_MININT_MAXの両方が含まれることがあることに注意してください。

29
fleix

入力に含まれていない最初の_N+1_候補整数を返すことができます。最も単純な候補は、番号_0_からNです。これにはO(N)スペースと時間が必要です。

_ int find_not_contained(container<int> const&data)
 {
     const int N=data.size();
     std::vector<char> known(N+1, 0);   // one more candidates than data
     for(int i=0; i< N; ++i)
         if(data[i]>=0 && data[i]<=N)
             known[data[i]]=1;
     for(int i=0; i<=N; ++i)
         if(!known[i])
             return i;
     assert(false);                     // should never be reached.
 }
_

ランダムな方法はスペース効率が高くなりますが、最悪の場合はデータのパスを増やす必要があります。

31
Walter

ここでは、ランダムメソッドが実際に非常に効率的です。

決定論的な方法を使用し、サイズnが大きすぎない、たとえば4000であると仮定する場合、サイズm = n + 1のベクトルx(または少し大きく、たとえば計算を容易にするために4096)、0で初期化されます。

範囲内の各iに対して、x [array [i] modulo m] = 1を設定します。

次に、単純なO(n) xで検索すると、arrayにない値が提供されます

注:モジュロ演算は、厳密には「%」演算ではありません

編集:ここで4096のサイズを選択すると計算が簡単になると述べました。より具体的には、これはモジュロ演算が単純な&演算で実行されることを意味します

9
Damien

入力ベクトルの順序を変更できる場合、O(N) timeを使用してO(1)補助スペース次のアルゴリズム[注1](ベクトルに繰り返しデータが含まれている場合も、アルゴリズムは機能します。)

size_t smallest_unused(std::vector<unsigned>& data) {
  size_t N = data.size(), scan = 0;
  while (scan < N) {
    auto other = data[scan];
    if (other < scan && data[other] != other) {
      data[scan] = data[other];
      data[other] = other;
    }
    else
      ++scan;
  }
  for (scan = 0; scan < N && data[scan] == scan; ++scan) { }
  return scan;
}

最初のパスでは、範囲kの後に[0, N)の範囲にあるkが見つかった場合、kの位置に存在することが保証されます。この再配置は、データの損失を避けるためにスワッピングによって行われます。スキャンが完了すると、インデックスと値が同じではない最初のエントリは、配列内のどこからも参照されません。

エントリは以前のインデックスから参照される可能性があるため、このアサーションは100%明白ではない場合があります。ただし、その場合、エントリはインデックスに等しくない最初のエントリにはなりません。これは、以前のエントリがその基準を満たすためです。

このアルゴリズムがO(N)であることを確認するには、6行目と7行目のスワップは、ターゲットエントリがそのインデックスと等しくない場合にのみ発生し、スワップ後、ターゲットエントリはそのインデックスと等しいことに注意してください。したがって、最大Nスワップを実行でき、5行目のif条件はtrueで最大N回です。一方、if条件がfalseの場合、scanがインクリメントされますが、これはN回しか発生しません。したがって、ifステートメントは最大で2N回(O(N))回評価されます。


ノート:

  1. コードを明確にするため、ここでは符号なし整数を使用しました。アルゴリズムは、符号付き整数に簡単に調整できます。たとえば、符号付き整数を[INT_MIN, 0)から符号なし整数[INT_MAX, INT_MAX - INT_MIN)にマッピングすることにより(減算は、結果が得られないCのセマンティクスに従ってではなく、数学的に行われます2の補数では、それは同じビットパターンです。もちろん、これは数字の順序を変更し、「最小の未使用整数」のセマンティクスに影響します。順序を保持するマッピングも使用できます。
6
rici

ランダムなx(INT_MIN..INT_MAX)を作成し、すべてに対してテストします。失敗時にx ++をテストします(40/400/4000の非常にまれなケース)。

4
Alexey Birukov

ステップ1:ベクトルをソートします

これはO(n log(n))で実行できます。オンラインでいくつかの異なるアルゴリズムを見つけ、最も好きなアルゴリズムを使用できます。

ステップ2:ベクターにない最初のintを見つけます

INT_MINからINT_MIN + 40/400/4000に簡単に反復し、ベクトルに現在のintがあるかどうかを確認します。

擬似コード:

SIZE = 40|400|4000 // The one you are using
for (int i = 0; i < SIZE; i++) {
    if (array[i] != INT_MIN + i)
        return INT_MIN + i;

解決策はO(n log(n)+ n)の意味:O(n log(n) )


編集:何かを求めて編集内容を読むだけbetterよりO(n log( n))、ごめんなさい。

2
dquijada

std::unordered_set<int>ではなく)std::vector<int>で整数が提供される場合、unordered_set<int>に存在しない1つの整数値に到達するまで、整数値の範囲を単純にトラバースできます。 std::unordered_set<int>find()メンバー関数を介した検索を提供するため、std::unodered_setの整数の存在を検索するのは非常に簡単です。

このアプローチのスペースの複雑さはO(1)になります。


intの有効な値lowest(つまり、std::numeric_limits<int>::min())で走査を開始すると、std::unordered_set<int>に含まれていないlowestintを取得します。

int find_lowest_not_contained(const std::unordered_set<int>& set) {
   for (auto i = std::numeric_limits<int>::min(); ; ++i) {
      auto it = set.find(i); // search in set
      if (it == set.end()) // integer not in set?
         return *it;
   }
}

同様に、intgreatestの可能な値(つまり、std::numeric_limits<int>::max())でトラバースを開始すると、lowestintstd::unordered_set<int>

int find_greatest_not_contained(const std::unordered_set<int>& set) {
   for (auto i = std::numeric_limits<int>::max(); ; --i) {
      auto it = set.find(i); // search in set
      if (it == set.end()) // integer not in set?
         return *it;
   }
}

intsが均一ハッシュ関数によってunordered_set<int>のバケットにマッピングされていると仮定すると、unordered_set<int>の検索操作は一定の時間で実現できます。実行時の複雑さはO(M)になります。ここで[〜#〜] m [〜#〜]は探している整数範囲のサイズです含まれていない値。 [〜#〜] m [〜#〜]unordered_set<int>のサイズの上限です(つまり、あなたの場合M <= 40)。

確かに、このアプローチでは、サイズがunordered_setのサイズよりも大きい整数範囲を選択すると、unordered_set<int>に存在しない整数値に遭遇することが保証されます。

0
El Profesor