iOS开发:如何实现范围内不重复随机数,用完后可循环?
解决iOS中获取不重复随机数(用完循环重置)的问题
这问题我之前做激励类APP的时候也碰到过!直接用arc4random_uniform(100)确实容易出现连续重复的情况,尤其是用户频繁触发激励的时候体验很差。给你两种简单又靠谱的实现思路,适配你0-99范围的需求:
方法一:洗牌数组池法(推荐)
这种方法是最直观且高效的,适合小范围(如0-99)的场景。核心思路是:
- 初始化一个包含所有目标数字的数组
- 对数组进行洗牌(打乱顺序)
- 每次取数时从数组末尾移除一个元素返回
- 当数组为空时,重新初始化并洗牌,开启新一轮循环
Swift 代码实现
class UniqueRandomGenerator { private var availableNumbers: [Int] // 初始化时传入范围,也可以直接写死0...99 init(range: ClosedRange<Int>) { availableNumbers = Array(range) shufflePool() } // Fisher-Yates洗牌算法,比系统默认shuffle更可控(系统的也完全够用) private func shufflePool() { for i in stride(from: availableNumbers.count - 1, through: 1, by: -1) { let randomIndex = Int(arc4random_uniform(UInt32(i + 1))) availableNumbers.swapAt(i, randomIndex) } } func getNextNumber() -> Int { // 池为空时重置并洗牌 if availableNumbers.isEmpty { availableNumbers = Array(0...99) shufflePool() } // 移除并返回最后一个元素(效率比移除第一个高) return availableNumbers.removeLast() } } // 使用示例 let generator = UniqueRandomGenerator(range: 0...99) // 每次调用都能拿到不重复的数,用完一轮自动重置 let nextIncentiveIndex = generator.getNextNumber()
Objective-C 代码实现
@interface UniqueRandomGenerator : NSObject - (NSInteger)getNextNumber; @end @implementation UniqueRandomGenerator { NSMutableArray *_availableNumbers; } - (instancetype)init { self = [super init]; if (self) { [self resetAndShufflePool]; } return self; } - (void)resetAndShufflePool { _availableNumbers = [NSMutableArray arrayWithCapacity:100]; for (NSInteger i = 0; i < 100; i++) { [_availableNumbers addObject:@(i)]; } // Fisher-Yates洗牌 NSUInteger count = _availableNumbers.count; for (NSInteger i = count - 1; i > 0; i--) { NSUInteger randomIndex = arc4random_uniform((UInt32)i + 1); [_availableNumbers exchangeObjectAtIndex:i withObjectAtIndex:randomIndex]; } } - (NSInteger)getNextNumber { if (_availableNumbers.count == 0) { [self resetAndShufflePool]; } NSNumber *number = [_availableNumbers lastObject]; [_availableNumbers removeLastObject]; return number.integerValue; } @end
这种方法的优势在于绝对不会出现重复,直到一轮所有数字用完,而且没有重试逻辑,性能非常稳定,完全适配激励语句的使用场景。
方法二:已使用集合校验法
如果不想维护完整的数字池,可以用集合记录已使用过的数字,生成随机数后校验是否已使用,未使用则返回并加入集合,当集合大小等于范围总数时清空集合重置。
Swift 代码示例
class SimpleUniqueGenerator { private let maxNumber: Int = 99 private var usedNumbers = Set<Int>() func getNextNumber() -> Int { // 所有数字都用过了,重置集合 if usedNumbers.count == maxNumber + 1 { usedNumbers.removeAll() } var randomNum: Int // 循环生成直到拿到未使用的数字 repeat { randomNum = Int(arc4random_uniform(UInt32(maxNumber + 1))) } while usedNumbers.contains(randomNum) usedNumbers.insert(randomNum) return randomNum } }
这种方法代码更简洁,但在快用完一轮时,可能会多次重试生成随机数,效率稍低。如果你的范围很小(如0-99),其实影响可以忽略,但还是更推荐第一种方法。
内容的提问来源于stack exchange,提问作者Enes
相关产品推荐
相关产品推荐

