【C言語】乱数で”3の倍数”を生成する際は”一般項”を用いよ

C言語

ようこそ、KiRiOのブログへ!

本記事では「新・明解C言語中級編」の自由課題について解説します。


自由課題のとおり、この参考書には解答が載っておりません。
そこで、本記事で私なりの解答を共有しますので、ぜひ解答の参考にしてみてください。

今回解説する課題は「演習1-4」で、課題内容は以下のとおりです。

演習1-4当てさせる数を3以上999以下の3の倍数(3, 6, 9, …, 999)とした《数当てゲーム》を作成せよ。
3の倍数でない値が入力された場合に、ただちにゲームを終了するものと、比較結果を表示せず再入力させる(入力回数としてカウントしない)ものの二つを作ること。
プレーヤが入力できる最大の回数が、どのくらいであれば適当であるのかも考察すること。


今回の課題内容を整理しますと、以下のようになります。

  1. “3〜999″の”3の倍数”を乱数で生成
  2. 最大入力回数の設定
  3. 《数当てゲーム1》:3の倍数でない値が入力された時点でゲームを終了
  4. 《数当てゲーム2》:3の倍数でない値が入力された際、比較結果を表示せずに再入力

それでは、これらを順番に設計していきます。

設計

“3〜999″の”3の倍数”を乱数で生成

乱数の生成については過去の演習でも扱っており、下記内容についてはすでに解説しています。

  • 生成範囲を指定せずに乱数を生成
    • rand()
    • 0〜RAND_MAXの範囲内で乱数を生成
  • 生成範囲を指定して乱数を生成
    • a + rand() % (b + 1)
    • a 以上 (a + b) 以下の乱数を生成

これまでの内容を踏まえると「3〜999の乱数」は「3 + rand() % 997」と表されます。

では、そこからさらに「3の倍数」に範囲を絞り込むためにはどのようにすればよいでしょうか。

こういうときも、まずは簡単な例から考えていきます。
「3〜999」「3の倍数」という条件を無視して「1以上10以下の偶数」という場合について考えてみましょう。

「1以上10以下の偶数」ですので「2, 4, 6, 8, 10」ということになりますが、これらの数をランダムに生成する必要があります。

ここで重要となる考え方が「一般項」です。

偶数(2の倍数)を自然数$n$を用いた一般項は「$2n$」で表されます。
1以上10以下の偶数ですと、$n$にあたる数は「1, 2, 3, 4, 5」です。
そのため「1, 2, 3, 4, 5」を乱数として生成し、それらを2倍すれば「2, 4, 6, 8, 10」となります。
これで「1以上10以下の偶数」を乱数として生成することができます。

それでは本題に戻りまして「3以上999以下の3の倍数」を乱数として生成する場合を考えましょう。
先ほどの例に則りますと、3の倍数の一般項は「$3n$ ($n = 1, 2, 3, …$)」です。
そのため、まずは$n$にあたる「1, 2, 3, …, 333」を乱数として生成します
その後、それらの乱数を3倍すれば「3以上999以下の3の倍数」を生成することができます

最大入力回数の設定

《数当てゲーム》の最大入力回数(試行回数)の設定方法については下記ブログで扱っています▼

【C言語】数当てゲームの入力回数は”指数不等式”で設定せよ
ようこそ、KiRiOのブログへ! 本記事では「新・明解C言語中級編」の自由課題について解説します。 自由課題のとおり、この参考書には解答が載っておりません。 そこで、本記事で私なりの解答を共有しますので、ぜひ解答の参考にしてみ...

本問では「3以上999以下の3の倍数(3, 6, 9, …, 999)」が当てさせる数ですので「連続する333個の整数」とも言い換えられます。よって、

$$n – 1 < \log_{2}333 < n$$ $$n - 1 < 8.38 < n$$

となり、本問における最大入力回数は「9回」として設定することにします。

《数当てゲーム1》:3の倍数でない値が入力された時点でゲームを終了

《数当てゲーム1/2》は、プレーヤの入力後に行う処理についてです。
そのため、それぞれのパターンでプレーヤの数値入力後に分岐処理を追記する必要があります。

まず《数当てゲーム1》ですが、こちらは「プレーヤの入力数値が3で割り切れなかったらプログラムを終了する」とします。

