NSArrayでランダムオブジェクトを選択する


83

オブジェクトを含む配列があるとしましょう。 この配列からランダムなオブジェクトを選択するにはどうすればよいですか?


ここでのすべての答えは正しいですが、より最新の解決策については、ここで私の答えを参照してください。このarc4random_uniform方法を使用して、モジュロバイアスを回避します。
アダム

この質問に対する答えではありませんが、興味深い点です。他のFoundationコレクション(NSSet NSHashTable)には、Set / HashTableから任意の(ランダムな)オブジェクトを読み取るメソッド "anyObject"があります。以下の提案に従って、NSArrayの拡張機能にこのメソッドを実装できます。
MottiShneor19年

回答:


192

@Darrylの答えは正しいですが、いくつかのマイナーな調整を使用できます。

NSUInteger randomIndex = arc4random() % theArray.count;

変更:

  • arc4random()overrand()を使用するとrandom()、シード(srand()またはを呼び出すsrandom())が不要なため、より簡単になります。
  • モジュロ演算子は、%また、それは意味的に明確にしながら)、全体的な文は短くなります。

27
arc4randomのマニュアルページから:arc4random_uniform()は、上限が2の累乗でない場合に「モジュロバイアス」を回避するため、「arc4random()%upper_bound」のような構造よりも推奨されます。
マックスヤンコフ2012年

@collibhoyなし、ので0 % 4 = 01 % 4 = 12 % 4 = 23 % 4 = 34 % 4 = 05 % 4 = 1あなたがでモジュロなら... nは、あなたの最大の結果がより大きくなることはありませんN-1
Dave DeLong 2015年

1
@DaveDeLongソースコードによるcountと、プロパティです:@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration> @property (readonly) NSUInteger count;
mikeho 2016

17

これは私が思いつくことができる最も簡単な解決策です:

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

これはチェックする必要がありますcount非ので、nilしかし、空がNSArray返されます0ためcount、および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返さ0れることが記載されていません。0upper_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

編集:Xcode 7用に更新。ジェネリック、null可能性


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 2012年

私はこのジョークがとても好きですが、それを理解できない人を助けるために投票しました。
Stig Brautaset 2013

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

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

inxは乱数です。

ここで、srand()は、ランダムピッキング関数の前のプログラムのどこにあってもかまいません。


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

それをintにキャストしたい場合は、その解決策を次に示します(enum呼び出しをランダム化する場合など、非連続番号の配列からランダムな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 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.