在NSArray中选择随机对象


83

假设我有一个包含对象1、2、3和4的 数组。我如何从该数组中选择一个随机对象?


此处的所有答案都是正确的,但有关最新解决方案的信息,请参见此处。它使用该arc4random_uniform方法来避免模偏差。
亚当,

不是这个问题的答案,而是有趣的一点-其他Foundation集合(NSSet NSHashTable)的方法“ anyObject”可从Set / HashTable中读取任意(随机)对象。可以按照以下建议在对NSArray的扩展中实现此方法。
Motti Shneor '19

Answers:


192

@Darryl的答案是正确的,但可以使用一些细微的调整:

NSUInteger randomIndex = arc4random() % theArray.count;

修改内容:

  • 使用arc4random()overrand()random()比较简单,因为它不需要种子(调用srand()srandom())。
  • 模运算符%)使得整体更短的语句,同时也使得它在语义上更清晰。

27
从arc4random手册页:建议在诸如``arc4random()%upper_bound''之类的结构上使用arc4random_uniform(),因为当上限不是2的幂时,它会避免出现``模偏差''。
Max Yankov 2012年

@collibhoy没有,因为0 % 4 = 01 % 4 = 12 % 4 = 23 % 4 = 34 % 4 = 05 % 4 = 1...如果通过模ñ,您最大的结果决不会大于n-1个
Dave DeLong 2015年

1
@DaveDeLong根据源代码,count是一个属性:@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration> @property (readonly) NSUInteger count;
mikeho '16

17

这是我能想到的最简单的解决方案:

id object = array.count == 0 ? nil : array[arc4random_uniform(array.count)];

这是必要的检查count,因为非nil却空无一人NSArray将返回0count,和arc4random_uniform(0)回报0。因此,没有检查,您将超出数组的范围。

这个解决方案很诱人,但却是错误的,因为它将导致空数组崩溃:

id object = array[arc4random_uniform(array.count)];

供参考,这里是文档

u_int32_t
arc4random_uniform(u_int32_t upper_bound);

arc4random_uniform() will return a uniformly distributed random number less than upper_bound.

手册页没有提到当以传递时arc4random_uniform返回。00upper_bound

此外,arc4random_uniform在中定义了<stdlib.h>,但#import在我的iOS测试程序中无需添加。


11

也许是这样的:

NSUInteger randomIndex = (NSUInteger)floor(random()/RAND_MAX * [theArray count]);

不要忘记初始化随机数生成器(例如,srandomdev())。

注意:根据以下答案,我已经更新为使用-count而不是点语法。


9
@interface NSArray<ObjectType>  (Random)
- (nullable ObjectType)randomObject;
@end

@implementation NSArray (Random)

- (nullable id)randomObject
{
    id randomObject = [self count] ? self[arc4random_uniform((u_int32_t)[self count])] : nil;
    return randomObject;
}

@end

编辑:更新为Xcode7。泛型,可为空


1

生成一个随机数并将其用作索引。例:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSArray *array = [NSArray arrayWithObjects: @"one", @"two", @"three", @"four", nil];
        NSUInteger randomNumber;
        int fd = open("/dev/random", O_RDONLY);
        if (fd != -1) {
            read(fd, &randomNumber, sizeof(randomNumber));
            close(fd);
        } else {
            fprintf(stderr, "Unable to open /dev/random: %s\n", strerror(errno));
            return -1;
        }
        double scaledRandomNumber = ((double)randomNumber)/NSUIntegerMax * [array count];
        NSUInteger randomIndex = (NSUInteger)floor(scaledRandomNumber);
        NSLog(@"random element: %@", [array objectAtIndex: randomIndex]);
    }
    return 0;
}

@Joshua如果您想了解更多细节,SecRandomCopyBytes()无论如何都可以在iPhone上使用密码学上有用的随机数。在Mac上,您可以直接访问/ dev / random。

我认为问题的重点是显示如何从数组中选择随机项目,而这个答案并不能真正提供最佳信息。
beakr

我很喜欢这个笑话,但是我拒绝了这个笑话,以帮助那些不明白的人。
Stig Brautaset

0
 srand([[NSDate date]  timeIntervalSince1970]);

 int inx =rand()%[array count];

inx是随机数。

其中,srand()可以位于程序中随机选择函数之前的任何位置。


0
ObjectType *objectVarName = [array objectAtIndex:arc4random_uniform((int)(array.count - 1))];

如果您想将其转换为int,这是解决方案(当您需要从非序数数组中随机获取int时(在将枚举调用随机化的情况下,等等)很有用)

int intVarName = (int)[(NSNumber *)[array objectAtIndex:arc4random_uniform((int)(array.count - 1))] integerValue];

0

在Swift 4中:

let array = ["one","two","three","four"]
let randomNumber = arc4random_uniform(UInt32(array.count))

array[Int(randomNumber)]

1
请查看我如何写一个好的答案。不建议使用仅代码的答案,因为它们没有解释如何解决问题中的问题。您应该更新您的答案,以解释此功能以及如何改善这个已有7年历史的问题的许多答案
FluffyKitten
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.