ただし、何も表示されずにプログラムが終了すると、プレーヤはなぜ終了したのかわからなくなってしまいますので、親切心としてメッセージを表示することにします。
今回は、「1回目の入力前」と「3の倍数以外の数値が入力された後」の2つの場面で以下のようにメッセージを表示することにします。

  • 1回目の入力前:3〜999の3の倍数を当ててください(制限回数:9回、3の倍数以外の数値が入力されるとゲームは終了します)
  • 3の倍数以外の入力後:3の倍数以外の数値が入力されたのでゲームを終了します…

《数当てゲーム2》:3の倍数でない値が入力された際、比較結果を表示せずに再入力

《数当てゲーム2》では、課題内容にしたがって「3の倍数の数値以外が入力された後に”再入力理由”と”入力メッセージ”を表示する」ことにします。

  • 3の倍数以外が入力されましたので再入力してください
  • *回目:(数値を入力)

設計は以上となります。

本問は、前問(演習1-3)を改良したプログラムとなりますので、その他の部分(数値の入力や比較など)につきましては下記ブログをご覧ください▼

【C言語】数当てゲームの入力回数は”指数不等式”で設定せよ
ようこそ、KiRiOのブログへ! 本記事では「新・明解C言語中級編」の自由課題について解説します。 自由課題のとおり、この参考書には解答が載っておりません。 そこで、本記事で私なりの解答を共有しますので、ぜひ解答の参考にしてみ...

実装

それでは、設計内容をもとに実装していきます。
以下の順番で実装していきます。

  • 3〜999の3の倍数を乱数で生成
  • 最大入力回数の設定
  • 《数当てゲーム1》:3の倍数でない値が入力された時点でゲームを終了
  • 《数当てゲーム2》:3の倍数でない値が入力された際、比較結果を表示せずに再入力

“3〜999″の”3の倍数”を乱数で生成

設計部分でも述べましたとおり、“1〜333″の乱数を生成後、それらを3倍します。
これをC言語で表すと「3 * (1 + rand() % 333)」となります。

しかし、この表記ですと「333」という数字の導出過程がわかりにくくなってしまいます(もちろん、ここまでの説明を読んできた方にはわかると思いますが、課題内容とソースコードのみを見た方が疑問を持つ場合があります)。

今回の導出過程を簡潔に述べると「3の倍数($3n$)の$n$を乱数として生成した後に3倍する」というものです。
そこで「3 * (1 + rand() % (999 / 3))」と書き換えると、「“3〜999″の”3の倍数”」という課題内容の数字をもとに導出したことがわかります。

以上を実装したものが下記プログラムです。

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

int main(void)
{
	int try;
	int ans;

	do
	{
		printf("《数当てゲーム》を開始しますか?【はい…1/いいえ…0】:");
		scanf("%d", &try);

		if (try == 0)
		{
			return 0;
		}
	} while (try != 0 && try != 1);

	srand(time(NULL));
	ans = 3 * (1 + rand() % (999 / 3));

	return 0;
}

最大入力回数の設定

最大入力回数は設計段階で「9回」に設定しました。
そこで、最大入力回数を定数に代入し、定数の値を制限回数用の変数(入力するたびにインクリメントされる)に代入します
以上を実装したものが下記プログラムです。

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

int main(void)
{
	int try;
	int ans;
	const int max = 9;
	int remain = max;

	do
	{
		printf("《数当てゲーム》を開始しますか?【はい…1/いいえ…0】:");
		scanf("%d", &try);

		if (try == 0)
		{
			return 0;
		}
	} while (try != 0 && try != 1);

	srand(time(NULL));
	ans = 3 * (1 + rand() % (999 / 3));

	return 0;
}

《数当てゲーム1》:3の倍数でない値が入力された時点でゲームを終了

ゲームの終了は、ifを用いた条件分岐を、プレーヤの入力後に追記します。
また、表示メッセージは設計段階で設定したものとします。
以上を実装したものが下記プログラムです(数値の入力や比較については前問の解説ブログをご覧ください)。

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

