views:

241

answers:

5

I have 2 numbers which are between 0 and 49. Let's call them x and y. Now I want to get a couple of other numbers which are not x or y, but are also between 0 and 49 (I am using Objective C but this is more of a general theory question I think?).

Method I thought of is:

 int a;
 int b;
 int c;

 do {
  a = arc4random() % 49;
 } while ((a == x) || (a == y));

 do {
  b = arc4random() % 49;
 } while ((b == x) || (b == y) || (b == a));

 do {
  c = arc4random() % 49;
 } while ((c == x) || (c == y) || (c == a) || (c == b));

But it seem kind of bad to me, I don't know, I am just trying to learn to be a better programmer, what would be the most elegant way to do this for best practices?

+5  A: 

You should shuffle an array of numbers (of values [0, ..., 49] in your case; you can also exclude your x and y from that array if you already know their values), then grab the first N values (however many you're seeking) from the shuffled array. That way, all the numbers are randomly of that range, and not "seen before".

Chris Jester-Young
But exclude x and y from the initial array.
invariant
@invariant: Yes, I missed reading that in the question. +1
Chris Jester-Young
Yes, I am going to do this using the Fisher-Yates shuffle... let me ask you however.. does my method produce a not-correct randomness? It seems you are saying that, and it's weird because I feel logically like it should, but it doesn't seem to?
Cocorico
@Cocorico: Some would say that using modulo for truncating the value of a random number is non-uniform, because it favours lower numbers. (Example: suppose the range of random numbers is 0..9, and you're trimming the value to 0..2. If you used `% 3`, then there is a bigger probability of getting 0 (40%) than 1 or 2 (30% each).)
Chris Jester-Young
@Cocorico: Now, `arc4random` has a much bigger range than 0..9, so, the effects of this should be minimal. But just be mindful of it. :-)
Chris Jester-Young
+6  A: 

You can use something called the Fisher-Yates shuffle. It's an efficient algorithm for producing a randomly ordered list of values from some set. You would first exclude N from the list of values from which to get random values, and then perform the shuffle.

LBushkin
Great results and easy to implement. What more could one ask for?
dash-tom-bang
Thanks, yes, I will try this... I already use the Fisher-Yates shuffle elsewhere I just realized.. I didn't think of it for this use for some reason.. I feel like my method should at least produce a random shuffle, but the results are saying no, so I shall try that.thanks!
Cocorico
I actually refrained from mentioning Fisher-Yates in my answer for a good reason: I wanted to encourage readers to use the shuffle function that their platform provides, rather than implementing shuffling by hand. Pretty much all standard in-place array shuffling implementations (e.g., `std::random_shuffle` in C++, `Collections.shuffle` in Java, etc.) all use Fisher-Yates.
Chris Jester-Young
+1  A: 

I'd do something more along the lines of:

NSMutableSet * invalidNumbers = [NSMutableSet set];
[invalidNumbers addObject:[NSNumber numberWithInt:x]];
[invalidNumbers addObject:[NSNumber numberWithInt:y]];

int nextRandom = -1;
do {
  if (nextRandom >= 0) {
    [invalidNumbers addObject:[NSNumber numberWithInt:nextRandome]];
  }
  nextRandom = arc4random() % 49;
} while ([invalidNumbers containsObject:[NSNumber numberWithInt:nextRandom]]);
Dave DeLong
A: 

You could add x, y and the new number to a data structure that you can use as a set and do something like (in pseudo-code; the set structure needs something like push to add values and in for checking membership):

number_of_randoms = 2;

set.push(x);
set.push(y);

for (i = 0; i<number_of_randoms; i++) {
  do {
    new_random = arc4random() % 49;
  } while !set.in(new_random);
  set.push(new_random);
}

So if objc has something appropriate, this is easy...[aha, it does, see Dave DeLong's post].

This algorithm makes sense if number_of_randoms is much less than 49; if they are comparable then you should one of the shuffle (aka permutation) ideas.

Andrew Jaffe
A: 

First, make a set of valid numbers:

// Create a set of all the possible numbers
NSRange range = { 0, 50 };// Assuming you meant [0, 49], not [0, 49)
NSMutableSet *numbers = [NSMutableSet set];
for (NSUInteger i = range.location; i < range.length; i++) {
    NSNumber *number = [NSNumber numberWithInt:i];
    [numbers addObject:number];
}

// Remove the numbers you already have
NSNumber *x = [NSNumber numberWithInt:(arc4random() % range.length)];
NSNumber *y = [NSNumber numberWithInt:(arc4random() % range.length)];
NSSet *invalidNumbers = [NSSet setWithObjects:x, y, nil];
[numbers minusSet:invalidNumbers];

Then, if you don't need the numbers to be guaranteed to be random, you could use -anyObject and -removeObject to pull out a couple of other numbers. If you do need them to be random, then follow LBushkin's answer, but be careful not to accidentally implement Sattolo's algorithm:

// Shuffle the valid numbers
NSArray *shuffledNumbers = [numbers allObjects];
NSUInteger n = [shuffledNumbers count];
while (n > 1) {
    NSUInteger j = arc4random() % n;
    n--;
    [shuffledNumbers exchangeObjectAtIndex:j withObjectAtIndex:n];
}
lemnar