Objective-C'de rasgele sayılar üretme


741

Ben esas olarak bir Java başıyım ve 0 ile 74 arasında bir rasgele rasgele sayı üretmenin bir yolunu istiyorum. Java'da yöntemi kullanırdım:

Random.nextInt(74)

Tohumlar veya gerçek rastgelelik hakkında bir tartışma ile ilgilenmiyorum, sadece Objective-C'de aynı görevi nasıl yerine getirdiğiniz. Google'ı araştırdım ve çok sayıda farklı ve çelişkili bilgi parçası var gibi görünüyor.

Yanıtlar:


1025

arc4random_uniform()İşlevi kullanmalısınız . Üstün bir algoritma kullanır rand. Bir tohum ayarlamanıza bile gerek yok.

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

arc4randomAdam sayfa:

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
arc4random_uniform(x)@Yood tarafından aşağıda açıklandığı şekilde kullanın . Ayrıca stdlib.h dosyasında (OS X 10.7 ve iOS 4.3'ten sonra) ve rasgele sayıların daha düzgün bir dağılımını sağlıyor. Kullanımint r = arc4random_uniform(74);
LavaSlider

4
Not: zayıf bir aralık seçerseniz, arc4random'dan dağılım çok zayıf olabilir. İki güç beklentisini fark etmemiştim. @ Yood'un sürümü için + 1 - daha büyük sayılar için fark edilir bir fark yarattı (örneğin 400 aralığı)
Adam

Sadece 32 bit sayı üretiyor mu?
jjxtra

4
@codecowboy Değil. Her zaman [0, (2 ^ 32) -1] aralığında bir tamsayı döndürür. Aralığın üst sınırını belirttiğiniz sayı ile sınırlayan modülodur.
Motasim

1
Bu 0 ile 73 arasında rastgele bir sayı vermez mi?
Tom Howard

424

arc4random_uniform(upper_bound)Bir aralık içinde rastgele bir sayı oluşturmak için bu işlevi kullanın . Aşağıdaki 0 ​​ile 73 arasında bir sayı üretecektir.

arc4random_uniform(74)

arc4random_uniform(upper_bound)man sayfasında anlatıldığı gibi modulo yanlılığını önler :

arc4random_uniform () işlevi, upper_bound değerinden daha düşük, eşit olarak dağıtılmış rasgele bir sayı döndürür. arc4random_uniform (), `` arc4random ()% upper_bound '' gibi yapılarda tavsiye edilir , çünkü üst sınır iki güç olmadığında " modulo yanlılığını " önler .


32
Arc4random_uniform () için iOS 4.3 gerekir. Daha eski cihazları destekliyorsanız, bir kontrol eklemelisiniz: Kontrol #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_3 başarısız olursa başka bir çözüme geri dönün.
Ron

6
arc4random_uniform () için de 10.7 veya üstü gerekir. 10.6
Tibidabo

@Tibidabo Yorumunuz çok yanlış ve yanıltıcı. İOS 10.3'te arc4random_uniform () kullanarak yoruldum ve hiçbir sorun yok. 10.7 veya üstünü gerektirmez
Dördüncü

1
@Fourth iOS 10.7 diye bir şey yok, macOS 10.7. O zamanlar maksimum iOS 5 olduğu yorumunu yazdığımdan beri 5 yıldan fazla oldu.
Tibidabo

63

C ile aynı, yapardın

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

(Java örneğinizin yaptığı şey 0'dır, ancak 74'ü hariç tuttuğunuzu varsayarsak)

Düzenleme: hissedin yerine ücretsiz random()ya arc4random()için rand()(diğerleri belirttiğimiz gibi, olduğu, oldukça Siktiri).


43
-1. Rastgele sayı üretecini tohumlamanız gerekir, yoksa her yürütmede aynı sayı desenini alırsınız.
Alex Reynolds

Sıfırdan farklı bir sayıdan başlamak istiyorum.
amok

2
@amok: Başlamak istediğiniz sayıyı sonuca ekleyebilirsiniz
Florin

2
9 numarayı almaya devam ediyorum. Rastgele söyleyebilirim; D
alexyorke

ben sadece rastgele () test ve rand () ile aynı sorunu gösterdi
LolaRun

50

Birçok projede kullandığım bir yöntemi ekleyebileceğimi düşündüm.

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

Sonunda birçok dosyada kullanırsam, genellikle bir makroyu

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

Örneğin

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

Not: Yalnızca iOS 4.3 / OS Xv10.7 (Lion) ve sonraki sürümler için


İkili tamamlayıcı kullanılarak ikili tamsayılarla sabitlendiğinde bile ekleme değişebilir. Bu nedenle, max - min + 1, max + 1 - min ve 1 + max - min ile tamamen aynıdır.
Michael Morris

44

Bu, 0 ile 47 arasında bir kayan nokta sayısı verecektir

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

Veya sadece basitçe

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

Hem alt hem de üst sınır da negatif olabilir . Aşağıdaki örnek kod size -35,76 ile +12,09 arasında rastgele bir sayı verir

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

Sonucu daha yuvarlak bir Tamsayı değerine dönüştürün :

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

Bu kötü. Neden çift değil float kullanıyorsunuz? Kayan nokta değerleriniz 0'dan 47'ye ise, (int) (rndValue + 0.5) yalnızca 0.0'dan 0.5'e kadar olan değerleri dönüştürür, ancak 0.5'den 1.5'e kadar olan değerler 1'e dönüştürülür. 0 ve 47 diğer tüm sayıların sadece yarısı kadar gelecektir.
gnasher729

@ gnasher729 Üzgünüm ne demek istediğini anlamıyorum. Açıkçası çift hassasiyet gerekiyorsa kolayca "şamandıra" yerine "çift" değiştirebilirsiniz
Tibidabo

Bunun büyük bir anlaşma olduğunu düşünmüyorum. Yalnızca tamsayı değerlerine ihtiyacınız varsa, float / double dönüşümünü kaldırarak arc4random () / 0x100000000) * (high_bound-low_bound) + low_bound kullanabilirsiniz.
Tibidabo

