C++ std::random_device | Hardware entropy for seeding
이 글의 핵심
Use random_device to seed engines—not as your main RNG—understand entropy(), multi-word seeding, and when implementations fall back to PRNG.
Introduction
std::random_device is a hardware-based random number generator introduced in C++11. Generates non-deterministic random numbers using the system’s entropy source (e.g. /dev/urandom), primarily used to seed random number engines or for cryptographic purposes.
1. random_device default
Default Enabled```cpp
#include
int main() { std::random_device rd;
// 난수 생성 (32비트 unsigned int)
for (int i = 0; i < 5; ++i) {
std::cout << rd() << std::endl;
}
return 0;
}
output of power:
3847291038
1029384756
2938475610
4756102938
1847562903
### Seed generationcpp
#include
int main() { // random_device로 시드 생성 std::random_device rd;
// mt19937 엔진 초기화
std::mt19937 gen{rd()};
// 균등 분포
std::uniform_int_distribution<> dist{1, 100};
// 난수 생성 (빠름)
for (int i = 0; i < 10; ++i) {
std::cout << dist(gen) << " ";
}
std::cout << std::endl;
return 0;
}
output of power:
42 17 89 3 56 91 28 64 11 73
## 2. Entropy
### entropy() method```cpp
#include <iostream>
#include <random>
int main() {
std::random_device rd;
// 엔트로피 확인 (비트)
double entropy = rd.entropy();
std::cout << "엔트로피: " << entropy << " bits" << std::endl;
if (entropy == 0.0) {
std::cout << "의사 난수 (결정적)" << std::endl;
} else {
std::cout << "하드웨어 난수 (비결정적)" << std::endl;
}
return 0;
}
```Output (Linux):```
엔트로피: 32 bits
하드웨어 난수 (비결정적)
```### Platform-specific implementation
| platform | entropy source | entropy() |
|--------|-------------|--------------------------|
| Linux | `/dev/urandom` | 32 bits |
| Windows | `CryptGenRandom` | 32 bits |
| macOS | `/dev/urandom` | 32 bits |
| Some Embedded | pseudorandom number | 0 bits |
---
## 3. Seed sequence
### Single seed vs multiple seeds```cpp
#include <random>
#include <array>
#include <algorithm>
int main() {
std::random_device rd;
// ❌ 단일 시드 (32비트만 사용)
std::mt19937 gen1{rd()};
// ✅ 여러 시드 (더 좋은 초기화)
std::array<unsigned int, std::mt19937::state_size> seedData;
std::generate(seedData.begin(), seedData.end(), std::ref(rd));
std::seed_seq seq(seedData.begin(), seedData.end());
std::mt19937 gen2{seq};
return 0;
}
```Description:
- The state size of `mt19937` is 624 32-bit integers (19,968 bits).
- A single seed uses only 32 bits (the rest is filled with the algorithm)
- Multiple seeds provide more entropy
---
## 4. Practical example
### Example 1: Cryptographically random numbers```cpp
#include <random>
#include <vector>
#include <iostream>
#include <iomanip>
std::vector<unsigned char> generateKey(size_t length) {
std::random_device rd;
std::vector<unsigned char> key(length);
for (auto& byte : key) {
byte = static_cast<unsigned char>(rd() % 256);
}
return key;
}
int main() {
// 16바이트 키 생성
auto key = generateKey(16);
std::cout << "암호 키: ";
for (auto byte : key) {
std::cout << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<int>(byte);
}
std::cout << std::endl;
return 0;
}
```output of power:```
암호 키: 3f7a9b2c8d1e4f6a5b3c9d2e7f1a4b8c
```### Example 2: UUID generation```cpp
#include <random>
#include <sstream>
#include <iomanip>
#include <iostream>
std::string generateUUID() {
std::random_device rd;
std::mt19937 gen{rd()};
std::uniform_int_distribution<> dist{0, 15};
std::uniform_int_distribution<> dist2{8, 11};
std::ostringstream oss;
oss << std::hex;
// 8-4-4-4-12 형식
for (int i = 0; i < 8; ++i) oss << dist(gen);
oss << "-";
for (int i = 0; i < 4; ++i) oss << dist(gen);
oss << "-4"; // 버전 4
for (int i = 0; i < 3; ++i) oss << dist(gen);
oss << "-";
oss << dist2(gen); // 변형 (8, 9, a, b)
for (int i = 0; i < 3; ++i) oss << dist(gen);
oss << "-";
for (int i = 0; i < 12; ++i) oss << dist(gen);
return oss.str();
}
int main() {
for (int i = 0; i < 3; ++i) {
std::cout << generateUUID() << std::endl;
}
return 0;
}
```output of power:```
550e8400-e29b-41d4-a716-446655440000
6ba7b810-9dad-11d1-80b4-00c04fd430c8
3d813cbb-47fb-32ba-91df-831e1593ac29
```### Example 3: Token generation```cpp
#include <random>
#include <string>
#include <iostream>
std::string generateToken(size_t length) {
std::random_device rd;
std::mt19937 gen{rd()};
const std::string chars =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
std::uniform_int_distribution<> dist{0, static_cast<int>(chars.size() - 1)};
std::string token;
token.reserve(length);
for (size_t i = 0; i < length; ++i) {
token += chars[dist(gen)];
}
return token;
}
int main() {
// 32자 토큰 생성
std::cout << "토큰: " << generateToken(32) << std::endl;
return 0;
}
```output of power:```
토큰: 7aB3xK9mP2qW5nL8vC1dF4jH6rT0yU3z
```---
## 5. Frequently occurring problems
### Issue 1: Performance```cpp
#include <random>
#include <chrono>
#include <iostream>
void benchmarkRandomDevice() {
std::random_device rd;
auto start = std::chrono::high_resolution_clock::now();
// ❌ random_device 직접 사용 (매우 느림)
for (int i = 0; i < 1000000; ++i) {
volatile unsigned int r = rd();
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "random_device: " << duration.count() << "ms" << std::endl;
}
void benchmarkMT19937() {
std::random_device rd;
std::mt19937 gen{rd()};
auto start = std::chrono::high_resolution_clock::now();
// ✅ mt19937 사용 (빠름)
for (int i = 0; i < 1000000; ++i) {
volatile unsigned int r = gen();
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "mt19937: " << duration.count() << "ms" << std::endl;
}
int main() {
benchmarkRandomDevice(); // ~5000ms
benchmarkMT19937(); // ~50ms
return 0;
}
```output of power:```
random_device: 5234ms
mt19937: 47ms
```### Issue 2: Platform Dependency```cpp
#include <random>
#include <iostream>
int main() {
std::random_device rd;
// 엔트로피 확인
double entropy = rd.entropy();
if (entropy == 0.0) {
std::cerr << "경고: random_device가 의사 난수를 사용합니다." << std::endl;
std::cerr << "플랫폼: 하드웨어 난수 미지원" << std::endl;
// 대안: 시간 기반 시드
auto now = std::chrono::high_resolution_clock::now();
auto seed = now.time_since_epoch().count();
std::mt19937 gen{static_cast<unsigned int>(seed)};
} else {
std::cout << "하드웨어 난수 사용 (엔트로피: " << entropy << " bits)" << std::endl;
}
return 0;
}
```### Issue 3: Seed quality```cpp
#include <random>
#include <array>
#include <algorithm>
// ❌ 단일 시드 (품질 낮음)
void poorSeeding() {
std::random_device rd;
std::mt19937 gen{rd()}; // 32비트만 사용
}
// ✅ 여러 시드 (품질 높음)
void goodSeeding() {
std::random_device rd;
// mt19937 상태 크기만큼 시드 생성
std::array<unsigned int, std::mt19937::state_size> seedData;
std::generate(seedData.begin(), seedData.end(), std::ref(rd));
std::seed_seq seq(seedData.begin(), seedData.end());
std::mt19937 gen{seq};
}
// ✅ 간단한 여러 시드
void simpleGoodSeeding() {
std::random_device rd;
// 8개 시드 (256비트)
std::array<unsigned int, 8> seedData;
for (auto& seed : seedData) {
seed = rd();
}
std::seed_seq seq(seedData.begin(), seedData.end());
std::mt19937 gen{seq};
}
```### Issue 4: Reproducibility```cpp
#include <random>
#include <iostream>
// ❌ 재현 불가 (디버깅 어려움)
void nonReproducible() {
std::random_device rd;
std::mt19937 gen{rd()}; // 매번 다른 시드
std::uniform_int_distribution<> dist{1, 100};
std::cout << dist(gen) << std::endl; // 매번 다른 결과
}
// ✅ 재현 가능 (디버깅 용이)
void reproducible(bool debug = false) {
std::mt19937 gen;
if (debug) {
gen.seed(42); // 고정 시드
} else {
std::random_device rd;
gen.seed(rd()); // 랜덤 시드
}
std::uniform_int_distribution<> dist{1, 100};
std::cout << dist(gen) << std::endl;
}
int main() {
std::cout << "디버그 모드:" << std::endl;
reproducible(true); // 항상 같은 결과
reproducible(true); // 항상 같은 결과
std::cout << "\n프로덕션 모드:" << std::endl;
reproducible(false); // 매번 다른 결과
reproducible(false); // 매번 다른 결과
return 0;
}
```---
## 6. Practical example: random number utility```cpp
#include <random>
#include <string>
#include <vector>
#include <array>
#include <algorithm>
class RandomUtils {
std::mt19937 gen;
public:
// 생성자: 고품질 시드
RandomUtils() {
std::random_device rd;
std::array<unsigned int, 8> seedData;
std::generate(seedData.begin(), seedData.end(), std::ref(rd));
std::seed_seq seq(seedData.begin(), seedData.end());
gen.seed(seq);
}
// 범위 내 정수
int randomInt(int min, int max) {
std::uniform_int_distribution<> dist{min, max};
return dist(gen);
}
// 범위 내 실수
double randomDouble(double min, double max) {
std::uniform_real_distribution<> dist{min, max};
return dist(gen);
}
// 랜덤 문자열
std::string randomString(size_t length) {
const std::string chars =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
std::uniform_int_distribution<> dist{0, static_cast<int>(chars.size() - 1)};
std::string result;
result.reserve(length);
for (size_t i = 0; i < length; ++i) {
result += chars[dist(gen)];
}
return result;
}
// 랜덤 바이트
std::vector<unsigned char> randomBytes(size_t length) {
std::uniform_int_distribution<> dist{0, 255};
std::vector<unsigned char> bytes(length);
for (auto& byte : bytes) {
byte = static_cast<unsigned char>(dist(gen));
}
return bytes;
}
// 배열 셔플
template<typename T>
void shuffle(std::vector<T>& vec) {
std::shuffle(vec.begin(), vec.end(), gen);
}
};
int main() {
RandomUtils rng;
// 정수
std::cout << "랜덤 정수 (1-100): " << rng.randomInt(1, 100) << std::endl;
// 실수
std::cout << "랜덤 실수 (0-1): " << rng.randomDouble(0.0, 1.0) << std::endl;
// 문자열
std::cout << "랜덤 문자열: " << rng.randomString(16) << std::endl;
// 바이트
auto bytes = rng.randomBytes(8);
std::cout << "랜덤 바이트: ";
for (auto byte : bytes) {
std::cout << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<int>(byte) << " ";
}
std::cout << std::endl;
// 셔플
std::vector<int> vec = {1, 2, 3, 4, 5};
rng.shuffle(vec);
std::cout << "셔플: ";
for (int x : vec) std::cout << x << " ";
std::cout << std::endl;
return 0;
}
```output of power:```
랜덤 정수 (1-100): 73
랜덤 실수 (0-1): 0.642857
랜덤 문자열: aB7xK3mP9qW2nL5v
랜덤 바이트: 3f 7a 9b 2c 8d 1e 4f 6a
셔플: 3 1 5 2 4
```---
## Cleanup
### Key takeaways
1. random_device: Hardware-based non-deterministic random numbers
2. Use: Seed generation, cryptographic random numbers
3. Performance: Slow (only used as seed)
4. entropy(): non-determinism measure (pseudo-random if 0)
5. Seed Quality: Initialize with multiple seeds
6. Reproducibility: Use fixed seeds when debugging
### random_device vs mt19937
| Features | random_device | mt19937 |
|------|--------------|---------|
| Speed | very slow | Fast |
| Quality | hardware random numbers | pseudorandom number |
| Use | seed generation | general random number |
| Reproducibility | Not possible | Possible (seed fixed) |
| Platform | Dependent | Independent |
### Practical tips
Principle of use:
- `random_device` is only used for seed generation
- Actual random numbers use engines such as `mt19937`
- For cryptographic purposes, consider using `random_device` directly.
- Initialize engine with multiple seeds (improves quality)
Performance:
- `random_device` uses system call (slow)
- Engine is memory based (fast)
- For large random numbers, an engine must be used.
Debugging:
- Use fixed seed when debugging
- `random_device` seed in production
- Verify platform by checking `entropy()`
### Next steps
- [C++ Distribution](/blog/cpp-distribution/)
- [C++ Random](/blog/cpp-random/)
- [C++ Algorithm Generate](/blog/cpp-algorithm-generate/)
---
## Related articles
- [C++ Distribution | ](/blog/cpp-distribution/)
- [C++ random number generation | ](/blog/cpp-random-guide/)
- [C++ Random | ](/blog/cpp-random/)
- [C++ async & launch | ](/blog/cpp-async-launch/)
- [C++ Atomic Operations | ](/blog/cpp-atomic-operations/)