【C言語】乱数の配列をクイックソートする方法を解説

この記事では、C言語を使って乱数を生成し、その乱数の配列をクイックソートという方法で効率的にソートする方法を学びます。

乱数の生成からソートの実装まで、具体的なコード例を通じてわかりやすく解説しますので、プログラミング初心者の方でも簡単に理解できる内容になっています。

これを読めば、C言語での乱数処理やソートアルゴリズムの基本が身につきます。

目次から探す

C言語における乱数の生成

C言語では、乱数を生成するために標準ライブラリの <stdlib.h> を使用します。

このライブラリには、乱数を生成するための関数がいくつか用意されています。

乱数を扱う際には、まずこのライブラリをインクルードする必要があります。

乱数生成のためのライブラリ

乱数を生成するために必要なライブラリは、以下のようにインクルードします。

#include <stdlib.h>
#include <time.h>
  • <stdlib.h>: 乱数生成に必要な関数が含まれています。
  • <time.h>: 現在の時刻を取得するために使用し、乱数の初期化に役立ちます。

乱数の生成方法

C言語では、rand()関数を使用して乱数を生成します。

この関数は、0から RAND_MAX(通常は32767)までの整数を返します。

乱数をより制御するためには、srand()関数を使って乱数の種(シード)を設定します。

シードを設定することで、プログラムを実行するたびに異なる乱数列を生成することができます。

以下は、乱数を生成する基本的なコードの例です。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
    // 現在の時刻をシードとして設定
    srand(time(NULL));
    // 乱数を生成して表示
    for (int i = 0; i < 5; i++) {
        int random_number = rand();
        printf("%d\n", random_number);
    }
    return 0;
}

このコードでは、srand(time(NULL)) によって、現在の時刻をシードとして設定しています。

これにより、毎回異なる乱数が生成されます。

乱数の配列を作成する

乱数を生成したら、それを配列に格納することができます。

以下のコードは、指定したサイズの乱数の配列を作成し、表示する例です。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ARRAY_SIZE 10  // 配列のサイズ
int main() {
    int random_numbers[ARRAY_SIZE];
    // 現在の時刻をシードとして設定
    srand(time(NULL));
    // 乱数を生成して配列に格納
    for (int i = 0; i < ARRAY_SIZE; i++) {
        random_numbers[i] = rand() % 100;  // 0から99の乱数
    }
    // 生成した乱数を表示
    printf("生成した乱数の配列:\n");
    for (int i = 0; i < ARRAY_SIZE; i++) {
        printf("%d ", random_numbers[i]);
    }
    printf("\n");
    return 0;
}

このコードでは、random_numbers という配列を定義し、rand() % 100 を使って0から99の範囲の乱数を生成しています。

生成した乱数は配列に格納され、最後に表示されます。

これで、C言語における乱数の生成と配列の作成についての基本的な理解が得られました。

次のステップでは、生成した乱数の配列をクイックソートでソートする方法について解説します。

クイックソートの実装

クイックソートは、効率的なソートアルゴリズムの一つで、平均的な計算量がO(n log n)と非常に優れています。

このセクションでは、C言語におけるクイックソートの実装方法を詳しく解説します。

クイックソート関数の定義

まず、クイックソートのメインとなる関数を定義します。

この関数は、ソート対象の配列とその配列の最初と最後のインデックスを引数として受け取ります。

