線形時間の最悪の場合をどのようにカウントするのですか?


8

この質問この質問は私に少し考えさせられました。長さの配列をソートするためnk中のユニークな要素、我々は、配列内の値の数を格納できるようにする必要があります。いくつかの提案がありますが、最悪の場合線形時間でこれを行う方法を探しています。すなわち:O(n+klogk)

リストの指定されたのを有する要素別個の要素、タプルのリストを決定すべての固有の要素よう要素の数であるで。AnkU={(xi,ci)}kxiAcixiA

私がこれまでに提案してきた(失敗した)アイデアの一部を以下に示します。

  1. 平衡型二分探索木 -これを使用すると、O(logk)をツリーに挿入して値を増やす必要があります。挿入後、O(k)でツリートラバーサルを実行できます。したがって、合計時間がO(nlogk)これは遅すぎます。
  2. ハッシュマップ -これにより、O(1) 予想される挿入、つまりO(n) 予想される時間を取得できます。ただし、これはまだO(n)最悪のケースではありません。
  3. 空の空間マッピングA最小要素と最大要素を見つけます。この範囲をカバーするのに十分なメモリを割り当てます(ただし、初期化しません)。このメモリを基本的にハッシュマップとして使用し、ランダムハッシュを含めて、破損したメモリにアクセスしないようにします。この戦略には問題があります。(1)失敗する可能性が非常に低い確率論的ですが、保証されていません。このようなメモリを使用すると、浮動小数点または整数の制約に制限されます。
  4. 連想配列 - ハッシュマップやBSTと同様に、使用できる他の多くの連想配列がありますが、これらの制約に一致するものは見つかりません。

たぶん私が見逃している明らかな方法があるかもしれませんが、それは潜在的に不可能かもしれないと私は思います。あなたの考えは何ですか?


3
要素の明確性の問題に決定木の複雑さの下限があるため比較モデルで実行できません。Ω(nlogn)
John L.

@ Apass.Jack、そうそうそうそう。私が考慮しなかった些細な削減。簡単な答えとして書いていただければ、承諾します。
ライアン、

HashMap 償却O(n)を保証しないのはなぜですか?
javadba

1
@javadbaたとえば、すべての要素が同じ値にハッシュされているとします。
John L.

ああ、それが不完全なハッシュの場合はそうです。
javadba

回答:


6

これはいい質問です。

このWikipediaの記事で述べたように、比較モデル、またはより一般的な代数的決定木モデルでは、要素のΘ(nlogn)性の問題に最悪の場合のΘ n log n 時間複雑度の下限があります。したがって、最悪の場合、重複をカウントしなくても、線形時間で個別の要素をカウントするアルゴリズムはありません。

ただし、別の計算モデルで実行できるかどうかは明確ではありません。それは、合理的な決定論的計算モデルではありそうにありません。


これは本当に要素の明確性の問題のインスタンスですか?タプルを生成するだけで、一意性をチェックする必要はありません。異論はない、好奇心旺盛。
mascoj

2
私が言っていることは、異なる要素のタプルを生成できる場合、タプルのサイズがであるかどうかをチェックすることによって、要素の一意性の問題を解決することもできます。n
John L.

よかった。ありがとう
mascoj

1

予想される実行時間がO(n)ランダム化されたアルゴリズムが存在します。またはここで、走行時間がより長くかかる確率cnに指数関数的に小さいc

特に、2つのユニバーサルハッシュ関数をランダムに選択し、それを使用して配列のすべての要素をハッシュします。これにより、2ユニバーサルハッシュの出力の長さを適切に選択した場合に、指定された実行時間が達成されます。

別の例として、あなたはその時間を実行している最悪のケースである無作為化アルゴリズム構築することができますO(n)(それは常に、どんな線形時間で実行されていない)と最大でのエラーの確率が持っている1/2100。(方法?上記のアルゴリズムを実行し、適切に選択されたcに対してcnステップより長く実行された場合は終了します。)実際には、宇宙線が原因でコンピュータが誤った答えを出力する可能性があるため、これで十分です。はるかに高い1 / 2 100c1/2100


1

アプローチ3は、Aho、Hopcroft、およびUllman(1974) 2.12を実行するソリューションを使用して安全にすることができます。たとえば、楽しみと利益のために初期化さていないメモリを使用するで説明されているコンピュータアルゴリズムの設計と分析

基本的に、カウントを持つN要素の配列に加えて、N要素の2つの配列と1つの補助カウントがあり、有効なカウントを示すスパースセットを作成します。

Cのような擬似コード:

uint* a = malloc(n);
uint* b = malloc(n);
uint* c = malloc(n);
uint len = 0;

get_count(uint x) {
    uint idx = a[x];
    return idx >= 0 && idx < len && b[idx] == x ? c[idx] : 0;
}

increment_count(uint x) {
    uint idx = a[x];
    if (idx < 0 || idx >= len || b[idx] != x) {
        idx = len;
        len++;
        a[x] = idx;
        b[idx] = x;
        c[idx] = 0;
    }
    c[idx]++;
}

スパースセットの実用的な実装については、このStackOverflowの回答で説明しています


PS cxまたはidxでインデックスを作成できますが、idxキャッシュの局所性を高めるために使用しました。
Peter Taylor

私は答えが好きですが、これが安全になる理由について混乱しています。ありそうもないことですが、メモリセルにアクセスすることはできませんでした。メモリセルには、奇跡的に「有効な」エントリがありませんでした。mallocで不運になっただけですか?
ライアン

1
1..uuO(1)

@ryan、参照research.swtch.com/sparseそれが安全になります何のために。それは間違いなく非常に巧妙なトリックです。
DW

3u+1u{a,b,c,len}cu=5123=134217728(3×512+1)(1+2k)k
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.