Fans实验室2:1亿次随机生成大乐透是否会中奖

1. 实验背景

最近好奇大乐透中奖概率,所以用C++写了一个程序模拟随机选号过程。程序会随机生成大乐透号码(前区5个1-35的数字,后区2个1-12的数字),并与目标号码(2023年7月14日开奖号码:前区02,14,32,34,35,后区05,11)进行对比,看看需要多少次才能"中奖"。

2. 关键技术点

2.1 CPU绑定

在多核系统中,为了获得稳定的性能,我们通常需要将程序绑定到特定的CPU核心上运行。这可以避免进程在不同核心之间切换带来的性能损耗。

void pin_thread_to_cpu(int cpu_id) {
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);     // 清空CPU集合
    CPU_SET(cpu_id, &cpuset);   // 设置要使用的CPU
    pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);  // 绑定线程到指定CPU
}

函数说明:

  • cpu_set_t:表示CPU集合的数据类型
  • CPU_ZERO:初始化CPU集合,将所有位置零
  • CPU_SET:将指定的CPU添加到集合中
  • pthread_setaffinity_np:将当前线程绑定到指定的CPU集合

2.2 随机数生成

使用C++11引入的随机数生成器,比传统的rand()函数具有更好的随机性:

std::random_device rd;  // 硬件随机数生成器
std::mt19937 gen(rd());  // Mersenne Twister伪随机数生成器
std::uniform_int_distribution<> dis_front(1, 35);  // 前区分布
std::uniform_int_distribution<> dis_back(1, 12);   // 后区分布

2.3 性能统计

使用chrono库进行时间统计,计算程序执行速度:

auto start_time = std::chrono::high_resolution_clock::now();
// ...执行代码...
auto current_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::seconds>(current_time - start_time).count();

3. 实验结果

➜  build ./lottery_simulator 100000000
尝试次数: 100000 / 100000000, 已用时间: 0 秒, 速度: 100000 次/秒
尝试次数: 200000 / 100000000, 已用时间: 0 秒, 速度: 200000 次/秒
尝试次数: 300000 / 100000000, 已用时间: 0 秒, 速度: 300000 次/秒
尝试次数: 400000 / 100000000, 已用时间: 0 秒, 速度: 400000 次/秒
尝试次数: 500000 / 100000000, 已用时间: 0 秒, 速度: 500000 次/秒
尝试次数: 600000 / 100000000, 已用时间: 0 秒, 速度: 600000 次/秒
尝试次数: 700000 / 100000000, 已用时间: 0 秒, 速度: 700000 次/秒
尝试次数: 800000 / 100000000, 已用时间: 0 秒, 速度: 800000 次/秒
尝试次数: 900000 / 100000000, 已用时间: 0 秒, 速度: 900000 次/秒
......
尝试次数: 28900000 / 100000000, 已用时间: 28 秒, 速度: 1032142 次/秒
尝试次数: 29000000 / 100000000, 已用时间: 28 秒, 速度: 1035714 次/秒
尝试次数: 29100000 / 100000000, 已用时间: 28 秒, 速度: 1039285 次/秒

经过 29163768 次尝试后匹配成功!
生成的前区号码: 2 14 32 34 35 
生成的后区号码: 5 11 

4. 结论

通过这个实验,我们可以直观地感受到大乐透一等奖的中奖难度 差不多和真实中奖差不多

5. 完整代码

#include <iostream>
#include <vector>
#include <random>
#include <algorithm>
#include <thread>
#include <chrono>
#include <sched.h>
#include <string>

// 将线程绑定到指定CPU核心
void pin_thread_to_cpu(int cpu_id) {
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(cpu_id, &cpuset);
    pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
}

// 比较两个向量是否相等
bool compare_vectors(const std::vector<int>& v1, const std::vector<int>& v2) {
    if (v1.size() != v2.size()) return false;
    return std::equal(v1.begin(), v1.end(), v2.begin());
}

