2

I am trying to randomize numbers in an array. I am able to do that using arc4random() % [indexes count]

My problem is - If an array consists of 20 items, every time the array shuffles, in a batch of 5, different number should appear. Example :

first shuffle: 1,4,2,5,6.

second shuffle: 7,12,9,15,3

-(IBAction)randomNumbers:(UIButton *)sender
{
    int length = 10; // int length = [yourArray count];

    NSMutableArray *indexes = [[NSMutableArray alloc] initWithCapacity:length];
    for (int i=0; i<5; i++)
        [indexes addObject:[NSNumber numberWithInt:i]];

    NSMutableArray *shuffle = [[NSMutableArray alloc] initWithCapacity:length];

    while ([indexes count])
    {
        int index = arc4random() % [indexes count];
        [shuffle addObject:[indexes objectAtIndex:index]];
        [indexes removeObjectAtIndex:index];
    }

    //    for (int i=0; i<[shuffle count]; i++)
    NSLog(@"%@", [shuffle description]);
}
Monolo
  • 18,205
  • 17
  • 69
  • 103
orgami
  • 161
  • 3
  • 15
  • see SO answer http://stackoverflow.com/questions/1617630/non-repeating-random-numbers – Deepesh Nov 30 '12 at 06:47
  • i checked it generates random numbers ..but when i click the button second time it generates random numbers but all the numbers are same ..it doesnt generate different random numbers other than the one obtained in first shuffle. – orgami Nov 30 '12 at 07:33
  • If something is non-repeating, it isn't random. – Tim Apr 28 '13 at 19:29

4 Answers4

2

As per your requirement....kindly check this code

Make this a property

@synthesize alreadyGeneratedNumbers;

Add these methods in your .m

-(int)generateRandomNumber{

    int TOTAL_NUMBER=20;

    int low_bound = 0;
    int high_bound = TOTAL_NUMBER;
    int width = high_bound - low_bound;
    int randomNumber = low_bound + arc4random() % width;

    return randomNumber;
}


-(IBAction)randomNumbers:(UIButton *)sender
{

    NSMutableArray *shuffle = [[NSMutableArray alloc] initWithCapacity:5];

    BOOL contains=YES;
    while ([shuffle count]<5) {
        NSNumber *generatedNumber=[NSNumber numberWithInt:[self generateRandomNumber]];
        //NSLog(@"->%@",generatedNumber);

        if (![alreadyGeneratedNumbers containsObject:generatedNumber]) {
            [shuffle addObject:generatedNumber];
            contains=NO;
            [alreadyGeneratedNumbers addObject:generatedNumber];
        }
    }

    NSLog(@"shuffle %@",shuffle);
    NSLog(@"Next Batch");


    if ([alreadyGeneratedNumbers count] >= TOTAL_NUMBER) {
        NSLog(@"\nGame over, Want to play once again?");//or similar kind of thing.
        [alreadyGeneratedNumbers removeAllObjects];
    }


}

Still I feel you need to some changes like

it will give you correct value, but what if user pressed 5th time?

out of 20 numbers you already picked 4 sets of 5 number, on on 6th time it will be in loop to search for next set of numbers and will become infinite.

So what you can do is, keep the track of shuffle and once it reaches the limit i.e, 20/5=4 disable the random button.

Anoop Vaidya
  • 46,283
  • 15
  • 111
  • 140
  • should i add these in while ([indexes count]) loop – orgami Nov 30 '12 at 06:55
  • i am getting sigabrt ..whats low_bound and high_bound ? – orgami Nov 30 '12 at 07:14
  • nope i am getting sigbrt ...error *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 3 beyond bounds [0 .. 1]' sometime its repeating the numbers – orgami Nov 30 '12 at 07:31
  • ohhh becoz your high bound is always fixed to 5, and it is generaign a number between 0-4, and you ar removing object from the indexes array... after two removeObjAtIndex... just change the high bound to [indexes count] – Anoop Vaidya Nov 30 '12 at 07:49
  • `code`while ([indexes count]) { int low_bound = 0; int high_bound = 5; int width = [indexes count] - low_bound; // 5 int value = low_bound + arc4random() % width; // 0 + 0..4 NSLog(@"Random no generated : %d",value); //int index = arc4random() % [indexes count]; [shuffle addObject:[indexes objectAtIndex:value]]; [indexes removeObjectAtIndex:value]; }`code` am i correct ? – orgami Nov 30 '12 at 07:51
  • its like there are maximum numbers say 20 ..out of these 20 ..there will be a limit of numbers say 6 which can appear .. only 6 numbers should appear in random order and those numbers should be <=20 numbers – orgami Nov 30 '12 at 08:03
1

Declare array that contains already generated number in extension or header file

@property (strong, nonatomic)NSMutableArray *existingNums;
@property (assign, nonatomic)NSInteger maxLimit;
@property (assign, nonatomic)NSInteger minLimit;

Then implement given code in implementation file

@synthesize existingNums;
@synthesize maxLimit;
@synthesize minLimit;

- (NSInteger)randomNumber {

    if(!existingNums)
        existingNums = [NSMutableArray array];

    while (YES) {

        NSNumber *randonNum = [NSNumber numberWithInteger:minLimit+arc4random()%maxLimit];

        if([existingNums containsObject:randonNum]) {

            if([existingNums count] == (maxLimit - minLimit))
                return -1; // All possible numbers generated in the given range

            continue;
        }

        [existingNums addObject:randonNum];

        return [randonNum integerValue];
    }

    return -1;   // return error
}

Hope this will help you :)

Augustine P A
  • 5,008
  • 3
  • 35
  • 39
1

This one works for me:

    NSMutableArray *numbers = [NSMutableArray new];
    BOOL addElement = YES;
    int limit = 100; // Range from 0 to 36
    int numElem = 10; // Number of elements
    do
    {
        int ranNum = (arc4random() % limit) +1;
        if ([numbers count] < numElem) {
            for (NSNumber *oneNumber in numbers) {
                addElement =([oneNumber intValue] != ranNum) ? YES:NO;
                if (!addElement) break;
            }
            if (addElement) [numbers addObject:[NSNumber numberWithInt:ranNum]];
        } else {
            break;
        }
    } while (1);
    NSLog(@"%@",numbers);
zago
  • 532
  • 2
  • 10
0

The problem with all these answers is that you need to review your previous generated random numbers and that takes extra time if you need a large number of random integers.

Another solution is using cryptography:

  1. Generate a random key
  2. Iterate between 0..n
  3. Encrypt each integer and apply modulo the number of alternatives do you want to use to the output of the function.

There are some extra details to take into account that don't matter for your case.

sw.
  • 3,240
  • 2
  • 33
  • 43