情報学部 菅沼ホーム 全体目次

ソート(並べ替え,C/C++ )

      1. メルセンヌ・ツイスタ
      2. バブルソート
      3. 選択ソート
      4. バケツソート
      5. ヒープソート
      6. クイックソート

  1. メルセンヌ・ツイスタ

      以下に示すプログラムでは乱数を使用していますが,C/C++ における乱数生成関数 srand,rand は,16 ビットを使用しているためその周期が短く(最大でも,32767 ),あまり質が良い乱数を生成してくれません.ここでは,以下に示すメルセンヌ・ツイスタ( MT.h をインクルード)を使用しています.メルセンヌ・ツイスタ法を使用した乱数生成関数が,標準ライブラリ内に含まれています.
    /*
       A C-program for MT19937, with initialization improved 2002/1/26.
       Coded by Takuji Nishimura and Makoto Matsumoto.
    
       Before using, initialize the state by using init_genrand(seed)  
       or init_by_array(init_key, key_length).
    
       Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
       All rights reserved.                          
    
       Redistribution and use in source and binary forms, with or without
       modification, are permitted provided that the following conditions
       are met:
    
         1. Redistributions of source code must retain the above copyright
            notice, this list of conditions and the following disclaimer.
    
         2. Redistributions in binary form must reproduce the above copyright
            notice, this list of conditions and the following disclaimer in the
            documentation and/or other materials provided with the distribution.
    
         3. The names of its contributors may not be used to endorse or promote 
            products derived from this software without specific prior written 
            permission.
    
       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
       LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
       NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
       SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    
    
       Any feedback is very welcome.
       http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
       email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
    */
    
    /*
       The original version of http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.c was modified by Takahiro Omi as
       - delete line 47 "#include<stdio.h>"
       - delete line 174 int main(void){...}
       - change N -> MT_N
       - change N -> MT_N
       - change the file name "mt19937ar.c" -> "MT.h"
    */
    
    
    /* Period parameters */  
    #define MT_N 624
    #define MT_M 397
    #define MATRIX_A 0x9908b0dfUL   /* constant vector a */
    #define UPPER_MASK 0x80000000UL /* most significant w-r bits */
    #define LOWER_MASK 0x7fffffffUL /* least significant r bits */
    
    static unsigned long mt[MT_N]; /* the array for the state vector  */
    static int mti=MT_N+1; /* mti==MT_N+1 means mt[MT_N] is not initialized */
    
    /* initializes mt[MT_N] with a seed */
    void init_genrand(unsigned long s)
    {
        mt[0]= s & 0xffffffffUL;
        for (mti=1; mti<MT_N; mti++) {
            mt[mti] = 
    	    (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); 
            /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
            /* In the previous versions, MSBs of the seed affect   */
            /* only MSBs of the array mt[].                        */
            /* 2002/01/09 modified by Makoto Matsumoto             */
            mt[mti] &= 0xffffffffUL;
            /* for >32 bit machines */
        }
    }
    
    /* initialize by an array with array-length */
    /* init_key is the array for initializing keys */
    /* key_length is its length */
    /* slight change for C++, 2004/2/26 */
    void init_by_array(unsigned long init_key[], int key_length)
    {
        int i, j, k;
        init_genrand(19650218UL);
        i=1; j=0;
        k = (MT_N>key_length ? MT_N : key_length);
        for (; k; k--) {
            mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
              + init_key[j] + j; /* non linear */
            mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
            i++; j++;
            if (i>=MT_N) { mt[0] = mt[MT_N-1]; i=1; }
            if (j>=key_length) j=0;
        }
        for (k=MT_N-1; k; k--) {
            mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
              - i; /* non linear */
            mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
            i++;
            if (i>=MT_N) { mt[0] = mt[MT_N-1]; i=1; }
        }
    
        mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ 
    }
    
    /* generates a random number on [0,0xffffffff]-interval */
    unsigned long genrand_int32(void)
    {
        unsigned long y;
        static unsigned long mag01[2]={0x0UL, MATRIX_A};
        /* mag01[x] = x * MATRIX_A  for x=0,1 */
    
        if (mti >= MT_N) { /* generate N words at one time */
            int kk;
    
            if (mti == MT_N+1)   /* if init_genrand() has not been called, */
                init_genrand(5489UL); /* a default initial seed is used */
    
            for (kk=0;kk<MT_N-MT_M;kk++) {
                y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
                mt[kk] = mt[kk+MT_M] ^ (y >> 1) ^ mag01[y & 0x1UL];
            }
            for (;kk<MT_N-1;kk++) {
                y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
                mt[kk] = mt[kk+(MT_M-MT_N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
            }
            y = (mt[MT_N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
            mt[MT_N-1] = mt[MT_M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
    
            mti = 0;
        }
      
        y = mt[mti++];
    
        /* Tempering */
        y ^= (y >> 11);
        y ^= (y << 7) & 0x9d2c5680UL;
        y ^= (y << 15) & 0xefc60000UL;
        y ^= (y >> 18);
    
        return y;
    }
    
    /* generates a random number on [0,0x7fffffff]-interval */
    long genrand_int31(void)
    {
        return (long)(genrand_int32()>>1);
    }
    
    /* generates a random number on [0,1]-real-interval */
    double genrand_real1(void)
    {
        return genrand_int32()*(1.0/4294967295.0); 
        /* divided by 2^32-1 */ 
    }
    
    /* generates a random number on [0,1)-real-interval */
    double genrand_real2(void)
    {
        return genrand_int32()*(1.0/4294967296.0); 
        /* divided by 2^32 */
    }
    
    /* generates a random number on (0,1)-real-interval */
    double genrand_real3(void)
    {
        return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0); 
        /* divided by 2^32 */
    }
    
    /* generates a random number on [0,1) with 53-bit resolution*/
    double genrand_res53(void) 
    { 
        unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6; 
        return(a*67108864.0+b)*(1.0/9007199254740992.0); 
    } 
    /* These real versions are due to Isaku Wada, 2002/01/09 added */
    			

  2. バブルソート

      ここでは,100,000 個の整数データを乱数によって生成し,それを昇順及び降順にソートし,データの生成時間( data の後ろ)及びソートに要した時間( sort の後ろ)を出力しています.なお,昇順及び降順は,関数名を引数として渡すことによって制御しています.

    1. 配列 data に n 個のデータが入っているものとする. i = 0, 1, ・・・, n-1

    2. i 番目のデータと i+1 番目のデータを比較し,もし,

      data[i+1] < data[i] (昇順の場合)

      であればそれらのデータを交換する. i = 0, 1, ・・・, n-2

      → この結果,data[n-1] に最も大きなデータが入ることになる

    3. 次に,n-1 個のデータに対し上の処理を繰り返す.

    4. 同様に,n-2,n-3,・・・ 個のデータに対して同じ処理を繰り返す
    /****************************/
    /* バブルソート             */
    /*      coded by Y.Suganuma */
    /****************************/
    #include <stdio.h>
    #include <time.h>
    #include "MT.h"
    
    /*****************************/
    /* データの比較(昇順)      */
    /* a > bの場合に0以外(真) */
    /*****************************/
    int ascend(int a, int b)
    {
    	return a > b;
    }
    
    /*****************************/
    /* データの比較(降順)      */
    /* a < bの場合に0以外(真) */
    /*****************************/
    int descend(int a, int b)
    {
    	return a < b;
    }
    
    /*************************************/
    /* 2つのデータを交換する            */
    /*      a,b : 2つのデータのアドレス */
    /*************************************/
    void swap(int *a, int *b)
    {
    	int temp = *a;
    	*a       = *b;
    	*b       = temp;
    }
    
    /*********************************/
    /* バブルソート                  */
    /*      n : データの数           */
    /*      data : データ            */
    /*      compare : 比較を行う関数 */
    /*********************************/
    void bubble(int n, int *data, int (*compare)(int, int))
    {
    	int sw = 0;
    	if (n > 1) {
    		for (int i1 = 0; i1 < n-1; i1++) {
    			if (compare(data[i1], data[i1+1])) {
    				sw = 1;
    				swap(&data[i1], &data[i1+1]);
    			}
    		}
    	}
    
    	if (sw > 0)
    		bubble(n-1, data, compare);
    }
    
    #define N 100000
    
    int main()
    {
    			// 初期設定
    	int *data = new int [N];
    	init_genrand((unsigned)time(NULL));
    			// 昇順
    	clock_t tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	clock_t tm2 = clock();
    	bubble(N, data, ascend);
    	clock_t tm3 = clock();
    	printf("昇順(データ数: %d)\n", N);
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    			// 降順
    	tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	tm2 = clock();
    	bubble(N, data, descend);
    	tm3 = clock();
    	printf("降順(データ数: %d)\n", N);
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    
    	return 0;
    }
    			
    (出力)
    昇順(データ数: 100000)
       data 0 ms
       sort 33453 ms
    降順(データ数: 100000)
       data 0 ms
       sort 33148 ms
    			

  3. 選択ソート

      ここでも,バブルソートと同様の処理を行っています.また,選択ソートの手順は以下に示すとおりです.

    1. 配列を走査して,最も小さな要素を見つけだす(昇順の場合).

    2. 見つけたら,それを配列の先頭要素と交換する.

    3. 上記の操作を配列の 2 番目の要素から始まるサブ配列について繰り返す.
    /****************************/
    /* 選択ソート               */
    /*      coded by Y.Suganuma */
    /****************************/
    #include <stdio.h>
    #include <time.h>
    #include "MT.h"
    
    /*****************************/
    /* データの比較(昇順)      */
    /* a > bの場合に0以外(真) */
    /*****************************/
    int ascend(int a, int b)
    {
    	return a > b;
    }
    
    /*****************************/
    /* データの比較(降順)      */
    /* a < bの場合に0以外(真) */
    /*****************************/
    int descend(int a, int b)
    {
    	return a < b;
    }
    
    /*************************************/
    /* 2つのデータを交換する            */
    /*      a,b : 2つのデータのアドレス */
    /*************************************/
    void swap(int *a, int *b)
    {
    	int temp = *a;
    	*a       = *b;
    	*b       = temp;
    }
    
    /*********************************/
    /* 選択ソート                    */
    /*      n : データの数           */
    /*      data : データ            */
    /*      compare : 比較を行う関数 */
    /*********************************/
    void select(int n, int *data, int (*compare)(int, int))
    {
    	int i1, m;
    
    	if (n > 1) {
    
    		m = 0;
    		for (i1 = 1; i1 < n; i1++) {
    			if (compare(data[m], data[i1]))
    				m = i1;
    		}
    
    		if (m != 0)
    			swap(&data[0], &data[m]);
    
    		select(n-1, &data[1], compare);
    	}
    }
    
    #define N 100000
    
    int main()
    {
    			// 初期設定
    	int *data = new int [N];
    	init_genrand((unsigned)time(NULL));
    			// 昇順
    	clock_t tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	clock_t tm2 = clock();
    	select(N, data, ascend);
    	clock_t tm3 = clock();
    	printf("昇順(データ数: %d)\n", N);
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    			// 降順
    	tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	tm2 = clock();
    	select(N, data, descend);
    	tm3 = clock();
    	printf("降順(データ数: %d)\n", N);
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    
    	return 0;
    }
    			
    (出力)
    昇順(データ数: 100000)
       data 0 ms
       sort 13704 ms
    降順(データ数: 100000)
       data 1 ms
       sort 13813 ms
    			

  4. バケツソート

      バケツソートとは,一次元配列に入っている n 個の正の整数をバケツ配列と呼ばれる 10 × (n-1) の二次元配列を使用してソートする方法であり,以下に示すような手続きに従がいます.

    1. 一次元配列の各数値を一の位に基づいてバケツ配列の対応する各行に格納する.たとえば,一次元配列の中に数値 97,3,100 がこの順序で入っていたとすると,97 は行 7,3 は行 3,100 は行 0 に格納される.これを「分配パス」と呼ぶ.

    2. バケツ配列の各行に格納されている値を行 0 から順番に元の一次元配列の戻す.これを「収集パス」という.この時点で,一次元配列の中の値は 100,3,97 の順序になる.

    3. 以上の処理を数値の各位について繰り返す.
    /****************************/
    /* バケツソート             */
    /*      coded by Y.Suganuma */
    /****************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include "MT.h"
    
    /*****************************/
    /* データの比較(昇順)      */
    /* a > bの場合に0以外(真) */
    /*****************************/
    int ascend(int a, int b)
    {
    	return a > b;
    }
    
    /*****************************/
    /* データの比較(降順)      */
    /* a < bの場合に0以外(真) */
    /*****************************/
    int descend(int a, int b)
    {
    	return a < b;
    }
    
    /*************************************/
    /* 2つのデータを交換する            */
    /*      a,b : 2つのデータのアドレス */
    /*************************************/
    void swap(int *a, int *b)
    {
    	int temp = *a;
    	*a       = *b;
    	*b       = temp;
    }
    
    /********************************************/
    /* バケツソート                             */
    /*      n : データの数                      */
    /*      data : データ                       */
    /*      cp : 比較方法(=0 : 昇順,=1 : 降順 */
    /*      k : 桁                              */
    /*      bu : バケツ                         */
    /*      num : 各バケツに入っているデータ数  */
    /********************************************/
    void bucket(int n, int *data, int cp, int k, int **bu, int *num)
    {
    	int kk = 1, sw1 = 1, sw2 = 0;
    			// 準備
    	for (int i1 = 0; i1 < k-1; i1++)
    		kk *= 10;
    	for (int i1 = 0; i1 < 10; i1++)
    		num[i1] = 0;
    			// 分配パス
    	for (int i1 = 0; i1 < n; i1++) {
    		if (data[i1] >= 10*kk)
    			sw2 = 1;
    		int k1 = (data[i1] / kk) % 10;
    		if (num[k1] >= n-1)
    			sw1 = 0;
    		else {
    			bu[k1][num[k1]] = data[i1];
    			num[k1]++;
    		}
    	}
    			// 収集パス
    	if (sw1 > 0) {
    		int k1 = 0;
    		if (cp == 0) {
    			for (int i1 = 0; i1 < 10; i1++) {
    				for (int i2 = 0; i2 < num[i1]; i2++) {
    					data[k1] = bu[i1][i2];
    					k1++;
    				}
    			}
    		}
    		else {
    			for (int i1 = 9; i1 >= 0; i1--) {
    				for (int i2 = 0; i2 < num[i1]; i2++) {
    					data[k1] = bu[i1][i2];
    					k1++;
    				}
    			}
    		}
    	}
    			// 次の桁
    	if (k < 10 && sw2 > 0)
    		bucket(n, data, cp, k+1, bu, num);
    }
    
    #define N 5000000
    
    int main()
    {
    			// 初期設定
    	int *data = new int [N];
    	int *num  = new int [10];
    	int **bu  = new int * [10];
    	for (int i1 = 0; i1 < 10; i1++)
    		bu[i1] = new int [N];
    	init_genrand((unsigned)time(NULL));
    			// 昇順
    	clock_t tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	clock_t tm2 = clock();
    	bucket(N, data, 0, 1, bu, num);
    	clock_t tm3 = clock();
    	printf("昇順(データ数: %d)\n", N);
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    			// 降順
    	tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	tm2 = clock();
    	bucket(N, data, 1, 1, bu, num);
    	tm3 = clock();
    	printf("降順(データ数: %d)\n", N);
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    
    	return 0;
    }
    			
    (出力)
    昇順(データ数: 5000000)
       data 39 ms
       sort 564 ms
    降順(データ数: 5000000)
       data 36 ms
       sort 540 ms
    			

  5. ヒープソート

      ヒープという言葉によって,プログラム実行時に任意に確保や解放を繰り返すことができるメモリ領域(ヒープ領域,ヒープメモリ,heap memory )を指す場合があります.しかし,ここで使用するヒープとは,木構造の一種であり,親ノードの値が子ノードの値より常に大きい(または,小さい)という条件を満たしています.一般的には,各ノードが最大で 2 つの子ノードしか持たないバイナリヒープを意味します.ヒープソートは,次に述べるクイックソートよりも多少遅いですが,非常に高速なソートであり,その手順は以下に示すとおりです.

    1. 整列対象の配列内にボトムアップでヒープを構築する.

    2. 構築されたヒープの先頭要素 [0] と最後の要素 [N-1] を交換する.

    3. 先頭要素 [0] から最後の一つ前の要素 [N-2] でヒープを再構成する.

    4. 最後の要素を [N-2] として( N = N - 1 ),手順 B に戻る.

    5. N が 0 となった時点で終了する.

      上の説明だけでは分かり難いと思いますので,ソート対象のデータ,
    int data[] = {1, 4, 2, 9, 0, 2, 8, 7, 3, 5}			
    を昇順に並べたい場合を例にとって説明します.昇順に並べるためには,ルートノードが最も大きいヒープ(親ノードの値が子ノードの値より常に大きいヒープ)を生成する必要があります.実際,上に示したデータ全体に対してヒープを生成すると以下のようになります.
    {9, 7, 8, 4, 5, 2, 2, 1, 3, 0}
    	親 [0] 子 [1], [2]
    	親 [1] 子 [3], [4]
    	親 [2] 子 [5], [6]
    	親 [3] 子 [7], [8]
    	親 [4] 子 [9]			
      一般に,
    data[k] : 親
    	data[k1], data[k2] : 子供,k1 = 2k+1, k2 = 2k+2
    	子供から見て親は:(k1-1) / 2 = (k2-1) / 2 = k			
    のような形でヒープが生成されます.

      このプログラムでは,STL 内のアルゴリズムである make_heapsort_heap を利用した場合についても検討しています.

    /****************************/
    /* ヒープソート             */
    /*      coded by Y.Suganuma */
    /****************************/
    #include <stdio.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include "MT.h"
    
    using namespace std;
    
    /*****************************/
    /* データの比較(昇順)      */
    /* a > bの場合に0以外(真) */
    /*****************************/
    int ascend(int a, int b)
    {
    	return a > b;
    }
    
    /*****************************/
    /* データの比較(降順)      */
    /* a < bの場合に0以外(真) */
    /*****************************/
    int descend(int a, int b)
    {
    	return a < b;
    }
    
    /*************************************/
    /* 2つのデータを交換する            */
    /*      a,b : 2つのデータのアドレス */
    /*************************************/
    void swap(int *a, int *b)
    {
    	int temp = *a;
    	*a       = *b;
    	*b       = temp;
    }
    
    /**********************************/
    /* ヒープの生成                   */
    /*      n : データの数            */
    /*      data : データ             */
    /*      parent : 親のインデックス */
    /*      compare : 比較関数        */
    /**********************************/
    void heap_c(int n, int *data, int parent, int (*compare)(int, int))
    {
    			// 子ノードのインデックス
    	int child = (2 * parent) + 1;
    			// 親ノードの値
    	int temp = data[parent];
    			// ヒープの生成
    	while (child < n) {
    				// 値が大きい(小さい)ほうの子ノードを選択する。
    		if (child < n-1 && compare(data[child + 1], data[child]))
    			child = child + 1;
    				// 親ノードの値が子ノードの値より大きい(小さい)場合は何もしない。
    		if (compare(temp, data[child]))
    			break;
    		else {   // temp が data[child] 以下(以上)の場合
    				// 親の要素に子を入れる
    			data[(child - 1) / 2] = data[child];
    				// 子ノードのインデックスを進める。
    			child = (2 * child) + 1;
    		}
    	}
    			// 親ノードとなる要素(最後の子供)に値を入れる
    	data[(child - 1) / 2] = temp;
    }
    
    /***************************/
    /* ヒープソート            */
    /*      n : データの数     */
    /*      data : データ      */
    /*      compare : 比較関数 */
    /***************************/
    void heap(int n, int *data, int (*compare)(int, int))
    {
    			// ヒープの生成
    			//    data[k] : 親
    			//    data[k1], data[k2] : 子供,k1 = 2k+1, k2 = 2k+2
    			//       子供から見て親は:(k1-1) / 2 = (k2-1) / 2 = k
    			// 例 {1, 4, 2, 9, 0, 2, 8, 7, 3, 5}
    			// 結果 {9, 7, 8, 4, 5, 2, 2, 1, 3, 0}   昇順の場合
    			//    親 [0] 子 [1], [2]
    			//    親 [1] 子 [3], [4]
    			//    親 [2] 子 [5], [6]
    			//    親 [3] 子 [7], [8]
    			//    親 [4] 子 [9]
    	for (int i1 = (n-1) / 2; i1 >= 0; i1--)
    		heap_c(n, data, i1, compare);
    			// ヒープソート実行
    	for (int i1 = n - 1; i1 > 0; i1--) {
    		swap(&data[0], &data[i1]);   // 先頭を最後と交換
    		heap_c(i1, data, 0, compare);   // 残りの部分に対するヒープの生成
    	}
    }
    
    #define N 5000000
    
    int main()
    {
    			// 初期設定
    	int *data = new int [N];
    	init_genrand((unsigned)time(NULL));
    			// 昇順
    	clock_t tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	clock_t tm2 = clock();
    	heap(N, data, ascend);
    	clock_t tm3 = clock();
    	printf("昇順(データ数: %d)\n", N);
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    
    	tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	tm2 = clock();
    	make_heap(&data[0], &data[N]);
    	sort_heap(&data[0], &data[N]);
    	tm3 = clock();
    	printf("      ***make_heap と sort_heap の利用***\n");
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    			// 降順
    	tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	tm2 = clock();
    	heap(N, data, descend);
    	tm3 = clock();
    	printf("降順(データ数: %d)\n", N);
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    
    	tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	tm2 = clock();
    	make_heap(&data[0], &data[N], greater<int>());
    	sort_heap(&data[0], &data[N], greater<int>());
    	tm3 = clock();
    	printf("      ***make_heap と sort_heap の利用***\n");
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    
    	return 0;
    }
    			
    (出力)
    昇順(データ数: 5000000)
       data 39 ms
       sort 1691 ms
          ***make_heap と sort_heap の利用***
       data 34 ms
       sort 1076 ms
    降順(データ数: 5000000)
       data 33 ms
       sort 1664 ms
          ***make_heap と sort_heap の利用***
       data 36 ms
       sort 1104 ms
    			

  6. クイックソート

      クイックソートの手順は以下に示すとおりです.例えば,次のようなデータが与えられたとしまする.このとき,左端のデータ( 37 )を枢軸要素と呼び,この要素がソート後の正しい位置にくるように以下の処理を行います(昇順の場合).

    37 2 6 4 89 8 10 12 68 45

    1. 配列の右端の要素から順に枢軸要素 ( 37 ) と比較していき 37 より小さな要素があれば,その要素を 37 と交換する.

      12 2 6 4 89 8 10 37 68 45

    2. 配列の左端(正確には,上で 37 と交換した 12 の直後の要素)から順に枢軸要素 ( 37 ) と比較していき,37 より大きな要素があれば,その要素を 37 と交換する.

      12 2 6 4 37 8 10 89 68 45

    3. 配列の右端(正確には,上で 37 と交換した 89 の直前の要素)から順に枢軸要素 ( 37 ) と比較していき,37 より小さな要素があれば,その要素を 37 と交換する.

      12 2 6 4 10 8 37 89 68 45

    4. 以上の結果,37 の左には 37 より大きな要素はなく,またその右側には 37 より小さな要素は無い状態となる.つまり,37 は,ソート後の正しい位置にあることがわかる.

    5. 37 の左側の配列( 12 2 6 4 10 8 ),及び,右側の配列( 89 68 45 )に対して以上の処理を再帰的に繰り返す.

      このプログラムでは,標準関数 qsort,及び,STL 内のアルゴリズムである sort を利用した場合についても検討しています.

    /****************************/
    /* クイックソート           */
    /*      coded by Y.Suganuma */
    /****************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <algorithm>
    #include <functional>
    #include "MT.h"
    
    using namespace std;
    
    /*****************************/
    /* データの比較(昇順)      */
    /* a > bの場合に0以外(真) */
    /*****************************/
    int ascend(int a, int b)
    {
    	return a > b;
    }
    
    /*****************************/
    /* 2つの整数の比較          */
    /* a > bの場合に0以外(真) */
    /*****************************/
    int ascend1(const void *arg1, const void *arg2)
    {
    	int *a1 = (int *)arg1;
    	int *a2 = (int *)arg2;
    	return *a1 - *a2;
    }
    
    /*****************************/
    /* データの比較(降順)      */
    /* a < bの場合に0以外(真) */
    /*****************************/
    int descend(int a, int b)
    {
    	return a < b;
    }
    
    /*****************************/
    /* 2つの整数の比較          */
    /* a < bの場合に0以外(真) */
    /*****************************/
    int descend1(const void *arg1, const void *arg2)
    {
    	int *a1 = (int *)arg1;
    	int *a2 = (int *)arg2;
    	return *a2 - *a1;
    }
    
    /*************************************/
    /* 2つのデータを交換する            */
    /*      a,b : 2つのデータのアドレス */
    /*************************************/
    void swap(int *a, int *b)
    {
    	int temp = *a;
    	*a       = *b;
    	*b       = temp;
    }
    
    /*********************************/
    /* クイックソート                */
    /*      n : データの数           */
    /*      data : データ            */
    /*      compare : 比較を行う関数 */
    /*********************************/
    void quick(int n, int *data, int (*compare)(int, int))
    {
    	int left = 1, right = n-1, pos = 0, sw1 = 1, sw2;
    			// 枢軸要素を正しい位置に置く
    	while (sw1 > 0) {
    
    		sw1 = 0;
    
    		if (right-pos > 0) {
    			sw2 = 0;
    			for (int i1 = right; i1 > pos && sw2 == 0; i1--) {
    				if (compare(data[pos], data[i1])) {
    					swap(&data[pos], &data[i1]);
    					sw1   = 1;
    					sw2   = 1;
    					pos   = i1;
    					right = i1 - 1;
    				}
    			}
    		}
    
    		if (pos-left > 0) {
    			sw2 = 0;
    			for (int i1 = left; i1 < pos && sw2 == 0; i1++) {
    				if (compare(data[i1], data[pos])) {
    					swap(&data[pos], &data[i1]);
    					sw1  = 1;
    					sw2  = 1;
    					pos  = i1;
    					left = i1 + 1;
    				}
    			}
    		}
    
    		if (right-pos <= 0)
    			sw1 = 0;
    	}
    			// サブ配列の処理
    	if (pos > 1)
    		quick(pos, data, compare);
    	if (pos < n-2)
    		quick(n-1-pos, &data[pos+1], compare);
    }
    
    #define N 5000000
    
    int main()
    {
    			// 初期設定
    	int *data = new int [N];
    	init_genrand((unsigned)time(NULL));
    			// 昇順
    	clock_t tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	clock_t tm2 = clock();
    	quick(N, data, ascend);
    	clock_t tm3 = clock();
    	printf("昇順(データ数: %d)\n", N);
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    
    	tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	tm2 = clock();
    	qsort(data, N, sizeof(int), ascend1);
    	tm3 = clock();
    	printf("      ***qsort の利用***\n");
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    
    	tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	tm2 = clock();
    	sort(&data[0], &data[N]);
    	tm3 = clock();
    	printf("      ***sort の利用***\n");
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    			// 降順
    	tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	tm2 = clock();
    	quick(N, data, descend);
    	tm3 = clock();
    	printf("降順(データ数: %d)\n", N);
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    
    	tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	tm2 = clock();
    	qsort(data, N, sizeof(int), descend1);
    	tm3 = clock();
    	printf("      ***qsort の利用***\n");
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    
    	tm1 = clock();
    	for (int i1 = 0; i1 < N; i1++)
    		data[i1] = genrand_int31();
    	tm2 = clock();
    	sort(&data[0], &data[N], greater<int>());
    	tm3 = clock();
    	printf("      ***sort の利用***\n");
    	printf("   data %d ms\n", (int)(tm2-tm1));
    	printf("   sort %d ms\n", (int)(tm3-tm2));
    
    	return 0;
    }
    			
    (出力)
    昇順(データ数: 5000000)
       data 57 ms
       sort 1063 ms
          ***qsort の利用***
       data 36 ms
       sort 1049 ms
          ***sort の利用***
       data 36 ms
       sort 438 ms
    降順(データ数: 5000000)
       data 36 ms
       sort 1031 ms
          ***qsort の利用***
       data 36 ms
       sort 1043 ms
          ***sort の利用***
       data 35 ms
       sort 439 ms
    			

情報学部 菅沼ホーム 全体目次