アルゴリズムで遊ぶ楽しみのために; 元の配列が大きい場合(単純なアルゴリズムを非実用的にするのに十分O(n^2)
であり、OPはジェネリックを含むバージョンのDelphiを使用しているため、ソートせずにTDictionary<string, integer>
すべての文字列を追跡し、重複を識別するためにを使用することを提案します。
これは、いくつかの理由で効率的です。
- TDictionaryは一定時間の挿入を提供します、それはほとんど
O(n)
です。アレイのサイズは最初からわかっているので、TDictionary
大きくすることなく使用できます。これにより、アルゴリズム全体が作成されますO(n)
。
- 文字列はすでに配列内にあり、Delphi文字列は参照カウントされるため、文字列を配列に入れても実際には文字列がコピーされません。
- このアルゴリズムでは、アレイは1回だけウォークされます。
コード:
type
TZeroWidthRecord = record
end;
function FindFirstDuplicate(const ArrayOfString: array of string): Integer;
var Dict: TDictionary<string, TZeroWidthRecord>;
i: Integer;
ZW: TZeroWidthRecord;
begin
Dict := TDictionary<string, TZeroWidthRecord>.Create(Length(ArrayOfString));
try
for i:=0 to High(ArrayOfString) do
try
Dict.Add(ArrayOfString[i], ZW);
except on E:Exception do
Exit(i);
end;
finally Dict.Free;
end;
// No duplicates found:
Result := -1;
end;
Davidのコメントに答えるために、このTDictionary
ベースのアルゴリズムをアルゴリズムと比較する短いテストプログラムを作成しましたsort-and-search
。文字列のランダム配列を作成してから、最初の重複を見つけてみました。私のアレイには重複が含まれていません。重複があった場合、TDictionaryの実行時間は平均的な最初のヒットの重複に比例します。たとえば、平均してTDictionaryがアレイの中央で重複を検出した場合、TDictアルゴリズムの平均実行時間は半分になります。ソートベースのアルゴリズムは配列全体をソートする必要があり、ソートは最も時間がかかるものです。
ソートや辞書ベースのアルゴリズムと同様に、現実的なデータでテストする必要があります。たとえば、OPの質問で短い文字列を使用してテストした場合、TDictとsortの間に競合は発生しません。Dictは、些細な長さの配列でも高速になります。しかし、平均文字列長が増加すると、ソートベースのアルゴリズムが改善され始めます。ただし、これも文字列によって異なります。たとえば、ほとんどの文字列が長いプレフィックスを共有する場合、並べ替えアルゴリズムの「比較」段階にかなり時間がかかり、TDictionaryの見栄えが再び良くなります。
テストテーブル1、重複なし
*==========*===========================*
| | Number of strings |
| Avg str | in the Array for |
| length | TDictionary to be faster |
*======================================*
| 7 | 33 |
| 10 | 73 |
| 13 | 73 |
| 16 | 163 |
| 19 | 163 |
| 22 | 366 |
| 25 | 366 |
| 28 | 549 |
| 37 | 2776 |
| 40 | 2776 |
| 43 | 2776 |
| 46 | 4164 |
| 49 | 9369 |
| 52 | 9369 |
| 55 | 9369 |
| 58 | 21079 |
*==========*===========================*
テストテーブル2、アレイの1/2で複製
これは、最初の重複がアレイの真ん中に見つかった場合の結果になります。平均文字列長58の大きな違いに注意してください。
*==========*===========================*
| | Number of strings |
| Avg str | in the Array for |
| length | TDictionary to be faster |
*======================================*
| 30 | 109 |
| 33 | 163 |
| 36 | 163 |
| 58 | 366 |
*==========*===========================*
テストテーブル3、1/4で複製
そして、これは、最初の重複がアレイの約1/4で見つかった場合に発生します。
*==========*===========================*
| | Number of strings |
| Avg str | in the Array for |
| length | TDictionary to be faster |
*======================================*
| 29 | 73 |
| 32 | 73 |
| 38 | 73 |
| 57 | 109 |
*==========*===========================*
これがテストアプリケーションです:http://pastebin.com/vDznwKtZ