10

I need to generate 8 random integers, but they need to be unique, aka not repeated.

For example, I want 8 numbers within the range 1 to 8.

I've seen arc4random but I'm not sure how to make them unique ?

Solution

-(NSMutableArray *)getRandomInts:(int)amount from:(int)fromInt to:(int)toInt {

  if ((toInt - fromInt) +1 < amount) {
      return nil;    
  }

  NSMutableArray *uniqueNumbers = [[[NSMutableArray alloc] init] autorelease];
  int r;
  while ([uniqueNumbers count] < amount) {

      r = (arc4random() % toInt) + fromInt;
      if (![uniqueNumbers containsObject:[NSNumber numberWithInt:r]]) {
          [uniqueNumbers addObject:[NSNumber numberWithInt:r]];
      }
  }
  return uniqueNumbers;
}
PengOne
  • 48,188
  • 17
  • 130
  • 149
Jules
  • 7,568
  • 14
  • 102
  • 186

6 Answers6

14
-(NSMutableArray *)getEightRandom {
  NSMutableArray *uniqueNumbers = [[[NSMutableArray alloc] init] autorelease];
  int r;
  while ([uniqueNumbers count] < 8) {
    r = arc4random();
    if (![uniqueNumbers containsObject:[NSNumber numberWithInt:r]]) {
      [uniqueNumbers addObject:[NSNumber numberWithInt:r]];
    }
  }
  return uniqueNumbers;
}

If you want to restrict to numbers less than some threshold M, then you can do this by:

-(NSMutableArray *)getEightRandomLessThan:(int)M {
  NSMutableArray *uniqueNumbers = [[[NSMutableArray alloc] init] autorelease];
  int r;
  while ([uniqueNumbers count] < 8) {
    r = arc4random() % M; // ADD 1 TO GET NUMBERS BETWEEN 1 AND M RATHER THAN 0 and M-1
    if (![uniqueNumbers containsObject:[NSNumber numberWithInt:r]]) {
      [uniqueNumbers addObject:[NSNumber numberWithInt:r]];
    }
  }
  return uniqueNumbers;
}

If M=8, or even if M is close to 8 (e.g. 9 or 10), then this takes a while and you can be more clever.

-(NSMutableArray *)getEightRandomLessThan:(int)M {
  NSMutableArray *listOfNumbers = [[NSMutableArray alloc] init];
  for (int i=0 ; i<M ; ++i) {
    [listOfNumbers addObject:[NSNumber numberWithInt:i]]; // ADD 1 TO GET NUMBERS BETWEEN 1 AND M RATHER THAN 0 and M-1
  }
  NSMutableArray *uniqueNumbers = [[[NSMutableArray alloc] init] autorelease];
  int r;
  while ([uniqueNumbers count] < 8) {
    r = arc4random() % [listOfNumbers count];
    if (![uniqueNumbers containsObject:[listOfNumbers objectAtIndex:r]]) {
      [uniqueNumbers addObject:[listOfNumbers objectAtIndex:r]];
    }
  }
  [listOfNumbers release];
  return uniqueNumbers;
}
PengOne
  • 48,188
  • 17
  • 130
  • 149
  • `#include ` Also, PengOne has a typo: it's `arc4random` not `acr4random` – Art Gillespie May 27 '11 at 14:57
  • 2
    -1. This answer, while it may work with no observable downside for small numbers, teaches a poor solution to this problem, which is really a shuffling problem. This algorithm is *never* guaranteed to complete. Please see @Art Gillespie's answer for an appropriate code snippet. (I'm downvoting here as a signpost to potential future readers since this answer was accepted.) – Ben Zotto May 27 '11 at 19:10
  • @quixoto: With probability 1, it will terminate in finite time. Moreover, the third suggestion gives a permutation generation algorithm that terminates in 8 steps. Moreover, he doesn't necessarily want 8 consecutive numbers. – PengOne May 27 '11 at 20:07
  • @quixoto: your comment is a better and more informative signpost to potential future readers than a down vote for a correct answer, just fyi – PengOne May 27 '11 at 20:11
  • -1 As it stands, your third listing behaves pretty much the same as your second listing, because you're not removing selected numbers from [listOfNumbers] after they've been used. Also, you don't need the "if (![uniqueNumbers containsObject:" check any more if you're using the listOfNumbers technique. – occulus Jun 12 '12 at 14:23
  • Wow, no-one noticed that in a whole year? – occulus Jun 12 '12 at 14:24
  • The method naming does not follow Obj-C conventions. Similar methods shouldn't start with `get`. – Sulthan Apr 28 '13 at 18:57
