【问题标题】:Picking a Random Object in an NSArray在 NSArray 中选择一个随机对象
【发布时间】:2010-07-23 14:08:49
【问题描述】:

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

【问题讨论】:

  • 这里的所有答案都是正确的,但有关更多最新解决方案,请参阅我的答案here。它使用arc4random_uniform 方法来避免模偏差。
  • 不是这个问题的答案,而是一个有趣的点 - 其他 Foundation 集合 (NSSet NSHashTable) 具有从 Set/HashTable 读取任意(随机)对象的方法“anyObject”。可以按照以下建议在 NSArray 的扩展中实现此方法。

标签: objective-c cocoa


【解决方案1】:

@Darryl 的回答是正确的,但可以进行一些小的调整:

NSUInteger randomIndex = arc4random() % theArray.count;

修改:

  • rand()random() 上使用arc4random() 更简单,因为它不需要播种(调用srand()srandom())。
  • modulo operator (%) 使整个语句更短,同时在语义上也更清晰。

【讨论】:

  • 来自 arc4random 手册页:建议使用 arc4random_uniform() 而非 ``arc4random() % upper_bound'' 之类的结构,因为当上限不是 2 的幂时,它可以避免“模偏差”。跨度>
  • @collibhoy 不,因为0 % 4 = 01 % 4 = 12 % 4 = 23 % 4 = 34 % 4 = 05 % 4 = 1...如果你以 n为模>,你的最大结果永远不会大于n-1
  • @DaveDeLong 根据源码,count是一个属性:@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration> @property (readonly) NSUInteger count;
【解决方案2】:

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

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

有必要检查count,因为非nil 但为空的NSArray 将为count 返回0,而arc4random_uniform(0) 返回0。因此,如果不进行检查,您将超出数组的范围。

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

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

供参考,这里是documentation

u_int32_t
arc4random_uniform(u_int32_t upper_bound);

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

0 作为upper_bound 传递时,手册页没有提到arc4random_uniform 返回0

另外,arc4random_uniform 定义在 <stdlib.h> 中,但在我的 iOS 测试程序中添加 #import 不是必需的。

【讨论】:

    【解决方案3】:

    大概是这样的:

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

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

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

    【讨论】:

      【解决方案4】:
      @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
      

      编辑:针对 Xcode 7 更新。泛型、可空性

      【讨论】:

        【解决方案5】:

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

        #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 如果您想了解更多细节,可以在 iPhone 上使用SecRandomCopyBytes() 获取加密有用的随机数。在 Mac 上,您可以直接访问 /dev/random。
        • 我认为问题的重点是展示如何从数组中选择一个随机项,而这个答案并没有真正提供最好的信息。
        • 我很喜欢这个笑话,但我投了反对票以帮助那些不明白的人。
        【解决方案6】:
         srand([[NSDate date]  timeIntervalSince1970]);
        
         int inx =rand()%[array count];
        

        inx 是随机数。

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

        【讨论】:

          【解决方案7】:
          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];
          

          【讨论】:

            【解决方案8】:

            在 Swift 4 中:

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

            【讨论】:

            • 请查看How do I write a good answer。不鼓励仅使用代码的答案,因为它们没有解释如何解决问题中的问题。您应该更新您的答案,以解释它的作用以及它如何改进这个 7 年前的问题已有的许多答案
            猜你喜欢
            • 1970-01-01
            • 2015-11-10
            • 2021-03-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多