int main(void)
{
	int try;
	int num, ans;
	const int max = 9;
	int remain = max;

	do
	{
		printf("《数当てゲーム》を開始しますか?【はい…1/いいえ…0】:");
		scanf("%d", &try);

		if (try == 0)
		{
			return 0;
		}
	} while (try != 0 && try != 1);

	srand(time(NULL));
	ans = 3 * (1 + rand() % (999 / 3));

	printf("\n3〜999の3の倍数を当ててください(制限回数:%d回、3の倍数以外の数値を入力するとゲームが終了します)\n", max);

	do
	{
		printf("%2d回目:", (max - remain) + 1");
		scanf("%d", &num);

		if ((num % 3) != 0)
		{
			puts("\a3の倍数以外の数値が入力されたのでゲームを終了します…");
			return 0;
		}

		remain--;

		if (remain == 0)
		{
			break;
		}
		
		if (num > ans)
		{
			printf("\a%3dよりも小さいです(残り%d回)\n", num, remain);
		}
		if (num < ans)
		{
			printf("\a%3dよりも大きいです(残り%d回)\n", num, remain);
		}
	} while (num != ans);

	if (num == ans)
	{
		printf("正解です!(入力回数:%d回)\n", max - remain);
	}
	if (num < ans)
	{
		printf("残念!正解は%dでした。\n", ans);
	}

	return 0;
}

《数当てゲーム2》:3の倍数でない値が入力された際、比較結果を表示せずに再入力

こちらも先ほどと同様、プレーヤの数値入力後に処理を追記します
ただし、《数当てゲーム2》の条件内容は「3の倍数が入力されるまで再入力を促す」ともとれますので、whileを用いることにします。
また、表示メッセージは設計段階で設定したものとします。
以上を実装したものが下記プログラムです(数値の入力や比較については前問の解説ブログをご覧ください)。

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

int main(void)
{
	int try;
	int num, ans;
	const int max = 9;
	int remain = max;

	do
	{
		printf("《数当てゲーム》を開始しますか?【はい…1/いいえ…0】:");
		scanf("%d", &try);

		if (try == 0)
		{
			return 0;
		}
	} while (try != 0 && try != 1);

	srand(time(NULL));
	ans = 3 * (1 + rand() % (999 / 3));

	printf("\n3〜999の3の倍数を当ててください(制限回数:%d回、3の倍数以外の数値を入力するとゲームが終了します)\n", max);

	do
	{
		printf("%2d回目:", (max - remain) + 1");
		scanf("%d", &num);

		while ((num % 3) != 0)
		{
			puts("\a3の倍数以外の数値が入力されましたので再入力してください");
			printf("%2d回目:", (max - remain) + 1");
			scanf("%d", &num);
		}

		remain--;

		if (remain == 0)
		{
			break;
		}
		
		if (num > ans)
		{
			printf("\a%3dよりも小さいです(残り%d回)\n", num, remain);
		}
		if (num < ans)
		{
			printf("\a%3dよりも大きいです(残り%d回)\n", num, remain);
		}
	} while (num != ans);

	if (num == ans)
	{
		printf("正解です!(入力回数:%d回)\n", max - remain);
	}
	if (num < ans)
	{
		printf("残念!正解は%dでした。\n", ans);
	}

	return 0;
}

以上で完了となります。
プログラムを実行して、3の倍数を答えとした《数当てゲーム》として機能しているか確認してみてください。

まとめ

それでは、本記事で説明した内容を以下にまとめます。

    乱数で3の倍数を生成するためには一般項を用いる

    1. 乱数群を一般項を用いて表す
      • $3n$($n = 1, 2, 3, …, 333$)

    2. 一般項の$n$にあたる乱数を生成($1, 2, 3, …, 333$)
      • 1 + rand() % (999 / 3)

    3. 生成した乱数を3倍する($3, 6, 9, …, 999$)
      • 3 * (1 + rand() % (999 / 3))

ここまで、記事を読んでいただき、ありがとうございました。
本問では、特定の「指定範囲」や「倍数列」に対して乱数生成していましたが、範囲や倍数を一般化した場合の乱数生成について下記ブログにて解説しています
本記事を一般化させた発展内容となっておりますので、ぜひこちらもご覧ください。

【C言語】任意の範囲内で"nの倍数"を乱数生成する方法【番外編】
ようこそ、KiRiOのブログへ! 本記事では「新・明解C言語中級編」の自由課題について解説します。 自由課題のとおり、この参考書には解答が載っておりません。 そこで、本記事で私なりの解答を共有しますので、ぜひ解答の参考にしてみ...

最後に、今回取り上げた課題が記載されている本を紹介します。
今回扱った課題以外にも、様々なプログラム開発について記載されているため、この記事とあわせて読んでいただくと、より理解が深まると思います。


また、参考として以下の本もご紹介しておきます。

こちらもオススメです。2021年12月14日に最新版が発売されました!

それでは、また別の記事でお会いしましょう!

コメント

タイトルとURLをコピーしました