Tạo số ngẫu nhiên trong Objective-C


741

Tôi chủ yếu là người đứng đầu Java và tôi muốn có cách tạo số giả ngẫu nhiên trong khoảng từ 0 đến 74. Trong Java tôi sẽ sử dụng phương thức:

Random.nextInt(74)

Tôi không quan tâm đến một cuộc thảo luận về hạt giống hoặc sự ngẫu nhiên thực sự, chỉ là cách bạn hoàn thành nhiệm vụ tương tự trong Mục tiêu-C. Tôi đã truy quét Google và dường như có rất nhiều thông tin khác nhau và mâu thuẫn.

Câu trả lời:


1025

Bạn nên sử dụng arc4random_uniform()chức năng. Nó sử dụng một thuật toán ưu việt để rand. Bạn thậm chí không cần phải đặt hạt giống.

#include <stdlib.h>
// ...
// ...
int r = arc4random_uniform(74);

Các arc4randomtrang người đàn ông:

NAME
     arc4random, arc4random_stir, arc4random_addrandom -- arc4 random number generator

LIBRARY
     Standard C Library (libc, -lc)

SYNOPSIS
     #include <stdlib.h>

     u_int32_t
     arc4random(void);

     void
     arc4random_stir(void);

     void
     arc4random_addrandom(unsigned char *dat, int datlen);

DESCRIPTION
     The arc4random() function uses the key stream generator employed by the arc4 cipher, which uses 8*8 8
     bit S-Boxes.  The S-Boxes can be in about (2**1700) states.  The arc4random() function returns pseudo-
     random numbers in the range of 0 to (2**32)-1, and therefore has twice the range of rand(3) and
     random(3).

     The arc4random_stir() function reads data from /dev/urandom and uses it to permute the S-Boxes via
     arc4random_addrandom().

     There is no need to call arc4random_stir() before using arc4random(), since arc4random() automatically
     initializes itself.

EXAMPLES
     The following produces a drop-in replacement for the traditional rand() and random() functions using
     arc4random():

           #define foo4random() (arc4random() % ((unsigned)RAND_MAX + 1))

96
Sử dụng arc4random_uniform(x)như được mô tả dưới đây bởi @yood. Nó cũng có trong stdlib.h (sau OS X 10.7 và iOS 4.3) và phân phối đồng đều hơn các số ngẫu nhiên. Cách sử dụngint r = arc4random_uniform(74);
LavaSlider

4
NB: phân phối từ arc4random có ​​thể rất kém, nếu bạn tình cờ chọn một phạm vi kém. Tôi đã không nhận ra sự kỳ vọng của hai người. +1 để sử dụng phiên bản @ yood - đã tạo ra sự khác biệt đáng chú ý cho số lượng lớn hơn (ví dụ: phạm vi 400)
Adam

Nó chỉ tạo ra số 32 bit?
jjxtra

4
@codecowboy Nó không. Nó luôn trả về một số nguyên trong phạm vi [0, (2 ^ 32) -1]. Đó là modulo giới hạn giới hạn trên của phạm vi đối với số bạn chỉ định.
Motasim

1
Điều này sẽ không cung cấp một số ngẫu nhiên từ 0 đến 73?
Tom Howard

424

Sử dụng arc4random_uniform(upper_bound)hàm để tạo một số ngẫu nhiên trong phạm vi. Sau đây sẽ tạo ra một số từ 0 đến 73 bao gồm.

arc4random_uniform(74)

arc4random_uniform(upper_bound)tránh sai lệch modulo như được mô tả trong trang man:

arc4random_uniform () sẽ trả về một số ngẫu nhiên được phân phối đồng đều ít hơn so với trên. arc4random_uniform () được khuyến nghị đối với các cấu trúc như `` arc4random ()% Upper_bound '' vì nó tránh được " độ lệch modulo " khi giới hạn trên không phải là lũy thừa của hai.


32
Lưu ý rằng arc4random_uniform () yêu cầu iOS 4.3. Trong trường hợp bạn đang hỗ trợ các thiết bị cũ hơn, bạn nên thêm một kiểm tra: #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_3 Nếu kiểm tra không thành công, hãy quay lại giải pháp khác.
Ron

6
arc4random_uniform () cũng yêu cầu 10.7 trở lên. Nó bị sập ứng dụng của bạn trong 10.6
Tibidabo

