見出し画像

2024-08-20 C++プログラミング練習

こんにちわ、ケロッグです。
最近Modern C++ チャレンジという問題集に取り組んでいます。これは100問の問題をC++を使って解く問題集です。

今回は、「πの値を少数2桁まで求めよ」という問題でした。
求める手順は以下のような感じです。
・直径dの円の面積はπd^2/4
・一辺dの正方形の面積はd^2
・円と正方形の中に乱数で点を生成し、円の面積÷正方形の面積を求めることで、πを求める。

プログラムは載せませんが、乱数を使った同様の問題をchatGPTに作ってもらいました。


問題: 乱数で迷路を生成するプログラム
概要:
2次元の迷路をランダムに生成し、その迷路を表示するプログラムを作成します。

要件:

迷路のサイズ:

幅 (width) と高さ (height) を指定できるようにします。迷路は width x height のサイズになります。
迷路のセル:

迷路の各セルは、通路(' ')または壁('#')のいずれかです。
迷路の生成:

ランダムに壁と通路を配置しますが、迷路の外周は必ず壁で埋めます。
表示:

生成した迷路をコンソールに表示します。迷路の外周は壁で、内部はランダムに壁と通路が配置されているようにします。
制約:

width と height は 5 以上の正の整数とします


私は以下のように書きました。

#include <iostream>
#include <cmath>
#include <random>
#include <array>
#include <algorithm>
#include <functional>

void generateMaze(const int width, const int height)
{
	std::random_device rd;
	auto seed_data = std::array<int, std::mt19937::state_size> {};
	std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
	std::seed_seq seq(std::cbegin(seed_data), std::cend(seed_data));
	auto eng = std::mt19937{ seq };
	auto dist = std::uniform_real_distribution<>{ 0,1 };

	for (auto i = 0; i <= height; i++)
	{
		for (auto k = 0; k <= width; k++)
		{
			if (i == 0 || k == 0 || i == height || k == width) {
				std::cout << "#";
			}
			else if(i==1 && k==1)
			{
				std::cout << "S";
			}
			else if (i == height-1 && k == width-1)
			{
				std::cout << "G";
			}
			else
			{
				auto x = dist(eng);
				if (x < 0.65) {
					std::cout << " ";
				}
				else {
					std::cout << "#";
				}
			}
		}
		std::cout << "\n";
	}
}

int main() {
	int ht;
	int wi;

	std::cout << "迷路の高さを入力してください" << std::endl;
	std::cin >> ht;
	std::cout << "迷路の幅を入力してください" << std::endl;
	std::cin >> wi;

	generateMaze(wi, ht);

	return 0;
}

コンソール画面

生成された迷路。スタート周辺でいきなり詰んでます笑


今回主に学びになったのは、generateMaze関数内の乱数生成の準備のところです。
ここは、覚えづらく理解しづらいところなので、chatGPTにこの乱数生成を他のことに例えて教えてもらいました。


コーヒーを淹れるプロセスの例え

  1. std::random_device を使用して、高品質な乱数を生成
    ここでは、高品質な豆を選ぶことに相当します。良い豆を選ぶことで、美味しいコーヒーの基盤が作られます。

  2. その乱数を std::array に保存
    選んだ豆を保存するステップです。選んだ豆を袋に入れて保存します。ここで豆が「保存」されて、次のステップに備えます。

  3. std::seed_seq を使って、生成した乱数を擬似乱数生成器に種として設定
    豆を挽く工程です。豆を挽くことで、コーヒーの風味を引き出し、コーヒーメーカーにセットする準備が整います。

  4. std::mt19937 を使って、擬似乱数生成器を初期化
    コーヒーメーカーをセットして準備することに相当します。豆を挽いた後、コーヒーメーカーにセットして準備が整います。ここで、コーヒーメーカーが適切に設定されます。

  5. std::uniform_real_distribution を使って、指定された範囲内で乱数を生成する準備
    お湯を沸かしてコーヒーを淹れる工程です。コーヒーメーカーが準備できたら、実際にお湯を入れてコーヒーを淹れます。ここで、選んだ豆と水の比率に応じて、コーヒーが完成します。

まとめ

この例えでは、コーヒーを淹れるプロセスを通じて、以下のように説明できます:

  1. 高品質な豆を選ぶ(std::random_device で乱数生成)

  2. 豆を保存する(std::array に保存)

  3. 豆を挽く(std::seed_seq で種を設定)

  4. コーヒーメーカーをセットする(std::mt19937 を初期化)

  5. お湯を沸かしてコーヒーを淹れる(std::uniform_real_distribution で乱数を生成)


このように何かにわかりやすく例えると、覚えやすいと思います。まあ今の時代覚える必要はないかもしれませんが、メンバ関数がどのような処理をしており何ができるのかを理解しておくことは重要だと思っています。

今回はここまでです。ありがとうございました。

いいなと思ったら応援しよう!