生成带指定概率的无重复元素随机长度Int数组技术实现求助
解决Swift中带权重的不重复随机数生成问题
嘿,我来帮你搞定这个需求!核心目标是生成指定次数(0-8次)的不重复带权重随机数,你的原代码因为每次都用完整的概率数组,所以会出现重复值。我们只需要调整逻辑,维护一个「可用候选池」,每次选中数字后就把它从池子里移除,就能避免重复啦。
完整优化代码
import UIKit // 把随机数生成函数移到循环外,避免重复定义(Swift不建议在循环内定义函数) func weightedRandom(from candidates: [(number: Int, probability: Double)]) -> Int? { guard !candidates.isEmpty else { return nil } // 提取概率数组并计算总和 let probabilities = candidates.map { $0.probability } let totalProbability = probabilities.reduce(0, +) let randomValue = totalProbability * Double(arc4random_uniform(UInt32.max)) / Double(UInt32.max) var accumulatedProbability = 0.0 for (_, candidate) in candidates.enumerated() { accumulatedProbability += candidate.probability if randomValue < accumulatedProbability { return candidate.number } } // 兜底返回最后一个候选(理论上不会走到这里) return candidates.last?.number } // 生成0-8之间的随机数n(arc4random_uniform(9)会返回0到8的整数) let n = Int(arc4random_uniform(9)) var resultArray: [Int] = [] // 初始化可用候选池:数字0-9对应各自的概率 var availableCandidates: [(number: Int, probability: Double)] = [ (0, 0.2), (1, 0.3), (2, 0.2), (3, 0.3), (4, 0.2), (5, 0.1), (6, 0.2), (7, 0.4), (8, 0.2), (9, 0.2) ] // 循环n次生成不重复随机数 for _ in 0..<n { guard let selectedNumber = weightedRandom(from: availableCandidates) else { break // 如果候选池为空(n>10时会触发,但这里n最大是8,所以基本不会执行) } resultArray.append(selectedNumber) // 从候选池中移除已选中的数字,确保后续不会重复选中 availableCandidates = availableCandidates.filter { $0.number != selectedNumber } } print(resultArray)
关键改动说明
- 函数移到循环外:原代码在
for循环内定义randomNumber函数,这是Swift的不良实践,会导致重复定义,我们把它移到全局作用域。 - 维护可用候选池:用元组数组
availableCandidates保存当前可以被选中的数字和对应的概率,每次选中后用filter移除该数字,彻底排除重复可能。 - 循环次数修正:原代码用
0...n会执行n+1次循环,如果你需要严格循环n次,改用0..<n即可(注释里已说明)。 - 健壮性处理:加入了候选池为空的判断,避免出现意外崩溃。
补充说明
如果你不想维护新的候选池数组,也可以通过把已选数字的概率设为0来实现,但这种方式效率略低(因为每次还是会遍历所有数字)。相比之下,直接移除候选的方式更直观高效。
内容的提问来源于stack exchange,提问作者Harper Creek
相关产品推荐
相关产品推荐