void print_usage() {
    std::cout << "使用方法:\n"
              << "  ./lottery_simulator [执行次数]\n"
              << "例如:\n"
              << "  ./lottery_simulator 1000000    # 执行100万次\n"
              << "  ./lottery_simulator 0          # 无限执行直到中奖\n";
}

int main(int argc, char* argv[]) {
    // 绑定到CPU核心0
    pin_thread_to_cpu(0);

    // 处理命令行参数
    unsigned long long max_attempts = 0;  // 0表示无限执行
    if (argc > 1) {
        try {
            max_attempts = std::stoull(argv[1]);
        } catch (...) {
            print_usage();
            return 1;
        }
    }

    // 中奖号码 (0714开奖)
    std::vector<int> winning_front = {2, 14, 32, 34, 35};  // 前区号码
    std::vector<int> winning_back = {5, 11};               // 后区号码
    std::sort(winning_front.begin(), winning_front.end());
    std::sort(winning_back.begin(), winning_back.end());

    // 随机数生成器
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis_front(1, 35);  // 前区号码范围1-35
    std::uniform_int_distribution<> dis_back(1, 12);   // 后区号码范围1-12

    unsigned long long attempts = 0;
    std::vector<int> generated_front;  // 生成的前区号码
    std::vector<int> generated_back;   // 生成的后区号码
    generated_front.reserve(5);
    generated_back.reserve(2);

    auto start_time = std::chrono::high_resolution_clock::now();

    while (true) {
        attempts++;
        
        // 检查是否达到最大尝试次数
        if (max_attempts > 0 && attempts > max_attempts) {
            std::cout << "\n达到指定的最大尝试次数 " << max_attempts << " 次,程序退出\n";
            break;
        }

        generated_front.clear();
        generated_back.clear();

        // 生成5个前区号码
        while (generated_front.size() < 5) {
            int num = dis_front(gen);
            if (std::find(generated_front.begin(), generated_front.end(), num) == generated_front.end()) {
                generated_front.push_back(num);
            }
        }

        // 生成2个后区号码
        while (generated_back.size() < 2) {
            int num = dis_back(gen);
            if (std::find(generated_back.begin(), generated_back.end(), num) == generated_back.end()) {
                generated_back.push_back(num);
            }
        }

        std::sort(generated_front.begin(), generated_front.end());
        std::sort(generated_back.begin(), generated_back.end());

        // 每10万次尝试打印一次进度
        if (attempts % 100000 == 0) {
            auto current_time = std::chrono::high_resolution_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::seconds>(current_time - start_time).count();
            std::cout << "尝试次数: " << attempts << " / " 
                     << (max_attempts > 0 ? std::to_string(max_attempts) : "无限") 
                     << ", 已用时间: " << duration << " 秒" 
                     << ", 速度: " << attempts/std::max(1LL, (long long)duration) << " 次/秒" 
                     << std::endl;
        }

        // 检查是否中奖
        if (compare_vectors(generated_front, winning_front) && 
            compare_vectors(generated_back, winning_back)) {
            std::cout << "\n经过 " << attempts << " 次尝试后匹配成功!\n";
            std::cout << "生成的前区号码: ";
            for (int num : generated_front) {
                std::cout << num << " ";
            }
            std::cout << "\n生成的后区号码: ";
            for (int num : generated_back) {
                std::cout << num << " ";
            }
            std::cout << "\n";
            break;
        }
    }

    return 0;
}
cmake_minimum_required(VERSION 3.10)
project(lottery_simulator)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(lottery_simulator main.cpp)

# Link pthread library
target_link_libraries(lottery_simulator pthread)

6. 使用方法

# 编译
mkdir build && cd build
cmake ..
make

# 运行(指定尝试次数)
./lottery_simulator 100000000  # 执行1亿次

基于 Claude 3.5 Sonnet

posted on 2025-07-15 23:08  HDU李少帅  阅读(47)  评论(0)    收藏  举报