Tamsayıyı kayan noktaya bu şekilde dönüştürdüğünüzde önyargı elde edersiniz. drand48Bunun yerine kayan noktalar için kullanın .
Franklin Yu

37

Rand (3) kılavuz sayfasına göre, rand fonksiyon ailesi rastgele (3) kullanılmamıştır. Bunun nedeni, rand () 'nin alt 12 bitinin döngüsel bir kalıptan geçmesidir. Rasgele bir sayı elde etmek için, imzasız bir tohumla srandom () öğesini çağırarak jeneratörü tohumlayın ve ardından random () öğesini çağırın. Yani, yukarıdaki kodun eşdeğeri

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

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

Tohumunuzu değiştirmek istemiyorsanız, programınızda yalnızca bir kez srandom () öğesini çağırmanız gerekir. Gerçekten rastgele değerler hakkında bir tartışma istemediğinizi söylemenize rağmen, rand () oldukça kötü bir rastgele sayı üretecidir ve random () hala 0 ve RAND_MAX arasında bir sayı üreteceğinden modulo önyargılarından muzdariptir. Örneğin, RAND_MAX 3 ise ve 0 ile 2 arasında rastgele bir sayı istiyorsanız, 1 veya 2'den 0 elde etme olasılığınızın iki katıdır.


7
Zamanı srandom () yerine geçmek yerine srandomdev () 'yi de çağırabilirsiniz. bu kadar kolay ve matematiksel olarak daha iyi.
benzado

31

Kullanmak daha iyi arc4random_uniform. Ancak, iOS 4.3'ün altında mevcut değildir. Neyse ki iOS bu sembolü derleme zamanında değil, çalışma zamanında bağlayacaktır (bu nedenle, kullanılabilir olup olmadığını kontrol etmek için #if önişlemci direktifini kullanmayın).

Kullanılabilir olup olmadığını belirlemenin en iyi yolu arc4random_uniformböyle bir şey yapmaktır:

#include <stdlib.h>

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

9
Bu soru geç bağlama kullanan Objective-C ile ilgilidir. Derleme / bağlantı zamanında bağlanan C'den farklı olarak, Objective-C çalışma zamanında sembolleri bağlar ve bağlayamadığı semboller NULL olarak ayarlanır. Bunun geçerli C olmadığı konusunda haklı olmanıza rağmen, kesinlikle geçerli Objective-C. Bu kodu iPhone uygulamamda kullanıyorum. [ps lütfen oyunuzu düzeltebilir misiniz].
AW101

