[迷信] データ列のソートには qsort 関数を使うべし
ソートのアルゴリズムにはいろいろありますが、中でもクイックソートは比較的高速なアルゴリズムです。その名前からも分かるように、qsort 関数はクイックソートの実装であることを示唆しています。実際には、規格上は qsort 関数にクイックソートを用いて実装することが要求されているわけではないので何でもよいのですが、多くの場合はクイックソートを用いていると考えられます。
標準Cライブラリでは、ソートのための関数は qsort 関数しかないため、qsort 関数は最も手軽に使用できるソート方法です。しかし、いくつかの状況では、qsort 関数は使えませんし、使えたとしても好ましくありません。以下に、qsort 関数を使うべきでは状況を挙げてみます。
qsort 関数が使えない状況
データ列が配列ではない場合
qsort 関数はデータ列が配列であることを前提としています。線形リストであったり、ファイル上のデータであったりする場合には使えません。いったん配列に読み込んでから qsort でソートし、書き戻すことは可能ですが、多くの場合メリットがありません。マージソートなど、データ構造や格納方法に適した他の方法を用いて、ソートを自作する方がよいでしょう。なお、C++の std::list の場合、メンバ関数 sort を利用できます。
安定なソートが必要な場合
クイックソートは安定ではありません。すなわち、比較して値が等しい要素がどの順序になるかは不定です。比較して値が等しい要素が元の順序を保つ必要がある場合には不適切です。C++では、安定ソートには std::stable_sort 関数テンプレートを利用することができます。
スタックが小さい場合
クイックソートは多くの場合、再帰呼出しを用いて実装されます。あるいは、再帰を用いない場合はあらかじめ大きな配列を自動変数として確保します。ローエンドの組込み機器など、スタックサイズの制限が厳しい環境では、ヒープソートなど、他の方法を選択する方が適しています。処理系がそのことを配慮して qsort 関数を実装している場合は問題ありませんが、その場合でも移植を行う場合は要注意です。
フリースタンディング環境の場合
フリースタンディング環境では qsort 関数がサポートされません。処理系によっては提供されていることもありますが、移植性を考慮するなら使用すべきではないでしょう。
qsort 関数が好ましくない状況
計算時間を予測したい場合
リアルタイム性が要求される状況など、計算時間を予測したい場合には、データに依存して計算時間が大幅に変化するクイックソートは不向きです。クイックソートの計算時間は、平均的には O(n log n) ですが、最悪計算時間は O(n2) であり、これはバブルソートの計算時間と同じです。リアルタイム性を保証するには、最悪計算時間に合わせるしかないため、クイックソートの利用は好ましくありません。ヒープソートなどを用いて自作すべきでしょう。
C++の場合
C++では、実装にもよりますが、qsort 関数より std::sort 関数テンプレートを用いる方が圧倒的に高速です。qsort は比較関数のコールバックがかなり大きなオーバーヘッドになります。一方、C++の std::sort では、比較に関数オブジェクトを使えますので、結果としてインライン置換することが可能になります。
また、配列の要素が C 互換型(POD 型)であればよいのですが、そうでなければ、qsort 関数を使うと不具合につながります。なぜなら、qsort 関数では、要素の交換を単なるメモリブロックの交換として行っているだけであり、適切な swap 関数を用いたり、コピーコンストラクタを呼び出したり、といったことは一切ないからです。
実は、今回「データ列のソートには qsort 関数を使うべし」を迷信として採り上げたのは、 C++ の場合に関しての理解不足をよく見かけるからです。先に挙げた(C++ 以外の)状況で qsort 関数を使うべきでないことは、少し経験を積んだプログラマであれば容易に判断がつきます。しかし、単に C++ になっただけの場合には、つい std::sort ではなく qsort を使ってしまうものです。
この記事のトラックバックURL:
ブックナビゲーション
- 技術情報
- Boost C++ Libraries メモ
- C++と組込み環境
- C++サンプル集
- C++テンプレート集
- C++プログラマのためのC言語入門
- C/C++迷信集
- [迷信] 'A'~'Z' の値は連続している
- [迷信] 0xe-0xe はゼロ
- [迷信] 1 バイトは 8 ビット
- [迷信] 2の累乗による割り算と右シフトは等価
- [迷信] FILE 型は構造体
- [迷信] abs は常に非負の値を返す
- [迷信] argv[0] はプログラム名
- [迷信] char 型は符号付き
- [迷信] double の出力書式は "%lf"
- [迷信] fflush で入力バッファをクリア
- [迷信] free でメモリを開放する
- [迷信] free に NULL を渡すとクラッシュする
- [迷信] gets は単純に fgets に置き換えられる
- [迷信] isalpha 関数の引数は char 型
- [迷信] new に失敗すると NULL が返る。
- [迷信] scanf ではバッファオーバーランを防げない
- [迷信] scanf でキーボードから入力
- [迷信] setjmp マクロの返却値は変数に代入できる
- [迷信] sizeof は定数式
- [迷信] void main(void)
- [迷信] とりあえず memset で初期化
- [迷信] アルゴリズム関数内で関数オブジェクトはコピーされない
- [迷信] オブジェクトの動的生成に失敗するとメモリリークする
- [迷信] コンストラクタから例外を送出してはならない
- [迷信] コンストラクタで自身をゼロクリア
- [迷信] コンパイラはプログラマの心を察してくれる
- [迷信] コンパイルエラーが出るのでアクセス指定子を修正
- [迷信] ソースコード中の即値を全廃せよ
- [迷信] ソースファイルの末尾に }
- [迷信] データ列のソートには qsort 関数を使うべし
- [迷信] プログラムは必ず main から始まる
- [迷信] 一重引用符の中には一文字しか書けない
- [迷信] 今どき int が 16 ビットの処理系なんて無い
- [迷信] 入力データ格納用配列のサイズは BUFSIZ
- [迷信] 割付けたメモリはプログラマが自分で解放しなければならない
- [迷信] 実数型とは浮動小数点型のことである
- [迷信] 引用符で囲んだヘッダ名はカレントディレクトリから探索する
- [迷信] 文字列から整数への変換には atoi
- [迷信] 構造体のタグ名は下線で始める
- [迷信] 構造体はクラスではない
- [迷信] 識別子に使える文字は英数字と下線のみ
- [迷信] 非局所オブジェクトは外部結合
- C99関数・マクロ・前処理スクリプト集
- C言語再入門
- C言語徹底入門
- Drupal メモ
- TOPPERS 情報
- ベターCとしてのC++
- マイコン メモ
- ライブラリ開発入門
- 分割コンパイルをきわめる
- 擬似プロセッサを作る
- 象の卵を探して...
- 車輪の再発明
- 過去の情報

