Reputation: 768
I've a problem with the generation of random values with arc4random in Xcode. I want to exclude from the random generation those number that are already picked up. This is the algorithm that I've wroten
int randomValue = (arc4random() % numberofquest)+ 1;
int kk=1;
if (kk==1) {
//first time add the first random value
[oldquest addObject: randomValue];
}
else {
//control if the number is already in the vector
for (int j=1; j<[oldquest count]; j++)
{
if (randomValue==oldquest[j])
{
randomValue = (arc4random() % numerodomande)+ 1;
}
else
{
[oldquest addObject: randomValue];
}
}
}
kk=kk+1
But It doesn't work. I suppose maybe because randomvalue and the j-th object in the array are not comparable (int the first and string the second?). Can anyone help me please?
Upvotes: 1
Views: 1380
Reputation: 4254
I'm sorry, Ares answer was almost right. You can compare two different NSNumber objects.
Here is my solution:
- (int) generateRandomNumber {
int number = arc4random() % 100;
if ([chosen_numbers indexOfObject:[NSNumber numberWithInt:number]]!=NSNotFound)
number = [self generateRandomNumber];
[chosen_numbers addObject:[NSNumber numberWithInt:number]];
return number;
}
Since Alessandro has some trouble implementing it heres an example inside a UIViewController class. This works for 100 numbers:
#import "ViewController.h"
@implementation ViewController {
NSMutableArray * chosen_numbers;
}
- (void)viewDidLoad
{
[super viewDidLoad];
chosen_numbers = [[NSMutableArray alloc] init];
for(int i = 0; i<90; i++) {
NSLog(@"number: %d",[self generateRandomNumber]);
}
}
- (int) generateRandomNumber {
int number = arc4random() % 100;
if ([chosen_numbers indexOfObject:[NSNumber numberWithInt:number]]!=NSNotFound)
number = [self generateRandomNumber];
[chosen_numbers addObject:[NSNumber numberWithInt:number]];
return number;
}
@end
Upvotes: 2
Reputation: 31745
You are right - it doesn't work because here:
if (randomValue==oldquest[j])
you are trying to compare an int
with an object... NSArrays can only store objects. In fact this line should not work for that reason:
[oldquest addObject: randomValue];
You need to box the int as an NSNumber and store that in the array:
NSNumber* boxedRandomValue = [NSNumber numberWithInt:randomValue];
[oldquest addObject: boxedRandomValue];
Then unbox it using the NSNumber instance method -(int)intValue
before comparing values:
if (randomValue==[oldquest[j] intValue])
update
There are a few other issues you will have to attend to:
the value of kk is reset to 1 on each iteration of the test, so kk == 1
is always true, the else
clause is never invoked. You need to set it once only outside of this block of code (for example you could make it a property, set it to 1 on initialise, then access and increment it here). Better still, just use [oldquest count] instead: if ([oldquest count]==0) {} else {}
. Then you can dispense with your kk
counter altogether.
your for-loop starts with j=1. This should be j=0 to address the first item in the array (item 0).
update 2
This line: randomValue = (arc4random() % numerodomande)+ 1
is going to cause all sort of other problems due to it's position in the checking loop. Try one of these suggestions:
just return
when you come across a dupe. No number gets added to the array...
set a BOOL test inside the loop, deal with it outside:
BOOL repeatedValue = NO;
for (int j=0; j<[self.oldquest count]; j++){
if (randomValue==[self.oldquest[j] intValue]) {
repeatedValue = YES;
break;
}
}
if (repeatedValue){
NSLog (@"value repeated");
[self addRandom];
//recursive call to this code block,
//assuming it is a method called `addRandom`
}
Try a compact version of the last suggestion (similar to Odrakir's solution) - I've enclosed it in an addRandom
method so you can see how to call it recursively.
- (void) addRandom {
int numberofquest = 5;
int randomValue = (arc4random() % numberofquest)+ 1;
NSNumber* boxedValue = [NSNumber numberWithInt:randomValue];
if ([self.oldquest indexOfObject:boxedValue]==NSNotFound) {
[self.oldquest addObject: boxedValue];
} else {
[self addRandom];
}
}
(If you do loop until you find a unique number you will have to watch out, as your total set of numbers is finitely limited to numberofquest
, so when you have a full set you may end up with an infinite loop.)
Instead of using NSMutableArray, you could use MutableOrderedSet instead - it's an ordered collection of unique objects, so will not add an object twice.
in @interface
@property (nonatomic, strong) NSMutableOrderedSet setOfRandoms;
in @implementation
int randomValue = (arc4random() % numberofquest)+ 1;
NSNumber randomValueBoxed = [NSNumber numberWithInt:randomValue];
[setOfRandoms addObject:randomValueBoxed];
update 3
The previous hints assumed it was the list of randoms you were interested in. Here is a complete solution to return a new unique random int in a self-contained method.
You need to set up 2 properties in your @interface and initialise them somewhere:
@property (nonatomic, assign) int maxRand;
//stores the highest allowed random number
@property (nonatomic, strong) NSMutableArray* oldRands;
//stores the previously generated numbers
uniqueRandom
returns a unique random number between 1 and self.maxRand each time. If all allowable numbers have been returned it returns 0.
- (int) uniqueRandom {
int result = 0;
if ([self.oldRands count] != self.maxRand) {
int randomValue = (arc4random() % self.maxRand )+ 1;
NSNumber* boxedValue = [NSNumber numberWithInt:randomValue];
if ([self.oldRands indexOfObject:boxedValue]==NSNotFound) {
[self.oldRands addObject: boxedValue];
result = randomValue;
} else {
result = [self uniqueRandom];
}
}
return result;
}
You should consider that it doesn't make sense to change self.maxRand once it is initialised unless you also reset self.oldRands. So you might want to use a const
or #define
instead, and/or tie it in to your self.oldRands
initialiser.
Upvotes: 1
Reputation: 5903
How about this:
-(void) generateRandomNumber {
int randomValue = arc4random_uniform(numberofquest) + 1;
if([oldQuest indexOfObject:[NSNumber numberWithInt:randomValue] == NSNotFound) {
//Unique value
[oldQuest addObject:[NSNumber numberWithInt:randomValue]];
}
else {
//Value already exists. Look for another one
return [self generateRandomNumber];
}
}
Obviously oldQuest is NSMutableArray instance that was previously initialized.
Upvotes: 1