@Tibidabo Nhận xét của bạn rất sai lệch và sai lệch. Tôi chỉ mệt mỏi khi sử dụng arc4random_uniform () trên iOS 10.3 và không có vấn đề gì. Nó không yêu cầu 10.7 trở lên
Thứ tư

1
@Fourth Không có thứ gì như iOS 10.7, đó là macOS 10.7. Đã hơn 5 năm kể từ khi tôi viết bình luận, đó là tối đa iOS 5 trở lại.
Tibidabo

63

Giống như C, bạn sẽ làm

#include <time.h>
#include <stdlib.h>
...
srand(time(NULL));
int r = rand() % 74;

(giả sử bạn có nghĩa là bao gồm 0 nhưng không bao gồm 74, đó là những gì ví dụ Java của bạn làm)

Chỉnh sửa: Hãy thoải mái thay thế random()hoặc arc4random()cho rand()(đó là, như những người khác đã chỉ ra, khá sucky).


43
-1. Bạn cần chọn trình tạo số ngẫu nhiên hoặc bạn sẽ nhận được cùng một mẫu số trên mỗi lần thực hiện.
Alex Reynold

Làm thế nào về tôi muốn bắt đầu từ một số khác không?
amok

2
@amok: Bạn chỉ có thể thêm số mà bạn muốn bắt đầu từ kết quả
Florin

2
Tôi tiếp tục nhận được số 9. Khá ngẫu nhiên tôi muốn nói; D
alexyorke

tôi chỉ thử nghiệm ngẫu nhiên () và nó cho thấy vấn đề tương tự như rand ()
LolaRun

50

Tôi nghĩ rằng tôi có thể thêm một phương pháp tôi sử dụng trong nhiều dự án.

- (NSInteger)randomValueBetween:(NSInteger)min and:(NSInteger)max {
    return (NSInteger)(min + arc4random_uniform(max - min + 1));
}

Nếu tôi kết thúc việc sử dụng nó trong nhiều tệp, tôi thường khai báo một macro như

#define RAND_FROM_TO(min, max) (min + arc4random_uniform(max - min + 1))

Ví dụ

NSInteger myInteger = RAND_FROM_TO(0, 74) // 0, 1, 2,..., 73, 74

Lưu ý: Chỉ dành cho iOS 4.3 / OS X v10.7 (Lion) trở lên


Bổ sung là giao hoán, ngay cả trên cố định với số nguyên nhị phân sử dụng bổ sung twos. Như vậy max - min + 1 hoàn toàn giống với max + 1 - min cũng như 1 + max - min.
Michael Morris

44

Điều này sẽ cung cấp cho bạn một số dấu phẩy động từ 0 đến 47

float low_bound = 0;      
float high_bound = 47;
float rndValue = (((float)arc4random()/0x100000000)*(high_bound-low_bound)+low_bound);

Hoặc chỉ đơn giản là

float rndValue = (((float)arc4random()/0x100000000)*47);

Cả giới hạn dưới và trên cũng có thể là tiêu cực . Mã ví dụ dưới đây cung cấp cho bạn một số ngẫu nhiên trong khoảng từ -35,76 đến +12,09

float low_bound = -35.76;      
float high_bound = 12.09;
float rndValue = (((float)arc4random()/0x100000000)*(high_bound-low_bound)+low_bound);

Chuyển đổi kết quả thành giá trị Số nguyên tròn hơn :

int intRndValue = (int)(rndValue + 0.5);

Thật tệ. Tại sao bạn sử dụng float, và không tăng gấp đôi? Và nếu các giá trị dấu phẩy động của bạn là từ 0 đến 47, thì (int) (rndValue + 0.5) sẽ chỉ chuyển đổi các giá trị từ 0,0 thành 0,5 thành 0, nhưng các giá trị từ 0,5 đến 1,5 sẽ được chuyển đổi thành 1, do đó, các số 0 và 47 sẽ chỉ tăng một nửa thường xuyên như tất cả các số khác.
gnasher729

@ gnasher729 Xin lỗi tôi không thấy quan điểm của bạn. Rõ ràng nếu bạn cần độ chính xác gấp đôi, bạn có thể dễ dàng thay thế "float" bằng "double"
Tibidabo

Tôi không nghĩ đó là một vấn đề lớn. Nếu bạn chỉ cần các giá trị số nguyên hơn bạn có thể sử dụng arc4random () / 0x100000000) * (high_bound-low_bound) + low_bound bằng cách xóa chuyển đổi float / double.
Tibidabo

