Menghasilkan angka acak di Objective-C

741

Saya terutama kepala Java, dan saya ingin cara untuk menghasilkan nomor pseudo-acak antara 0 dan 74. Di Jawa saya akan menggunakan metode ini:

Random.nextInt(74)

Saya tidak tertarik dalam diskusi tentang benih atau keacakan benar, hanya bagaimana Anda menyelesaikan tugas yang sama di Objective-C. Saya telah menjelajahi Google, dan sepertinya ada banyak bit informasi yang berbeda dan saling bertentangan.

rustyshelf
sumber

Jawaban:

1025

Anda harus menggunakan arc4random_uniform()fungsi ini. Ini menggunakan algoritma superior untuk rand. Anda bahkan tidak perlu membuat benih.

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

The arc4randomman page:

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))
lajos
sumber
96
Gunakan arc4random_uniform(x)seperti dijelaskan di bawah oleh @yood. Ini juga di stdlib.h (setelah OS X 10.7 dan iOS 4.3) dan memberikan distribusi angka acak yang lebih seragam. Penggunaanint r = arc4random_uniform(74);
LavaSlider
4
NB: distribusi dari arc4random bisa sangat buruk, jika Anda memilih kisaran yang buruk. Saya belum menyadari harapan kekuatan-dari-dua. +1 untuk versi @ yood digunakan - membuat perbedaan nyata untuk angka yang lebih besar (mis. Kisaran 400)
Adam
Apakah hanya menghasilkan angka 32 bit?
jjxtra
4
@codecowboy Tidak. Itu selalu mengembalikan integer dalam kisaran [0, (2 ^ 32) -1]. Modulo-lah yang membatasi batas atas rentang ke angka yang Anda tentukan.
Motasim
1
Bukankah ini akan memberikan angka acak antara 0 dan 73?
Tom Howard
424

Gunakan arc4random_uniform(upper_bound)fungsi untuk menghasilkan angka acak dalam rentang. Berikut ini akan menghasilkan angka antara 0 dan 73 inklusif.

arc4random_uniform(74)

arc4random_uniform(upper_bound)menghindari modulo bias seperti yang dijelaskan dalam halaman manual:

arc4random_uniform () akan mengembalikan nomor acak yang terdistribusi secara merata kurang dari upper_bound. arc4random_uniform () direkomendasikan daripada konstruksi seperti `` arc4random ()% upper_bound '' karena ia menghindari " bias modulo " ketika batas atas bukan kekuatan dua.

ya
sumber
32
Perhatikan bahwa arc4random_uniform () membutuhkan iOS 4.3. Jika Anda mendukung perangkat yang lebih lama, Anda harus menambahkan cek: #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_3 Jika gagal, periksa kembali ke solusi lain.
Ron
6
arc4random_uniform () membutuhkan 10.7 atau lebih baru juga. Aplikasi Anda mogok dalam 10,6
Tibidabo
@Tibidabo Komentar Anda sangat salah dan menyesatkan. Saya hanya lelah menggunakan arc4random_uniform () di iOS 10.3 dan tidak ada masalah. Tidak memerlukan 10,7 atau lebih baru
Keempat
1
@Fourth Tidak ada yang namanya iOS 10.7, itu macOS 10.7. Sudah lebih dari 5 tahun sejak saya menulis komentar, itu maks iOS 5 saat itu.
Tibidabo
63

Sama seperti C, Anda akan melakukannya

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

(dengan asumsi yang Anda maksudkan termasuk 0 tetapi tidak termasuk 74, yang adalah apa yang dilakukan contoh Java Anda)

Sunting: Jangan ragu untuk mengganti random()atau arc4random()untuk rand()(yang, seperti orang lain tunjukkan, cukup sial).

James Ko
sumber
43
-1. Anda perlu menyemai generator angka acak atau Anda akan mendapatkan pola angka yang sama pada setiap eksekusi.
Alex Reynolds
Bagaimana kalau saya ingin mulai dari angka yang berbeda dari nol?
mengamuk
2
@amok: Anda bisa menambahkan nomor yang ingin Anda mulai dari hasil
Florin
2
Saya terus mendapatkan nomor 9. Cukup acak, saya katakan; D
alexyorke
saya baru saja menguji secara acak (), dan itu menunjukkan masalah yang sama seperti rand ()
LolaRun
50

Saya pikir saya bisa menambahkan metode yang saya gunakan di banyak proyek.

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

