【C言語入門】乱数を生成する方法を解説!rand関数の基礎から応用まで

  • 2025.05.23
       
【C言語入門】乱数を生成する方法を解説!rand関数の基礎から応用まで

C言語は、システムプログラミングやゲーム開発においてよく使用されているプログラミング言語の一種ですが、中でも「乱数生成」 は多くの場面で活用されています。
ゲーム内のランダム要素や暗号化など、無作為に数値を選んで使いたいケースにおいて、乱数生成は非常に便利な機能となります。

C言語で乱数生成を行う場合に使用するのが、「rand関数」 です。
今回は、rand関数の使用方法について、実際のコード例なども踏まえながら、具体的に解説をしていきたいと思います。

乱数とは?

乱数とは、任意に定めた固定の数値や一定の法則に基づいて決められる数値ではなく、無作為に決定される数値のことを指します。
決められた動きではなく、ランダムな動作をプログラムにさせたい場合にほぼ必須になるのが乱数です。

特定の言語に限らず、様々なプログラミング言語において乱数生成のための機能が用意されていますが、C言語の場合は「rand関数」を使って乱数生成を行います。

C言語のrand関数の使い方

それでは、rand関数を使って乱数を生成する方法について見ていきましょう。

基本構文と使い方

rand関数を使う際には、事前準備として標準ライブラリの1つである「stdlib.h」というヘッダーファイルをインクルードする必要があります。

#include <stdlib.h>

rand関数を使用することで、0~RAND_MAXまでの整数の乱数を生成することができます。
RAND_MAXは、 stdlib.hファイルで定義されている rand関数が返す最大値で、動作環境ごとに数値は異なりますが、最低でも 32767以上の数値が定義されています。

rand関数では引数の指定は行わないため、以下の構文のみで乱数を生成することができます。

rand()

以下のサンプルコードで、実際の使い方を見てみましょう。

【サンプルコード】

#include <stdio.h>
#include <stdlib.h>
 
int main(void) {

    printf("RAND_MAX: %d\n", RAND_MAX);

    for(int i = 0; i < 3; i++) {
        printf("%d\n", rand());
    }

    return 0;
}

【実行結果】

RAND_MAX: 2147483647
2086521570
1907555127
495653426

上記のサンプルコードでは RAND_MAXの数値が 2147483647となっているため、0〜2147483647の範囲で乱数が生成されています。
※本記事でのプログラムの実行結果は基本的に全てランダムです。自分で試したら結果が変わった!と思われるかもしれませんが、それはプログラムが正しく乱数を生成していることになります。

例えば 10~20までの乱数が欲しいといったように、生成する乱数の範囲を指定したい場合には、以下の式で範囲を指定しましょう。

【基本構文】

rand() % (最大値 - 最小値 + 1) + 最小値

以下はサンプルコードです。

【実行結果】

8
5
10

乱数を初期化する

実は、先ほどのサンプルコードをそのまま実行すると、何度繰り返しても同じパターンの乱数が生成されてしまいます。
これは、乱数の生成時に使用する 「乱数シード」 が変更されていないからです。

コンピュータの性質上、厳密にはプログラムで完全な乱数を作ることはできないため、代わりに計算によって 「ランダムに見える数字」 を生成しています。
これを疑似乱数と呼びます。

乱数シードとは、この疑似乱数の生成元となる初期値のことを言います。
rand関数での乱数の生成手順を簡潔に説明すると、以下のような流れで生成しています。

・1回目:シード値を初期値として計算を行い、乱数を生成
 ↓
・2回目:1回目で生成した乱数を元に同じ計算を行い、別の乱数を生成
 ↓
・3回目:(以下繰り返し)

使用する計算式はどれも同じであるため、初期値であるシード値が変わらなければ結果も全て同じとなってしまいます。
そのため、プログラムの実行毎に乱数をばらつかせるためには、シード値を毎回変化させる必要があります。

これを解決するために行われるのが、乱数の初期化です。
一般的に使用されるのは、 srand関数と time関数を組み合わせた初期化方法になります。

srand関数は、シード値を設定することができる関数です。
この関数の引数に、time関数で取得した現在時刻を指定して、シード値を初期化します。

time関数を使用する際は、「time.h」 をインクルードする必要があります。