9

Uniqueness is something that you need to provide-- randomness APIs won't do this for you.

As has been suggested, you can generate a number and then check to see whether it collides with something you've already generated, and if so, try agin. Please note however, that depending on the quantity of numbers and the size of the range, this becomes an algorithm that doesn't have a guaranteed end point.

If you're really just trying to get a contiguous set of numbers in random order, this is a not the way to do it, since it could take an unpredictably long time to complete. In this case, building an array of all desired values first, and then "shuffling" the array is a better choice. The best shuffle is the Fisher-Yates, but if you don't need it to be perfectly unbiased, you could also do what's described here.

Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
7

Checking for already generated numbers is potentially expensive (theoretically, it could take forever.) However, this is a solved problem. You want a shuffling algorithm, like the Fisher-Yates_shuffle

On iOS probably something like:

NSMutableArray *randSequence = [[NSMutableArray alloc] initWithCapacity:8];
for (int ii = 1; ii < 9; ++ii)
    [randSequence addObject:[NSNumber numberWithInt:ii]];

for (int ii = 8; ii > 0; --ii) {
    int r = arc4random() % (ii + 1);
    [randSequence exchangeObjectAtIndex:ii withObjectAtIndex:r];

// you can now iterate over the numbers in `randSequence` to get
// your sequence in random order
Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
Art Gillespie
  • 8,747
  • 1
  • 37
  • 34
  • 1
    if ii goes from 8 and is incremented, then it will always be > 0, hence this will definitely never terminate. – PengOne May 27 '11 at 14:51
  • This is quite late, but don't you have to take into account the bias?Should you not change `int r = arc4random() % 8` to `int r = arc4random() % i` ? – Byte Sep 03 '12 at 06:06
  • @Byte you are absolutely right, better yet it should be int r = arc4random_uniform(i)+1; – Kaan Dedeoglu Apr 28 '13 at 19:06
2

Store the numbers in an array, and each time you generate the next - check if it already exists in the array. If not, then add it and continue.

Luke
  • 11,426
  • 43
  • 60
  • 69
1

Here's some psuedo code

  1. For each number 1-8 generate a random number.
  2. Add the random number and the integer to a dictionary as a key value pair
  3. get all the keys of the dictionary as an array (hint: have a look at the allKeys method)
  4. Sort this array (ascending or descending isn't important)
  5. Now for each of these numbers as the key, get the corresponding integer from the dictionary
Abizern
  • 146,289
  • 39
  • 203
  • 257
  • It works, but shuffling is better due to efficiency though it shouldn't really matter with only eight integers. – vakio May 27 '11 at 14:55
  • I just wanted to provide another way of reordering a list without having to write some code. As for efficiency - I couldn't tell you without profiling or analysing the other algorithms. – Abizern May 27 '11 at 14:58
0

Try this code...this will give you all the possible unique number set in Mutable array...

-(NSInteger) randomNumber {
NSInteger newRandomNumber = (NSInteger) arc4random() % 10;
NSInteger uniqueNumber;
if ([self.arrayContainingNumbers containsObject: [NSNumber numberWithInteger:newRandomNumber]]) {
    [self randomNumber];
    } else {
    [self.arrayContainingNumbers addObject: [NSNumber numberWithInteger:newRandomNumber]];
}
uniqueNumber = [[self.mutableArrayContainingNumbers lastObject]integerValue];
     NSLog(@"new Unique Number is %ld",(long)uniqueNumber);

return uniqueNumber;  
}

Don't forget to add this method :)

    -(NSMutableArray *) arrayContainingNumbers {
if (!_mutableArrayContainingNumbers) {
    _mutableArrayContainingNumbers = [[NSMutableArray alloc] init];
}
return _mutableArrayContainingNumbers; 
}
Chetan
  • 881
  • 14
  • 22