Jika saya akhirnya menggunakannya dalam banyak file saya biasanya mendeklarasikan makro sebagai

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

Misalnya

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

Catatan: Hanya untuk iOS 4.3 / OS X v10.7 (Lion) dan yang lebih baru

Groot
sumber
Tambahan bersifat komutatif, bahkan saat diperbaiki dengan bilangan bulat biner menggunakan dua-pelengkap. Dengan demikian, max - min + 1 persis sama dengan max + 1 - min dan juga 1 + max - min.
Michael Morris
44

Ini akan memberi Anda angka floating point antara 0 dan 47

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

Atau sederhananya

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

Baik batas bawah dan atas bisa negatif juga. Contoh kode di bawah ini memberi Anda angka acak antara -35,76 dan +12,09

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

Konversi hasil menjadi nilai Integer bulat :

int intRndValue = (int)(rndValue + 0.5);
Tibidabo
sumber
Ini buruk. Mengapa Anda menggunakan float, dan bukan double? Dan jika nilai floating point Anda dari 0 hingga 47, maka (int) (rndValue + 0.5) hanya akan mengonversi nilai dari 0,0 hingga 0,5 menjadi 0, tetapi nilai dari 0,5 hingga 1,5 akan dikonversi menjadi 1 dll. Jadi angka 0 dan 47 akan muncul hanya setengah sesering angka-angka lainnya.
gnasher729
@ gnasher729 Maaf saya tidak mengerti maksud Anda. Tentunya jika Anda membutuhkan presisi ganda, Anda dapat dengan mudah mengganti "float" dengan "double"
Tibidabo
Saya pikir itu bukan masalah besar. Jika Anda membutuhkan nilai integer saja daripada Anda dapat menggunakan arc4random () / 0x100000000) * (high_bound-low_bound) + low_bound dengan menghapus float / konversi ganda.
Tibidabo
Anda mendapatkan bias saat Anda mengkonversi integer ke floating point dengan cara ini. Gunakan drand48sebagai gantinya untuk floating point.
Franklin Yu
37

Menurut halaman buku panduan untuk rand (3), fungsi keluarga rand telah usang secara acak (3). Ini disebabkan oleh fakta bahwa 12 bit rand () yang lebih rendah melewati pola siklik. Untuk mendapatkan nomor acak, cukup seed generator dengan memanggil srandom () dengan seed yang tidak ditandatangani, lalu panggil acak (). Jadi, yang setara dengan kode di atas adalah

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

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

Anda hanya perlu memanggil srandom () sekali dalam program Anda kecuali Anda ingin mengubah seed Anda. Meskipun Anda mengatakan Anda tidak ingin diskusi tentang nilai acak, rand () adalah penghasil angka acak yang cukup buruk, dan random () masih mengalami bias modulo, karena akan menghasilkan angka antara 0 dan RAND_MAX. Jadi, mis. Jika RAND_MAX adalah 3, dan Anda menginginkan angka acak antara 0 dan 2, Anda dua kali lebih mungkin mendapatkan 0 daripada 1 atau 2.

Michael Buckley
sumber
7
Anda mungkin juga memanggil srandomdev () alih-alih melewatkan waktu ke srandom (); sama mudahnya dan secara matematis lebih baik.
benzado
31