Bạn nhận được sự thiên vị khi bạn chuyển đổi số nguyên sang dấu phẩy động theo cách này. Sử dụng drand48thay thế cho các điểm nổi.
Franklin Yu

37

Theo trang hướng dẫn cho rand (3), họ rand của các hàm đã bị lỗi thời (3). Điều này là do thực tế là 12 bit rand () thấp hơn đi qua một mô hình tuần hoàn. Để có được một số ngẫu nhiên, chỉ cần khởi tạo trình tạo bằng cách gọi srandom () với một hạt không dấu, sau đó gọi ngẫu nhiên (). Vì vậy, tương đương với mã ở trên sẽ là

#import <stdlib.h>
#import <time.h>

srandom(time(NULL));
random() % 74;

Bạn sẽ chỉ cần gọi srandom () một lần trong chương trình của mình trừ khi bạn muốn thay đổi hạt giống của mình. Mặc dù bạn nói rằng bạn không muốn thảo luận về các giá trị thực sự ngẫu nhiên, rand () là một trình tạo số ngẫu nhiên khá tệ và ngẫu nhiên () vẫn bị sai lệch modulo, vì nó sẽ tạo ra một số trong khoảng từ 0 đến RAND_MAX. Vì vậy, ví dụ: nếu RAND_MAX là 3 và bạn muốn có một số ngẫu nhiên trong khoảng từ 0 đến 2, bạn có khả năng nhận được 0 nhiều hơn 1 hoặc 2.


7
Bạn cũng có thể gọi srandomdev () thay vì chuyển thời gian sang srandom (); nó chỉ dễ dàng và tốt hơn về mặt toán học.
benzado

31

Tốt hơn để sử dụng arc4random_uniform. Tuy nhiên, điều này không có sẵn dưới iOS 4.3. May mắn thay, iOS sẽ liên kết biểu tượng này trong thời gian chạy, không phải lúc biên dịch (vì vậy đừng sử dụng chỉ thị tiền xử lý #if để kiểm tra xem nó có khả dụng không).

Cách tốt nhất để xác định nếu arc4random_uniformcó sẵn là làm một cái gì đó như thế này:

#include <stdlib.h>

int r = 0;
if (arc4random_uniform != NULL)
    r = arc4random_uniform (74);
else
    r = (arc4random() % 74);

9
Câu hỏi này là về Objective-C sử dụng liên kết muộn. Không giống như C liên kết tại thời gian biên dịch / liên kết, Objective-C liên kết các ký hiệu trong thời gian chạy và các ký hiệu mà nó không thể liên kết được đặt thành NULL. Mặc dù bạn đúng rằng đây không phải là C hợp lệ, nhưng chắc chắn đó là Objective-C hợp lệ. Tôi sử dụng mã chính xác này trong ứng dụng iPhone của tôi. [ps xin vui lòng bạn có thể sửa chữa downvote của bạn].
AW101

Trong khi object-c sử dụng liên kết muộn cho các phương thức objc, đối với hàm C thì đây không phải là trường hợp. Mã này chắc chắn sẽ bị sập nếu chức năng không tồn tại vào thời gian chạy.
Richard J. Ross III

8
Theo Apple "... trình liên kết đặt địa chỉ của các chức năng không khả dụng thành NULL ...", Xem danh sách 3.2: developer.apple.com/l Library / mac / # document / DocumentTools / . Ok vì vậy nó phải được liên kết yếu, nhưng nó không sụp đổ.
AW101

1
Kiểm tra xem địa chỉ của hàm là NULL có phải là phương thức đã được sử dụng trong tất cả các phiên bản C, C ++ và Objective-C cả trên MacOS X và iOS.
gnasher729

14

Tôi đã viết lớp tiện ích số ngẫu nhiên của riêng mình chỉ để tôi có một cái gì đó có chức năng giống như Math.random () trong Java. Nó chỉ có hai chức năng, và tất cả đều được tạo ra trong C.

Tập tin tiêu đề:

//Random.h
void initRandomSeed(long firstSeed);
float nextRandomFloat();

Hồ sơ thực hiện:

//Random.m
static unsigned long seed;

void initRandomSeed(long firstSeed)
{ 
    seed = firstSeed;
}

float nextRandomFloat()
{
    return (((seed= 1664525*seed + 1013904223)>>16) / (float)0x10000);
}

Đó là một cách khá cổ điển để tạo ra các randoms giả. Trong đại biểu ứng dụng của tôi, tôi gọi:

#import "Random.h"

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
    initRandomSeed( (long) [[NSDate date] timeIntervalSince1970] );
    //Do other initialization junk.
}

