Chọn một đối tượng ngẫu nhiên trong một NSArray


83

Giả sử tôi có một mảng với các đối tượng, 1, 2, 3 và 4. Làm cách nào để chọn một đối tượng ngẫu nhiên từ mảng này?


Tất cả các câu trả lời ở đây đều đúng, nhưng để biết thêm giải pháp cập nhật, hãy xem câu trả lời của tôi tại đây . Nó sử dụng arc4random_uniformphương pháp này để tránh sai lệch modulo.
Adam

không phải là câu trả lời cho câu hỏi này, mà là một điểm thú vị - các bộ sưu tập Foundation khác (NSSet NSHashTable) có các phương thức "anyObject" để đọc một đối tượng tùy ý (ngẫu nhiên) từ Set / HashTable. Người ta có thể triển khai phương pháp này trong một phần mở rộng cho NSArray, theo các gợi ý bên dưới.
Motti Shneor

Câu trả lời:


192

Câu trả lời của @ Darryl là đúng, nhưng có thể sử dụng một số chỉnh sửa nhỏ:

NSUInteger randomIndex = arc4random() % theArray.count;

Các sửa đổi:

  • Sử dụng arc4random()over rand()random()đơn giản hơn vì nó không yêu cầu seeding (gọi srand()hoặc srandom()).
  • Các nhà điều hành modulo ( %) làm báo cáo kết quả tổng thể ngắn hơn, đồng thời cũng làm cho nó ngữ nghĩa rõ ràng hơn.

27
Từ trang người đàn ông của arc4random: arc4random_uniform () được khuyên dùng trên các cấu trúc như `ʻarc4random ()% upper_bound '' vì nó tránh được" modulo bias "khi giới hạn trên không phải là lũy thừa của hai.
Max Yankov

@collibhoy không, bởi vì 0 % 4 = 0, 1 % 4 = 1, 2 % 4 = 2, 3 % 4 = 3, 4 % 4 = 0, 5 % 4 = 1... Nếu bạn modulo bởi n , kết quả lớn nhất của bạn sẽ không bao giờ lớn hơn n-1 .
Dave DeLong

1
@DaveDeLong Theo mã nguồn, countlà một tài sản:@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration> @property (readonly) NSUInteger count;
mikeho

17

Đây là giải pháp đơn giản nhất mà tôi có thể đưa ra:

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

Đó là cần thiết để kiểm tra countvì một phi nilnhưng trống rỗng NSArraysẽ trở lại 0cho count, và arc4random_uniform(0)lợi nhuận 0. Vì vậy, nếu không có séc, bạn sẽ đi ra ngoài giới hạn trên mảng.

Giải pháp này rất hấp dẫn nhưng sai vì nó sẽ gây ra sự cố với một mảng trống:

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

Để tham khảo, đây là tài liệu :

u_int32_t
arc4random_uniform(u_int32_t upper_bound);

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

Trang người đàn ông không đề cập rằng arc4random_uniformtrả về 0khi 0được thông qua như upper_bound.

Ngoài ra, arc4random_uniformđược định nghĩa trong <stdlib.h>, nhưng việc thêm #importkhông cần thiết trong chương trình thử nghiệm iOS của tôi.


11

Có lẽ điều gì đó dọc theo dòng:

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

Đừng quên khởi tạo trình tạo số ngẫu nhiên (ví dụ: srandomdev ()).

LƯU Ý: Tôi đã cập nhật để sử dụng -count thay vì cú pháp dấu chấm, theo câu trả lời bên dưới.


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

Chỉnh sửa: Đã cập nhật cho Xcode 7. Generics, nullability


1

Tạo một số ngẫu nhiên và sử dụng nó làm chỉ mục. Thí dụ:

#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 nếu bạn muốn biết thêm một chút chi tiết, bạn có thể sử dụng SecRandomCopyBytes()để nhận các số ngẫu nhiên hữu ích về mặt mật mã, trên iPhone. Trên Mac, bạn có quyền truy cập trực tiếp vào / dev / random.

Tôi nghĩ điểm chính của câu hỏi là chỉ ra cách chọn một mục ngẫu nhiên từ mảng và câu trả lời này không thực sự cung cấp thông tin tốt nhất.
beakr

Tôi khá thích trò đùa này, nhưng tôi đã bỏ phiếu để hỗ trợ những người không hiểu.
Stig Brautaset

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

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

inx là số ngẫu nhiên.

trong đó srand () có thể ở bất kỳ đâu trong chương trình trước hàm chọn ngẫu nhiên.


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

nếu bạn muốn chuyển nó thành một int, đây là giải pháp cho điều đó (hữu ích khi bạn cần một int ngẫu nhiên từ một mảng các số không tuần tự, trong trường hợp ngẫu nhiên một lệnh gọi enum, v.v.)

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

0

Trong Swift 4:

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

array[Int(randomNumber)]

1
Vui lòng xem lại Cách viết câu trả lời hay . Câu trả lời chỉ có mã không được khuyến khích vì chúng không giải thích cách chúng giải quyết vấn đề trong câu hỏi. Bạn nên cập nhật câu trả lời của bạn để giải thích điều này không và làm thế nào nó cải tiến dựa trên nhiều câu trả lời câu hỏi 7 tuổi này đã có
FluffyKitten
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.