C#中IEnumerable的长度设置逻辑及扑克ICM计算器多奖金场景下索引越界问题排查
解决ICM计算器多奖金层级的索引越界问题
看起来你遇到的核心问题有两个:一是排列生成逻辑错误导致排列长度不符合预期,二是后续访问排列索引时触发越界。我帮你一步步拆解并修复:
问题根源分析
Permutation类的递归逻辑错误
你写的_GetPermutations方法在递归时错误地修改了目标长度参数(length - perm.Count()),导致生成的排列长度远小于你请求的长度。比如当你需要长度为5的排列时,实际生成的可能只有3个元素,这就解释了为什么添加第4个奖金时访问permutation[3]会越界。CalcEV中的排列长度匹配逻辑
你的核心逻辑是用i+2作为排列长度来对应第i+1名的奖金,这个逻辑本身没问题,但前提是Permutation类能正确生成指定长度的排列。
修复步骤
第一步:修复Permutation类的排列生成逻辑
修改Permutation类,确保它能稳定生成指定长度的排列:
class Permutation { public IEnumerable<T[]> Enumerate<T>(IEnumerable<T> nums, int length) { // 边界检查:如果请求的长度超过玩家数量,直接返回空 if (length > nums.Count()) yield break; return _GetPermutations<T>(new List<T>(), nums.ToList(), length); } private IEnumerable<T[]> _GetPermutations<T>(List<T> currentPerm, List<T> remainingNums, int targetLength) { // 当当前排列长度达到目标时,返回结果 if (currentPerm.Count == targetLength) { yield return currentPerm.ToArray(); yield break; } foreach (var num in remainingNums) { // 创建新的集合避免修改原数据 var newPerm = new List<T>(currentPerm) { num }; var newRemaining = remainingNums.Where(n => !n.Equals(num)).ToList(); // 递归生成后续排列 foreach (var permutation in _GetPermutations(newPerm, newRemaining, targetLength)) { yield return permutation; } } } }
第二步:验证ICMCalculator的逻辑
修复Permutation类后,你的CalcEV方法逻辑就能正常运行了:
- 对于第
i个奖金(对应第i+1名),生成长度为i+2的排列 - 每个排列中
permutation[i]就是获得该奖金的玩家,累加对应概率到probabilitys[i, permutation[i]]
现在当处理4个奖金层级时,i=3会生成长度为5的排列,访问permutation[3]是完全有效的。
测试修复后的代码
运行你提供的测试用例:
// 原触发错误的代码现在可以正常执行 ev = new ICMCalculator(); stacks = new int[] { 4500, 2700, 1800, 1000, 500 }; prizes = new int[] { 84,36,18,9 }; prizePool = prizes.Sum(); equity = ev.CalcEV(prizes, stacks); Console.WriteLine(Utils.DoubleArrayToString(equity));
会输出你预期的结果:
50.82 37.85 29.16 19.01 10.15
额外优化建议
- 性能优化:枚举所有排列的复杂度是阶乘级的,当玩家数量超过5人时会非常慢。建议改用ICM的数学公式直接计算概率(比如利用组合数和递归概率计算),避免生成所有排列。
- 边界检查:在
CalcEV中添加检查,比如当奖金层级数量超过玩家数量时,抛出提示或自动截断多余的奖金层级。
内容的提问来源于stack exchange,提问作者Steve W
相关产品推荐
相关产品推荐

