C++ std::random_device | Hardware entropy for seeding

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 #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 #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/)