Quicksortを利用して配列のソートを行っているのですが、
配列の数が多くなるとスタックオーバーフローを起こしてしまいます。
おそらく、再帰処理が原因だと思うのですが、
再帰させないようなQuicksortのアルゴリズム、
またquicksort並みの速度を持つソート法を教えていただけないでしょうか。
クイックソートと同等のソート方法は、ヒープソート、マージソートだそうです。
ヒープソートの場合、スタックを必要としません。
状況や環境に依存するでしょうが、
物理メモリ512Mの私のPCでは
N=50000000のIntegerでは無事ソートできました。
その2倍はしばらく待ったけど帰ってきませんでした^^;
あまり詳しくは無いのですが、クイックソートは逆整列に近いデータだと恐らく莫大なメモリを消費するのではないでしょうか。
逆整列よりも、規則的に折り混ざった状態でしょう。
クイックソートの速度は、O(n*log(n))になりますが、一番遅い場合は、O(2*N)になったと思います。
再帰処理でなく、スタックを自前で用意してやるというのも手です。
CMAGAZINEの「アルゴリズムとデータ構造」から
「コームソート」を移植。
オーダは N LOG N だそうですがクイックソートより遅い気がします。
スタックの消費は少ないです(たぶん)
>一番遅い場合は、O(2*N)
O(N^2)ですよね^^
※const で Nを定義してください
procedure CombSort(var a: array of Integer);
var
i, temp, flag, gap: Integer;
begin
gap := N;
repeat
gap := (gap * 10) div 13;
(* 収縮率は1.3(gapが毎回1/1.3になる) *)
if (gap = 0) then
gap := 1;
flag := 1;
(* 先頭から順に見ていって *)
i := 0; (*前に移動*)
while (i < N - gap) do
begin
(* 距離がgapだけ離れた要素を比較し, 並びがおかしければ *)
if (a[i] > a[i + gap]) then
begin
(* 入れ替える *)
flag := 0;
temp := a[i];
a[i] := a[i + gap];
a[i + gap] := temp;
end;
Inc(i);
end;
(* 1度も並べ替えをせずに,gap=1で見終わったら終了 *)
until ((gap <= 1) and (flag = 1)); //trueで終了
end;
にしのさん、ウォレスさん
コメントありがとうございます。
しばらくネットにつなげない状況で、返信が遅れてしまいすみません。
ウォレスさんのコームソートを早速試させていただきました。
コードまで書いていただいて、ありがとうございます。
試してみたところ、クイックソートではエラーを起こしていた
配列の数でも無事にソートすることができました。
ただ、ウォレスさんもおっしゃっているように、
やはり速度がかなり遅くなってしまいます。
また、にしのさんの「スタックを自前で用意する」というのは
どういうことなのでしょうか?
クイックソートの速度のまま、
なんとかソートを行いたいと思っています。
何か方法があれば教えていただきたいと思います。
よく知りませんが、プロジェクト/オプション/リンカ の最大スタックサイズを
大きくしたら駄目ですか?
コームソート(コムソートの方が正しい表記?)はコード・理屈が極めて平易であるのにオーダが N LOG N である事から紹介しました。
ヒープソートは速度的にはコムソートと変わらないのではないでしょうか。
(試していません)
非再帰的なクイックソートが「C言語による最新アルゴリズム事典」に載っていますね。
スタックの代わりを自前で用意するので結局速度は遅くなる気がしますが・・・
(試していませんよ)
非再帰型クイックソート
出典「C言語による最新アルゴリズム事典」
区間がTHRESHOLD未満で挿入ソートに切り替えるみたい^^;
速度は再帰型とかわらないみたい。
※挿入ソートを持ってくればさらに高速に。
const で以下を定義。
THRESHOLD =0;
STACKSIZE =32; (* たかだか Integer のビット数程度 *)
//----------------------
procedure nonrecursiveqsort( var a : array of Integer );
var
i, j, left, right, p :Integer;
leftstack : array[0..STACKSIZE-1] of integer;
rightstack : array[0..STACKSIZE-1] of integer;
x, t :Integer;
begin
left := 0; right := N - 1; p := 0;
while( true ) do begin
if (right - left <= THRESHOLD)then begin
if (p = 0)then break;
dec(p);
left := leftstack[p];
right := rightstack[p];
end;
x := a[(left + right) div 2];
i := left; j := right;
while( true ) do begin
while (a[i] < x)do inc(i);
while (x < a[j])do dec(j);
if (i >= j)then break;
t := a[i]; a[i] := a[j]; a[j] := t;
inc(i); dec(j);
end;
if (i - left > right - j)then begin
if (i - left > THRESHOLD)then begin
leftstack[p] := left;
rightstack[p] := i - 1;
inc(p);
end;
left := j + 1;
end else begin
if (right - j > THRESHOLD)then begin
leftstack[p] := j + 1;
rightstack[p] := right;
inc(p);
end;
right := i - 1;
end;
end;
//inssort(N, a);
end;
ツイート | ![]() |