はじめに
ソートアルゴリズムの実装は代入/比較/繰り返しなど、プログラミングを学ぶ上での基本がきっちり詰まっていて、ビギナーのトレーニングには格好の題材です。簡単なソートアルゴリズムの代表格といえばバブルソートと選択ソートでしょうか。
この記事では、選択ソートアルゴリズムを「STL」(C++の標準テンプレートライブラリ;Standard Template Library)で実装し、インテルが公開しているマルチコアCPU向けのC++テンプレートライブラリ「インテル スレッディング・ビルディング・ブロック」(インテルTBB)を使ってマルチスレッドによる高速化を試みます。
選択ソートのSTLによる実装
選択ソート(selection sort)のアルゴリズムはバブルソート以上に単純です。配列 data[N] を昇順にソートするには
data[i]~data[N-1]の中から最も小さい要素を探し、 それとdata[i]とを交換する
を i = 0~N-1 に対してくり返すだけ。data[0]~data[N-1]中の最小要素とdata[0]とを交換すれば、当然data[0]が最小となります。次に(data[0]を除いた)data[1]~data[N-1]中の最小要素をdata[1]と交換するのでdata[1]は2番目に小さい要素となります。以下この操作を繰り返せばソート完了という次第。
STLの<algorithm>には最も小さい要素(を指すイテレータ)を返すmin_element、および2つの要素をイテレータを介して交換するiter_swapがありますから、選択ソートの実装はきわめて簡単です。
// 選択ソート template<typename Iterator> void selection_sort(Iterator first, Iterator last) { while ( first != last ) { // [first,last)中の最小要素をfirstと交換 std::iter_swap(first, std::min_element(first,last)); ++first; } }
10行足らずで書けてしまう選択ソート、性能はいかがなものでしょう。お試しに文字列の並び、vector<string> をソートしましょうか。前準備としてソートアルゴリズムを関数オブジェクトに仕立てておきます。
class sort_base { public: typedef std::vector<std::string>::iterator iterator; protected: iterator first_; iterator last_; virtual void do_sort() const =0; // ←導出クラスではコレを再定義して処理を書く public: sort_base(iterator first, iterator last) : first_(first), last_(last) {} void operator()() const { do_sort(); // ソートして... // 昇順に並んでいることを確認 assert( std::adjacent_find(first_, last_, std::greater<std::string>()) == last_ ); } }; /* * 至極真っ当な選択ソート */ class normal_sort : public sort_base { public: normal_sort(iterator first, iterator last) : sort_base(first,last) {} protected: virtual void do_sort() const { selection_sort(first_, last_); } }; // 関数オブジェクトを評価(実行)し、処理時間を測定する void execute(const sort_base& sort_fn) { tbb::tick_count start = tbb::tick_count::now(); sort_fn(); std::cout << (tbb::tick_count::now() - start).seconds() << " [sec]\n"; } int main() { std::vector<std::string> source; std::vector<std::string> input; { // ソート対象を生成。 // A..Hの順列(8!個)を用意し、 std::string value = "ABCDEFGH"; do { source.push_back(value); } while ( std::next_permutation(value.begin(), value.end()) ); // その順序をかき混ぜる。 std::random_shuffle(source.begin(),source.end()); } { // フツーに選択ソート input = source; std::cout << "normal_sort ....... " << std::flush; execute(normal_sort(input.begin(), input.end())); } }