#include <time.h>

time関数の引数には NULLを指定します。
以下のように記述することで、現在時刻を srand関数の引数に指定することができます。

【基本構文】

srand((unsigned int)time(NULL)); 

サンプルコードで実際の動きを確認してみましょう。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
 
int main(void) {

    // 同一シードの場合
    for(int i = 0; i < 3; i++) {

        srand(1); 
        printf("%d回目の乱数列: ", i + 1);
        
        for(int j = 0; j < 3; j++) {
            printf("%d, ", rand());
        }
        printf("\n");
    }
    
    // 現在時刻で初期化
    for(int i = 0; i < 3; i++) {

        srand((unsigned int)time(NULL)); 
        printf("%d回目の乱数列: ", i + 1);
        
        for(int j = 0; j < 3; j++) {
            printf("%d, ", rand());
        }
        sleep(1); // 時刻を変えるために1秒待機
        printf("\n");
    }
    
    return 0;
}

【実行結果】

1回目の乱数列: 16807, 282475249, 1622650073, 
2回目の乱数列: 16807, 282475249, 1622650073, 
3回目の乱数列: 16807, 282475249, 1622650073, 
1回目の乱数列: 185243666, 1686489959, 200084160, 
2回目の乱数列: 185260473, 1968965208, 1822734233, 
3回目の乱数列: 185277280, 103956810, 1297900659, 

シード値を同一の値に設定した場合と、現在時刻を使用して別々の値に設定した場合で、生成される乱数の違いを比べてみました。

設定するシード値が同じ数値である場合は、各ループ毎に生成される乱数も同じパターンのものが形成されています。
srand関数を使用しなかった場合のプログラムも、これと同等の動きをすることになります。

一方、time関数で取得した現在時刻を使用した場合には、異なるパターンの乱数が生成されているのが分かると思います。
この処理を行わないと、何度プログラムを実行してもシード値が変化することがないため、基本的には必ず入れておくべき処理となります。

なお、time関数は秒単位で時刻を取得するため、上記のサンプルコードでは sleep関数でループ毎の処理を1秒ずつ遅延させています。
unistd.h」ファイルは、time関数を使用するのに必要なライブラリです。

rand関数を使った実装例

rand関数を使った簡単な実装例として、サイコロをプログラムで表現する場合のサンプルコードと、じゃんけんゲームを実装する場合のサンプルコードを紹介したいと思います。

サイコロをプログラムで再現する

まずは、サイコロをプログラムで実装する例から紹介します。
ポイントは、サイコロの出目と同じように1〜6までの範囲で乱数が生成されるようにすることと、値ごとの処理を条件分けすることです。

以下のサンプルコードでは、1回のプログラム実行で3回サイコロを振り、その結果を表示するという流れで実装しています。

【サンプルコード】

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {

    srand((unsigned int)time(NULL));

    for(int i = 0; i < 3; i++) {

        printf("%d回目: ", i + 1);
        
        int num = rand() % 6 + 1;

	if (num >= 1 && num <= 6 ) {

	    printf("サイコロの目は%dです\n", num);

	} else {

	    printf("1〜6以外の値です\n");

        }
    }
    
    return 0;
}

【実行結果】

1回目: サイコロの目は5です
2回目: サイコロの目は3です
3回目: サイコロの目は1です

上記のサンプルコードでは出た目をコンソールに出力するのみですが、生成した値を比較して勝敗を判断するなど、よりゲームらしくすることもできます。

【サンプルコード】

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {

    srand((unsigned int)time(NULL));

    int num1 = rand() % 6 + 1;
    int num2 = rand() % 6 + 1;

    printf("あなたのサイコロの目は%dです\n", num1);
    printf("相手のサイコロの目は%dです\n", num2);

    if (num1 > num2) {

        printf("この勝負はあなたの勝ちです\n");

    } else {

	printf("この勝負はあなたの負けです\n");

    }
    
    return 0;
}

【実行結果】

あなたのサイコロの目は6です
相手のサイコロの目は5です
この勝負はあなたの勝ちです

じゃんけんゲームを実装する

続いて、じゃんけんゲームを実装する簡単な例を紹介します。

通常のじゃんけんとは異なり、プログラム上では数値を元に勝敗を判断する必要があるため、初めに連続する3つの値を乱数で生成した後、それぞれの値をグー・チョキ・パーに見立てて条件分けを行います。

