#はじめに
冬の工作でハマった内容。せっかくなので埋めてみる
問題
M5stack Core2にてランダムな数字を表示する処理を作っていたが、
その中でリセットしても毎回同じ数字が出現してしまうという問題が発生。
結論
randomSeed(esp_random())
or randomSeed(micros())
を使うといいっぽい
どうやら起動直後に乱数発生を行う場合は、millisでは同じ数字になっていまうとのこと。
Arduino などの多くのプラットフォームでは「millis() は起動してからの経過ミリ秒を返す」関数ですが、
M5Stack や ESP32 のリセット直後というのは必ず同じようなタイミングで起動処理が走るため、
millis() が返す値はほぼ固定されてしまいます。
つまり、リセット直後に randomSeed(millis()) を呼んでも、同じシード(初期値)を与えてしまう可能性が高く、
毎回同じ乱数列が生成されてしまうわけです。
表作成コード
randomSeed(millis())
を使うとリセットしても毎回同じ文字が出現していまう。
#include <M5Core2.h>
// グリッド設定
const int COLUMNS = 5; // 列数
const int ROWS = 5; // 行数
const int CELL_WIDTH = 60; // セル1つの幅
const int CELL_HEIGHT = 40; // セル1つの高さ
// グリッド開始座標
const int START_X = 10;
const int START_Y = 10;
const int maxNum = 50;
// 乱数を表示する関数
void drawRandomGrid()
{
// 画面をクリア
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(WHITE);
for (int row = 0; row < ROWS; row++)
{
for (int col = 0; col < COLUMNS; col++)
{
int x = START_X + col * CELL_WIDTH;
int y = START_Y + row * CELL_HEIGHT;
M5.Lcd.drawRect(x, y, CELL_WIDTH, CELL_HEIGHT, WHITE);
int randNum = random(1, maxNum + 1);
int centerX = x + (CELL_WIDTH / 2);
int centerY = y + (CELL_HEIGHT / 2);
// 文字描画位置を少しずらしてセル中央に表示
M5.Lcd.setCursor(centerX - 10, centerY - 8);
M5.Lcd.printf("%d", randNum);
}
}
}
void setup()
{
M5.begin();
M5.Lcd.setRotation(1);
M5.Lcd.fillScreen(BLACK);
// 乱数シードを設定(毎回異なる乱数を得るために)
// randomSeed(millis());
randomSeed(micros());
drawRandomGrid();
}
void loop()
{
}
(おまけ) 重複しないようにする
上記コードだと数字の重複もあり得る。重複をさせない場合は事前に配列を作ってからシャッフルするのが良さそう。
ビンゴカードとかつくるときに活用しよう(作らないか)
// 1~20の数字を用意
std::vector<int> candidates;
candidates.reserve(20);
for (int n = 1; n <= 20; n++) {
candidates.push_back(n);
}
// シャッフルする
uint32_t seedVal = esp_random();
std::mt19937 g(seedVal);
std::shuffle(candidates.begin(), candidates.end(), g);