LoginSignup
1
0

M5stackCore2でstd:: shuffleを使う(random_deviceでハマる)

Last updated at Posted at 2024-05-23

背景

M5stackCore2でランダムな配列が欲しかったため、std:: shuffleを使おうとした

その結果がシャッフルされていない。

result
9; 6; 3; 1; 2; 5; 7; 10; 8; 4; 
4; 2; 3; 10; 5; 6; 7; 9; 1; 8;
8; 5; 3; 9; 6; 2; 7; 4; 10; 1; 
1; 6; 3; 4; 2; 5; 7; 8; 9; 10;
10; 2; 3; 8; 5; 6; 7; 1; 4; 9; 
9; 5; 3; 1; 6; 2; 7; 10; 8; 4; //ここからほぼ同じ結果
4; 6; 3; 10; 2; 5; 7; 9; 1; 8; 
8; 2; 3; 9; 5; 6; 7; 4; 10; 1;
1; 5; 3; 4; 6; 2; 7; 8; 9; 10; 
10; 6; 3; 8; 2; 5; 7; 1; 4; 9;
program
#include <M5Unified.h>
#include <algorithm>
#include <iostream>
#include <random>

template <typename T>
void printVector(std::vector<T> v) {
  for (const auto &item : v) {
    std::cout << item << "; ";
  }
  std::cout << std::endl;
}
std::vector<int> numList={1,2,3,4,5,6,7,8,9,10};

void setup(void) {
    auto cfg = M5.config();
    M5.begin(cfg);
    Serial.begin(9600);
}

void loop(void) {
    M5.update();
    std::random_device seed_gen;
    std::mt19937 engine(seed_gen());
    std::shuffle(numList.begin(), numList.end(), engine);
    printVector(numList);
}

開発環境

  • Windows11 + platformIO
  • M5Stack Core2

解説

ESP32 random_deviceでググったところ以下の記事が出てきた。こちらの記事を参考に解説していく。

cppreferenceのrandom_deviceの解説によると、

std::random_device may be implemented in terms of an implementation-defined pseudo-random number engine if a non-deterministic source (e.g. a hardware device) is not available to the implementation. In this case each std::random_device object may generate the same number sequence.

random_deviceは非決定的なソースが利用できない場合に疑似乱数で実装されるため、同じ数列を生成する。非決定的なソースとは、主にPCの温度など。
疑似乱数を使っている場合はエントロピーが0になるので、試しに、マイコンなどでも非決定的なソースは存在するが、マイコンで使用されることは想定されてないようだ。

Serial.println(seed_gen.entropy());でエントロピーを表示すると0.0になっていた。
Serial.println(seed_gen());してランダムシードの値をプリントすると、

3499211612
3499211612
3499211612
3499211612
3499211612
....

ずっと同じ数列が生成されている。これでは確かにランダムにはならない。

では、どうするかというと、ESP32にはハードウェア乱数を生成するAPIesp_random()があるため、こちらを使ってくださいとのこと。WifiやBluetoothのノイズや、SoCs上のノイズをADCで読みだしたものなどを使って乱数が生成される。

program
#include <M5Unified.h>
#include <algorithm>
#include <iostream>
#include <random>

template <typename T>
void printVector(std::vector<T> v) {
  for (const auto &item : v) {
    std::cout << item << "; ";
  }
  std::cout << std::endl;
}
std::vector<int> numList={1,2,3,4,5,6,7,8,9,10};

void setup(void) {
    auto cfg = M5.config();
    M5.begin(cfg);
    Serial.begin(9600);
}

void loop(void) {
    M5.update();
    std::mt19937 engine(esp_random());
    std::shuffle(numList.begin(), numList.end(), engine);
    printVector(numList);
}
result
10; 9; 2; 8; 6; 7; 3; 4; 5; 1;
3; 4; 10; 8; 5; 6; 1; 7; 2; 9; 
8; 6; 7; 4; 10; 1; 3; 2; 5; 9;
8; 10; 7; 3; 1; 4; 2; 6; 9; 5; 
10; 8; 2; 4; 6; 7; 1; 9; 3; 5;
3; 8; 2; 4; 9; 10; 7; 5; 1; 6; 
8; 1; 2; 6; 7; 3; 5; 4; 9; 10;
10; 3; 4; 9; 2; 6; 1; 5; 7; 8; 
6; 2; 10; 9; 7; 4; 8; 5; 1; 3;
1; 9; 5; 7; 8; 3; 2; 4; 6; 10; 
2; 1; 4; 3; 8; 7; 5; 6; 9; 10;
4; 5; 1; 6; 10; 7; 9; 3; 2; 8; 
5; 10; 6; 9; 4; 1; 3; 7; 2; 8;
3; 5; 4; 10; 7; 6; 9; 2; 1; 8; 
5; 3; 7; 4; 10; 2; 8; 6; 9; 1;

結論

シード値としてstd::random_deviceではなくesp_random()を使おう

コメント追記

最新のESP32の開発環境ではハードウェアランダムに対応しているのではないか?というコメントをいただきました

色々調査してみたところ、M5stackCore2の開発環境では、randomの内蔵されているパッケージのバージョンがtoolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5となっており、
試しに、貼っていただいたリンクのバージョンに近いtoolchain-xtensa-esp32@12.2.0+20230208を入れてみましたが、コンパイルエラーで動かずでした。

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0