You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:41:47