指定する乱数の範囲は任意のもので問題ありませんが、今回のサンプルコードでは0〜2の範囲で乱数生成を行います。
0 はグー、1 はチョキ、2 はパーが出たものと見なし、その上で条件式を記述します。

【サンプルコード】

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {

    srand((unsigned int)time(NULL)); // 現在時刻の情報で初期化

    for(int i = 0; i < 3; i++) {

        printf("%d回戦目: ", i + 1);
        
        int num1 = rand() % 3;
        int num2 = rand() % 3;

        switch(num1) {

            case 0:

                if(num2 == 0) {
                  printf("両方ともグーを出しました。引き分けです。\n");

                } else if(num2 == 1) {
                  printf("あなたはグーで、相手はチョキを出しました。あなたの勝ちです。\n");

                } else {
                  printf("あなたはグーで、相手はパーを出しました。あなたの負けです。\n");
                }

                break;

            case 1:

                if(num2 == 1) {
                  printf("両方ともチョキを出しました。引き分けです。\n");

                } else if(num2 == 2) {
                  printf("あなたはチョキで、相手はパーを出しました。あなたの勝ちです。\n");

                } else {
                  printf("あなたはチョキで、相手はグーを出しました。あなたの負けです。\n");
                }

                break;

            case 2:

                if(num2 == 2) {
                  printf("両方ともパーを出しました。引き分けです。\n");

                } else if(num2 == 0) {
                  printf("あなたはパーで、相手はグーを出しました。あなたの勝ちです。\n");

                } else {
                  printf("あなたはパーで、相手はチョキを出しました。あなたの負けです。\n");
                }

                break;

            default:
                printf("値が不正です\n");
        }

    }
    
    return 0;
}

【実行結果】

1回戦目: 両方ともグーを出しました。引き分けです。
2回戦目: あなたはチョキで、相手はグーを出しました。あなたの負けです。
3回戦目: あなたはグーで、相手はチョキを出しました。あなたの勝ちです。

このように、乱数を使ってじゃんけんゲームを再現することもできます。

まとめ

いかがでしたか?今回は、rand関数の使い方について解説しました。

乱数生成は様々な場面で役に立つ便利な機能ですので、シード値の初期化方法と合わせて、覚えておくといいでしょう。

C言語の勉強方法は?

書籍やインターネットで学習する方法があります。昨今では、YouTubeなどの動画サイトやエンジニアのコミュニティサイトなども充実していて多くの情報が手に入ります。
そして、より効率的に知識・スキルを習得するには、知識をつけながら実際に手を動かしてみるなど、インプットとアウトプットを繰り返していくことが重要です。特に独学の場合は、有識者に質問ができたりフィードバックをもらえるような環境があると、理解度が深まるでしょう。

ただ、C言語に限らず、ITスキルを身につける際、どうしても課題にぶつかってしまうことはありますよね。特に独学だと、わからない部分をプロに質問できる機会を確保しにくく、モチベーションが続きにくいという側面があります。独学でモチベーションを維持する自信がない人にはプログラミングスクールという手もあります。費用は掛かりますが、その分スキルを身につけやすいです。しっかりと知識・スキルを習得して実践に活かしたいという人はプログラミングスクールがおすすめです。

プログラミングスクールならテックマニアがおすすめ!

ITスキル需要の高まりとともにプログラミングスクールも増えました。しかし、どのスクールに通うべきか迷ってしまう人もいるでしょう。そんな方にはテックマニアをおすすめします!これまで多くのITエンジニアを育成・輩出してきたテックマニアでもプログラミングスクールを開講しています。

<テックマニアの特徴>
・たしかな育成実績と親身な教育 ~セカンドキャリアを全力支援~
・講師が現役エンジニア ~“本当”の開発ノウハウを直に学べる~
・専属講師が学習を徹底サポート ~「わからない」を徹底解決~
・実務ベースでスキルを習得 ~実践的な凝縮カリキュラム~

このような特徴を持つテックマニアはITエンジニアのスタートラインとして最適です。
話を聞きたい・詳しく知りたいという方はこちらからお気軽にお問い合わせください。

     

Programmingカテゴリの最新記事