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

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

https://cpprefjp.github.io/reference/algorithm/shuffle.html

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


```c++: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;
```

```C++: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でググったところ以下の記事が出てきた。こちらの記事を参考に解説していく。

https://github.com/espressif/esp-idf/issues/11398

[cppreferenceのrandom_deviceの解説](https://en.cppreference.com/w/cpp/numeric/random/random_device#top)によると、



>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にはハードウェア乱数を生成するAPI``esp_random()``があるため、こちらを使ってくださいとのこと。WifiやBluetoothのノイズや、SoCs上のノイズをADCで読みだしたものなどを使って乱数が生成される。

https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html



```C++: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);
}
```

```C++: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を入れてみましたが、コンパイルエラーで動かずでした。