Sau đó tôi chỉ nói:

float myRandomNumber = nextRandomFloat() * 74;

Lưu ý rằng phương pháp này trả về một số ngẫu nhiên trong khoảng từ 0,0f (đã bao gồm) và 1.0f (loại trừ).


3
1. Hàm số ngẫu nhiên được tạo ngẫu nhiên thường không ngẫu nhiên. 2. Nó bị hỏng hoàn toàn trên bộ xử lý 64 bit. 3. Sử dụng giây kể từ năm 1970 làm hạt giống ngẫu nhiên làm cho các con số có thể dự đoán được.
gnasher729

7

Đã có một số câu trả lời tuyệt vời, rõ ràng, nhưng câu hỏi yêu cầu một số ngẫu nhiên trong khoảng từ 0 đến 74. Sử dụng:

arc4random_uniform(75)


4

Kể từ iOS 9 và OS X 10.11, bạn có thể sử dụng các lớp GameplayKit mới để tạo số ngẫu nhiên theo nhiều cách khác nhau.

Bạn có bốn loại nguồn để chọn: một nguồn ngẫu nhiên chung (chưa được đặt tên, xuống hệ thống để chọn những gì nó làm), đồng quy tuyến tính, ARC4 và Mersenne Twister. Chúng có thể tạo ra ints ngẫu nhiên, float và bools.

Ở cấp độ đơn giản nhất, bạn có thể tạo một số ngẫu nhiên từ nguồn ngẫu nhiên tích hợp sẵn của hệ thống như thế này:

NSInteger rand = [[GKRandomSource sharedRandom] nextInt];

Điều đó tạo ra một số từ -2,147,483,648 đến 2,147,483,647. Nếu bạn muốn một số từ 0 đến giới hạn trên (độc quyền), bạn sẽ sử dụng số này:

NSInteger rand6 = [[GKRandomSource sharedRandom] nextIntWithUpperBound:6];

GameplayKit có một số hàm tạo tiện lợi được tích hợp để hoạt động với xúc xắc. Ví dụ: bạn có thể lăn một cái chết sáu mặt như thế này:

GKRandomDistribution *d6 = [GKRandomDistribution d6];
[d6 nextInt];

Ngoài ra, bạn có thể định hình phân phối ngẫu nhiên bằng cách sử dụng những thứ như GKShuffledDistribution.


Mersenne là nhanh nhất và tốt nhất cho những thứ như nhà phát triển trò chơi trong đó chất lượng của số ngẫu nhiên được tạo ra thường không quá quan trọng.
Robert Wasmann

3

Tạo số ngẫu nhiên trong khoảng từ 0 đến 99:

int x = arc4random()%100;

Tạo số ngẫu nhiên trong khoảng từ 500 đến 1000:

int x = (arc4random()%501) + 500;

1

// Ví dụ sau sẽ tạo một số từ 0 đến 73.

int value;
value = (arc4random() % 74);
NSLog(@"random number: %i ", value);

//In order to generate 1 to 73, do the following:
int value1;
value1 = (arc4random() % 73) + 1;
NSLog(@"random number step 2: %i ", value1);

Đầu ra:

  • số ngẫu nhiên: 72

  • số ngẫu nhiên bước 2: 52


1

Đối với nhà phát triển trò chơi sử dụng ngẫu nhiên () để tạo randoms. Có thể nhanh hơn ít nhất gấp 5 lần so với sử dụng arc4random (). Sai lệch Modulo không phải là một vấn đề, đặc biệt đối với các trò chơi, khi tạo các randoms bằng cách sử dụng toàn bộ phạm vi ngẫu nhiên (). Hãy chắc chắn để hạt giống đầu tiên. Gọi srandomdev () trong AppDelegate. Đây là một số chức năng của trình trợ giúp:

static inline int random_range(int low, int high){ return (random()%(high-low+1))+low;}
static inline CGFloat frandom(){ return (CGFloat)random()/UINT32_C(0x7FFFFFFF);}
static inline CGFloat frandom_range(CGFloat low, CGFloat high){ return (high-low)*frandom()+low;}

Tuy nhiên, hãy lưu ý rằng ngẫu nhiên () không quá ngẫu nhiên, vì vậy nếu tốc độ không quan trọng trong mã của bạn (như nếu chỉ được sử dụng một lần trong một thời gian) thì hãy sử dụng arc4random ().
Robert Wasmann
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.