Lebih baik digunakan arc4random_uniform . Namun, ini tidak tersedia di bawah iOS 4.3. Untungnya iOS akan mengikat simbol ini saat runtime, bukan pada waktu kompilasi (jadi jangan gunakan arahan preprocessor #jika untuk memeriksa apakah tersedia).

Cara terbaik untuk menentukan apakah arc4random_uniformtersedia adalah melakukan sesuatu seperti ini:

#include <stdlib.h>

int r = 0;
if (arc4random_uniform != NULL)
    r = arc4random_uniform (74);
else
    r = (arc4random() % 74);
AW101
sumber
9
Pertanyaan ini adalah tentang Objective-C yang menggunakan pengikatan akhir. Tidak seperti C yang mengikat pada waktu kompilasi / tautan, Objective-C mengikat simbol saat runtime, dan simbol yang tidak dapat diikat diatur ke NULL. Meskipun Anda benar bahwa ini bukan C yang valid, yang paling pasti adalah Objective-C yang valid. Saya menggunakan kode persis ini di aplikasi iPhone saya. [ps tolong bisakah Anda memperbaiki downvote Anda].
AW101
Sementara objektif-c menggunakan metode late binding untuk metode objc, untuk fungsi C hal ini tidak terjadi. Kode ini pasti akan macet jika fungsi tidak ada pada saat run-time.
Richard J. Ross III
8
Menurut Apple "... linker menetapkan alamat fungsi yang tidak tersedia ke NULL ...", Lihat daftar 3.2: developer.apple.com/library/mac/#documentation/DeveloperTools/… . Ok jadi itu harus ditautkan dengan lemah, tetapi tidak macet.
AW101
1
Memeriksa bahwa alamat suatu fungsi adalah NULL adalah metode yang telah digunakan di semua versi C, C ++ dan Objective-C baik pada MacOS X dan iOS.
gnasher729
14

Saya menulis kelas utilitas angka acak saya sendiri sehingga saya akan memiliki sesuatu yang berfungsi sedikit lebih seperti Math.random () di Jawa. Ini hanya memiliki dua fungsi, dan semuanya dibuat dalam C.

File tajuk:

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

File implementasi:

//Random.m
static unsigned long seed;

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

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

Ini adalah cara yang cukup klasik untuk menghasilkan pseudo-randoms. Dalam delegasi aplikasi saya, saya menelepon:

#import "Random.h"

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

Kemudian saya katakan:

float myRandomNumber = nextRandomFloat() * 74;

Perhatikan bahwa metode ini mengembalikan angka acak antara 0,0f (inklusif) dan 1.0f (eksklusif).

Eli
sumber
3
1. Fungsi bilangan acak yang dibuat secara acak biasanya tidak terlalu acak. 2. Ini benar-benar rusak pada prosesor 64 bit. 3. Menggunakan detik sejak 1970 sebagai seed acak membuat angka-angka dapat diprediksi.
gnasher729
7

Sudah ada beberapa jawaban yang jelas dan jelas, tetapi pertanyaannya menanyakan angka acak antara 0 dan 74. Gunakan:

arc4random_uniform(75)

Tom Howard
sumber
4

Pada iOS 9 dan OS X 10.11, Anda dapat menggunakan kelas GameplayKit baru untuk menghasilkan angka acak dalam berbagai cara.

Anda memiliki empat jenis sumber untuk dipilih: sumber acak umum (tanpa nama, ke sistem untuk memilih apa yang dilakukannya), linear congruential, ARC4 dan Mersenne Twister. Ini dapat menghasilkan int acak, float, dan bools.

Pada tingkat paling sederhana, Anda dapat menghasilkan angka acak dari sumber acak bawaan sistem seperti ini:

NSInteger rand = [[GKRandomSource sharedRandom] nextInt];

Itu menghasilkan angka antara -2.147.483.648 dan 2.147.483.647. Jika Anda ingin angka antara 0 dan batas atas (eksklusif) Anda akan menggunakan ini:

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

GameplayKit memiliki beberapa konstruktor yang mudah digunakan untuk bekerja dengan dadu. Misalnya, Anda dapat menggulung dadu enam sisi seperti ini:

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

Plus Anda dapat membentuk distribusi acak dengan menggunakan hal-hal seperti GKShuffledDistribution.

TwoStraws
sumber
Mersenne adalah yang tercepat dan terbaik untuk hal-hal seperti game dev di mana kualitas angka acak yang dihasilkan biasanya tidak terlalu penting.
Robert Wasmann
3

Hasilkan angka acak antara 0 hingga 99:

int x = arc4random()%100;

Hasilkan angka acak antara 500 dan 1000:

int x = (arc4random()%501) + 500;
adijazz91
sumber
1

// Contoh berikut akan menghasilkan angka antara 0 dan 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);

Keluaran:

  • nomor acak: 72

  • angka acak langkah 2: 52

soumya
sumber
1

Untuk game dev, gunakan random () untuk menghasilkan random. Mungkin setidaknya 5x lebih cepat daripada menggunakan arc4random (). Bias modulo tidak menjadi masalah, terutama untuk game, saat menghasilkan random menggunakan rentang acak penuh (). Pastikan untuk menyemai terlebih dahulu. Panggil srandomdev () di AppDelegate. Berikut beberapa fungsi pembantu:

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;}
Robert Wasmann
sumber
Sadarilah bahwa random () tidak terlalu acak, jadi jika kecepatan tidak penting dalam kode Anda (seperti jika hanya digunakan sesekali) maka gunakan arc4random ().
Robert Wasmann