Obj-c objc yöntemleri için geç bağlamayı kullanırken C fonksiyonu için durum böyle değildir. İşlev çalışma zamanında mevcut değilse bu kod kesinlikle çökecektir.
Richard

8
Apple'a göre "... bağlayıcı kullanılamayan işlevlerin adresini NULL olarak ayarlıyor ...", Bkz. Liste 3.2: developer.apple.com/library/mac/#documentation/DeveloperTools/… . Tamam, zayıf bir şekilde bağlı olması gerekiyor, ancak çökmüyor.
AW101

1
Bir işlevin adresinin NULL olup olmadığını denetlemek, hem MacOS X hem de iOS'ta C, C ++ ve Objective-C'nin tüm sürümlerinde kullanılan bir yöntemdir.
gnasher729

14

Java'da Math.random () gibi biraz daha fazla çalışacak bir şey elde etmek için kendi rasgele sayı yardımcı program sınıfımı yazdım. Sadece iki işlevi vardır ve hepsi C ile yapılır.

Başlık dosyası:

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

Uygulama dosyası:

//Random.m
static unsigned long seed;

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

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

Sözde rasgele üretmenin oldukça klasik bir yolu. Uygulama temsilcimde şunu ararım:

#import "Random.h"

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

Sonra daha sonra diyorum ki:

float myRandomNumber = nextRandomFloat() * 74;

Bu yöntemin 0.0f (dahil) ve 1.0f (özel) arasında rastgele bir sayı döndürdüğünü unutmayın.


3
1. Rasgele oluşturulan rasgele sayı fonksiyonları genellikle çok rasgele değildir. 2. Tamamen 64 bit işlemcide bozuk. 3. Rastgele tohum olarak 1970'den bu yana saniyeler kullanmak sayıları öngörülebilir kılar.
gnasher729

7

Zaten bazı harika, eklemli cevaplar var, ancak soru 0 ile 74 arasında rastgele bir sayı istiyor. Kullanın:

arc4random_uniform(75)


4

İOS 9 ve OS X 10.11'den itibaren yeni GameplayKit sınıflarını çeşitli şekillerde rastgele sayılar üretmek için kullanabilirsiniz.

Seçebileceğiniz dört kaynak türünüz vardır: genel bir rastgele kaynak (isimsiz, ne yapacağını seçmek için sisteme kadar), doğrusal uyumlu, ARC4 ve Mersenne Twister. Bunlar rastgele ints, float ve bools üretebilir.

En basit seviyede, sistemin yerleşik rastgele kaynağından rastgele bir sayı üretebilirsiniz:

NSInteger rand = [[GKRandomSource sharedRandom] nextInt];

Bu -2.147.483.648 ile 2.147.483.647 arasında bir sayı üretir. 0 ile üst sınır (özel) arasında bir sayı istiyorsanız bunu kullanırsınız:

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

GameplayKit, zarlarla çalışmak için yerleşik bazı kullanışlı kuruculara sahiptir. Örneğin, altı taraflı bir kalıbı şu şekilde yuvarlayabilirsiniz:

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

Artı gibi şeyleri kullanarak rastgele dağılımı şekillendirebilirsiniz GKShuffledDistribution.


Mersenne, üretilen rastgele sayının kalitesinin genellikle çok önemli olmadığı oyun devi gibi şeyler için en hızlı ve en iyisidir.
Robert Wasmann

3

0 ile 99 arasında rastgele bir sayı üret:

int x = arc4random()%100;

500 ve 1000 arasında rastgele sayı üret:

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

1

// Aşağıdaki örnek 0 ile 73 arasında bir sayı üretecektir.

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);

Çıktı:

  • rasgele sayı: 72

  • rastgele sayı adımı 2: 52


1

Oyun dev için rastgele oluşturmak için random () kullanın. Muhtemelen arc4random () kullanmaktan en az 5 kat daha hızlıdır. Modulo önyargısı, özellikle rasgele () kullanarak rasgele oluştururken oyunlar için bir sorun değildir. İlk önce tohumladığınızdan emin olun. AppDelegate'te srandomdev () öğesini çağırın. İşte bazı yardımcı fonksiyonlar:

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;}

Random () 'ın o kadar rasgele olmadığını unutmayın, bu nedenle kodunuzda hız önemli değilse (sadece arada bir kullanılırsa) arc4random () kullanın.
Robert Wasmann
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.