これは長文です。我慢してください。要約すると、問題は次のとおりです。実行可能なインプレース基数ソートアルゴリズムはありますか?
予備
文字「A」、「C」、「G」、「T」のみを使用する小さな固定長の文字列が大量にあり(そう、ご想像のとおり: DNA )、それらを並べ替えます。
現時点では、 STLのすべての一般的な実装で、std::sort
which uses introsortを使用しています。これは非常にうまく機能します。しかし、基数ソートは問題セットに完全に適合し、実際にははるかにうまく機能するはずだと確信しています。
詳細
私はこの仮定を非常に単純な実装でテストしましたが、比較的小さな入力 (10,000 のオーダー) の場合、これは真実でした (まあ、少なくとも 2 倍以上高速でした)。ただし、問題のサイズが大きくなると ( N > 5,000,000)、実行時間は大幅に低下します。
その理由は明らかです。基数ソートでは、データ全体をコピーする必要があります (実際には、私の単純な実装では複数回)。これは、メイン メモリに最大 4 GiB を入れたことを意味し、明らかにパフォーマンスが低下します。そうでなかったとしても、実際には問題のサイズがさらに大きくなるため、これほど多くのメモリを使用する余裕はありません。
ユースケース
理想的には、このアルゴリズムは、DNA と DNA5 (追加のワイルドカード文字「N」を許可する)、またはIUPAC 曖昧コードを含む DNA (結果として 16 の異なる値) に対して、2 から 100 の間の任意の文字列長で動作する必要があります。ただし、これらすべてのケースをカバーすることはできないことを認識しているため、速度が向上したことに満足しています。コードは、どのアルゴリズムにディスパッチするかを動的に決定できます。
リサーチ
残念ながら、基数ソートに関するウィキペディアの記事は役に立ちません。インプレース バリアントに関するセクションは完全にゴミです。基数ソートに関するNIST-DADS セクションはほとんど存在しません。アルゴリズム「MSL」を説明するEfficient Adaptive In-Place Radix Sortingという有望な論文があります。残念ながら、この論文も残念です。
具体的には、次のようなものがあります。
まず、アルゴリズムにはいくつかの誤りがあり、多くのことが説明されていません。特に、再帰呼び出しについては詳しく説明していません (現在のシフト値とマスク値を計算するために、ポインターをインクリメントまたは削減すると仮定しています)。また、関数dest_group
anddest_address
を定義せずに使用します。これらを効率的に実装する方法がわかりません (つまり、O(1) では、少なくともdest_address
自明ではありません)。
最後になりましたが、このアルゴリズムは、配列インデックスを入力配列内の要素と交換することにより、インプレース性を実現します。これは明らかに数値配列でのみ機能します。文字列で使用する必要があります。もちろん、強力な型付けを台無しにして、インデックスが属していない場所にインデックスを格納することをメモリが許容できると仮定して先に進むこともできます。しかし、これは、文字列を 32 ビットのメモリに圧縮できる場合にのみ機能します (32 ビット整数を想定)。それはわずか 16 文字です (16 > log(5,000,000) であることは今のところ無視しましょう)。
著者の 1 人による別の論文では、正確な説明がまったく提供されていませんが、MSL のランタイムがサブリニアであり、完全に間違っています。
要約すると、動作する参照実装、または少なくとも DNA 文字列で動作する動作中の基数ソートの適切な疑似コード/説明を見つける希望はありますか?