void quickSort(int arr[], int low, int high) {
    if (low < high) {
        // パーティション処理を行い、ピボットのインデックスを取得
        int pi = partition(arr, low, high);
        // ピボットを基準に左右の部分配列を再帰的にソート
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}

この関数では、まず配列の範囲が有効であるかを確認し、次にパーティション処理を行います。

パーティション処理の結果、ピボットのインデックスが得られたら、そのインデックスを基準に左右の部分配列を再帰的にソートします。

パーティション処理の実装

次に、パーティション処理を実装します。

この処理では、配列の中からピボットを選び、ピボットより小さい要素と大きい要素を分ける役割を果たします。

int partition(int arr[], int low, int high) {
    // ピボットとして配列の最後の要素を選択
    int pivot = arr[high];
    int i = (low - 1); // 小さい要素のインデックス
    for (int j = low; j < high; j++) {
        // 現在の要素がピボット以下であれば
        if (arr[j] <= pivot) {
            i++; // 小さい要素のインデックスを増加
            // arr[i]とarr[j]を交換
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    // ピボットを正しい位置に移動
    int temp = arr[i + 1];
    arr[i + 1] = arr[high];
    arr[high] = temp;
    return (i + 1); // ピボットのインデックスを返す
}

この関数では、配列の最後の要素をピボットとして選び、配列を走査しながらピボットより小さい要素を左側に集めていきます。

最終的にピボットを正しい位置に移動し、そのインデックスを返します。

再帰的なソート処理

クイックソートの再帰的な処理は、最初に定義したquickSort関数内で行われます。

パーティション処理によって得られたピボットのインデックスを基に、左右の部分配列を再帰的にソートします。

このプロセスは、配列が完全にソートされるまで続きます。

以下は、クイックソートを使用して乱数の配列をソートする全体のプログラムの例です。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void quickSort(int arr[], int low, int high);
int partition(int arr[], int low, int high);
int main() {
    int n = 10; // 配列のサイズ
    int arr[n];
    // 乱数の初期化
    srand(time(NULL));
    for (int i = 0; i < n; i++) {
        arr[i] = rand() % 100; // 0から99の乱数を生成
    }
    printf("ソート前の配列:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    // クイックソートの実行
    quickSort(arr, 0, n - 1);
    printf("ソート後の配列:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

このプログラムでは、まず乱数の配列を生成し、ソート前とソート後の配列を表示します。

クイックソートを実行することで、配列が正しくソートされることが確認できます。

乱数の配列をソートするプログラムの全体構成

このセクションでは、乱数の配列を生成し、それをクイックソートでソートするプログラムの全体構成について解説します。

プログラムは、ヘッダファイルのインクルードから始まり、メイン関数の構成、そして乱数生成とソートの流れを順を追って説明します。

ヘッダファイルのインクルード

C言語のプログラムでは、必要な機能を提供するためにヘッダファイルをインクルードします。

乱数を生成するためには <stdlib.h><time.h> を、入出力を行うためには <stdio.h> を使用します。

以下のようにインクルードします。

#include <stdio.h>   // 入出力関数を使用するため
#include <stdlib.h>  // rand() と srand() を使用するため
#include <time.h>    // time() を使用するため

メイン関数の構成

メイン関数はプログラムのエントリーポイントです。

ここでは、乱数の配列を生成し、クイックソートを呼び出す流れを構成します。

以下はメイン関数の基本的な構成です。

int main() {
    int n = 10; // 配列のサイズ
    int arr[n]; // 整数型の配列を宣言
    // 乱数の配列を生成
    generate_random_array(arr, n);
    // ソート前の配列を表示
    printf("ソート前の配列:\n");
    print_array(arr, n);
    // クイックソートを呼び出す
    quicksort(arr, 0, n - 1);
    // ソート後の配列を表示
    printf("ソート後の配列:\n");
    print_array(arr, n);
    return 0; // プログラムの正常終了
}

乱数生成とソートの流れ

メイン関数内では、まず乱数の配列を生成する関数 generate_random_array を呼び出します。

この関数では、rand() を使って乱数を生成し、配列に格納します。

次に、ソート前の配列を表示するために print_array関数を呼び出します。

その後、クイックソートを実行するために quicksort関数を呼び出します。

ソートが完了したら、再度 print_array関数を使ってソート後の配列を表示します。

以下は、乱数生成とソートの流れを示すサンプルコードです。

void generate_random_array(int arr[], int size) {
    srand(time(NULL)); // 現在の時刻をシードとして使用
    for (int i = 0; i < size; i++) {
        arr[i] = rand() % 100; // 0から99の乱数を生成
    }
}
void print_array(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]); // 配列の要素を表示
    }
    printf("\n");
}

このように、乱数の配列を生成し、クイックソートを実行するプログラムの全体構成が整いました。

次のセクションでは、実行例と結果の確認について詳しく見ていきます。

実行例と結果の確認

プログラムのコンパイルと実行

ここでは、先に作成した乱数の配列をクイックソートするプログラムをコンパイルし、実行する手順を説明します。

まず、以下のようなプログラムを quicksort_random.c というファイル名で保存します。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define SIZE 10 // 配列のサイズ
// クイックソートの関数
void quicksort(int arr[], int low, int high) {
    if (low < high) {
        int pivot = partition(arr, low, high);
        quicksort(arr, low, pivot - 1);
        quicksort(arr, pivot + 1, high);
    }
}
// パーティション処理の関数
int partition(int arr[], int low, int high) {
    int pivot = arr[high]; // ピボットを配列の最後の要素に設定
    int i = (low - 1); // 小さい要素のインデックス
    for (int j = low; j < high; j++) {
        if (arr[j] < pivot) {
            i++;
            // arr[i]とarr[j]を交換
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    // ピボットを正しい位置に移動
    int temp = arr[i + 1];
    arr[i + 1] = arr[high];
    arr[high] = temp;
    return (i + 1);
}
// 乱数の配列を生成する関数
void generate_random_array(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        arr[i] = rand() % 100; // 0から99の乱数を生成
    }
}
// メイン関数
int main() {
    srand(time(NULL)); // 乱数の初期化
    int arr[SIZE];
    generate_random_array(arr, SIZE); // 乱数の配列を生成
    printf("ソート前の配列:\n");
    for (int i = 0; i < SIZE; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    quicksort(arr, 0, SIZE - 1); // クイックソートを実行
    printf("ソート後の配列:\n");
    for (int i = 0; i < SIZE; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

このプログラムをコンパイルするには、ターミナルを開いて以下のコマンドを入力します。

gcc -o quicksort_random quicksort_random.c

コンパイルが成功したら、次にプログラムを実行します。

./quicksort_random

ソート結果の表示

プログラムを実行すると、まずソート前の乱数の配列が表示され、その後にソートされた配列が表示されます。

例えば、以下のような出力が得られるかもしれません。

ソート前の配列:
34 7 23 32 5 62 32 1 99 12 
ソート後の配列:
1 5 7 12 23 32 32 34 62 99

この出力から、乱数の配列が正しくソートされていることが確認できます。

実行結果の考察

実行結果を考察すると、乱数の生成とクイックソートの実装が正しく機能していることがわかります。

ソート前の配列はランダムに生成されており、ソート後の配列は昇順に並んでいます。

クイックソートは平均的にO(n log n)の時間計算量を持つため、大きな配列に対しても効率的に動作します。

このプログラムを実行することで、C言語における乱数の生成とクイックソートの実装方法を理解することができ、実際に動作するプログラムを通じて学びを深めることができます。

目次から探す