使用Pandas高效统计彩票组合与开奖结果的匹配次数
高效统计彩票组合匹配频次的方案
核心思路
不要逐个组合与开奖数据暴力比对(37万×6万=2.26e10次操作,完全不可行),优先选择向量化运算或编译级循环的方案,结合二进制掩码优化匹配数计算,最大化处理速度。
步骤与实现方案
1. 数据预处理:转换为二进制掩码
将每组5个数字转换成36位整数掩码(每个数字对应一个比特位,数字1对应第0位,数字36对应第35位),这样两组数字的匹配数等价于两个掩码按位与后统计1的个数,计算速度远超集合交集。
import numpy as np from numba import njit, popcount import itertools import pandas as pd # 转换组合为掩码的批量处理函数 def combos_to_masks(combos_np): masks = np.zeros(len(combos_np), dtype=np.int64) for i in range(5): masks |= 1 << (combos_np[:, i] - 1) return masks # 生成所有36选5组合并转掩码 all_combos = np.array(list(itertools.combinations(range(1, 37), 5))) all_masks = combos_to_masks(all_combos) # 开奖数据转掩码(假设draws_np是形状为(60000,5)的numpy数组) # 示例:随机生成开奖数据,实际替换为你的数据 draws_np = np.random.randint(1, 37, size=(60000, 5)) draw_masks = combos_to_masks(draws_np)
2. 最优实现:Numba编译并行循环
利用Numba将Python循环编译为机器码,结合硬件加速的popcount指令统计匹配数,并开启多线程并行,这是当前最快的可行方案。
@njit(parallel=True) def count_matches(all_masks, draw_masks): num_combos = len(all_masks) num_draws = len(draw_masks) result = np.zeros((num_combos, 6), dtype=np.int32) for i in range(num_combos): current_mask = all_masks[i] cnt = np.zeros(6, dtype=np.int32) for j in range(num_draws): # 计算匹配数并累加对应计数 match_num = popcount(current_mask & draw_masks[j]) cnt[match_num] += 1 result[i] = cnt return result # 执行统计 match_counts = count_matches(all_masks, draw_masks)
3. 结果合并到DataFrame
将统计结果合并到你已生成的所有组合DataFrame中:
df_all = pd.DataFrame(all_combos, columns=[f"num_{i+1}" for i in range(5)]) # 新增匹配次数列 df_all[["match_0", "match_1", "match_2", "match_3", "match_4", "match_5"]] = match_counts
4. 备选方案:分块向量化处理
如果无法使用Numba,可以用numpy分块处理开奖数据,避免内存溢出,同时利用向量化运算加速:
# 分块大小,根据内存调整(比如1000条/块) block_size = 1000 num_blocks = len(draw_masks) // block_size + (1 if len(draw_masks) % block_size != 0 else 0) result = np.zeros((len(all_masks), 6), dtype=np.int32) for block_idx in range(num_blocks): start = block_idx * block_size end = min((block_idx + 1) * block_size, len(draw_masks)) block_draw_masks = draw_masks[start:end] # 向量化计算所有组合与当前块开奖数据的匹配数 matches = np.bit_count(all_masks[:, None] & block_draw_masks[None, :], axis=1) # 统计每个组合的匹配数分布并累加 for combo_idx in range(len(all_masks)): hist, _ = np.histogram(matches[combo_idx], bins=[0,1,2,3,4,5,6]) result[combo_idx] += hist print(f"完成块 {block_idx+1}/{num_blocks}")
关键优化点
- 二进制掩码:将数字匹配转换为位运算,比集合交集快一个数量级
- Numba编译:将Python循环转换为机器码,结合多线程并行,利用多核CPU
- 硬件加速指令:使用
popcount指令统计比特位数量,比字符串计数快数倍 - 避免暴力循环:拒绝逐个组合与开奖数据比对,优先选择向量化或编译级方案
内容的提问来源于stack exchange,提问作者Enrico Galli
相关产品推荐
相关产品推荐

