Random and non repeated text

Discussion in 'iOS Programming' started by Jimmyl1, Jan 13, 2012.

  1. Jimmyl1 macrumors newbie

    Joined:
    Jan 12, 2012
    #1
    Is it possible somehow to make this non repeated? Im reading on the internet about the Fisher Yates method and Im wondering if its any easy way to use it in this easy codes.

    Fisher-yates shuffle method

    Code:
    - (void)shuffleArray:(NSMutableArray *)array {
        //fisher-yates shuffle
        for (int i = [array count] - 1; i > 0; i--) {
            int j = arc4random() % [array count];
            [array exchangeObjectAtIndex:i withObjectAtIndex:j];
        }
    }
    For example exchange the "% 6;" to some function?

    Code:
    -(IBAction)randomWords:(id)sender {
        int ran = arc4random() % 6;
        switch (ran) {
                case 0:
                    textlabel.text = @"Cat";
                    break;
                case 1:
                    textlabel.text = @"Dog";
                    break;
                case 2:
                    textlabel.text = @"Fish";
                    break;
                case 3:
                    textlabel.text = @"Horse";
                    break;
                case 4:
                    textlabel.text = @"Bird";
                    break;
                case 5:
                    textlabel.text = @"f";
                 break;    
                default:
                    break;
        }
    }
     
  2. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #2
    I'm confused, what exactly are you looking to do?

    Are you trying to shuffle a bunch of objects?

    Just call rand() repeatedly and assign the generated value to each object. Then sort the objects in ascending order by that random value. Seed random when your app launches with the current time, so that the "random" values aren't the same each time your app is closed and started again.
     
  3. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #3
    Wirelessly posted (Mozilla/5.0 (iPhone; CPU iPhone OS 5_0_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A405 Safari/7534.48.3)

    Or use arc4random(). It's self-seeding.
     
  4. Jimmyl1 thread starter macrumors newbie

    Joined:
    Jan 12, 2012
    #4
    Yes I'm trying to shuffle a bunch of objects/words.
    I wrote down the words in a textfile and trying now to change the Label randomly to one of the words of the textfile, how should the code look like?

    So far I got this:

    Code:
    -(IBAction)random:(id)sender {
        int index = (arc4random() % 6) - 1;
        wordslabel.text = NSString *path = [[NSBundle mainBundle] pathForResource:@"words" ofType:@"txt"];;
    }
    
    If anyone could help me with some guidance.
     
  5. ianray macrumors 6502

    Joined:
    Jun 22, 2010
    Location:
    @
    #5
    1. Get words from file into array
    2. Choose array entry index randomly
    3. ???
    4. Profit

    Sorry for trying to be funny, but this problem is straightforward when you break it down in to pieces. Good luck :)
     
  6. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #6
    I think some of you are forgetting that the random choices cannot be repeating. A couple of solutions come to mind: 1) remove the word from the array when chosen, 2) store the chosen indexes and check against them when choosing a new one.
     
  7. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #7
    After you have shuffled the array with the code you show then pick the first value in the array and remove it. Then repeat this next time. It won't repeat.

    This is just like a deck of cards. Shuffle the cards so they are in random order. Then take one card and remove it. Repeat this until there are no more cards. Then do it again.
     
  8. Jimmyl1, Jan 14, 2012
    Last edited: Jan 14, 2012

    Jimmyl1 thread starter macrumors newbie

    Joined:
    Jan 12, 2012
    #8
    Method 1.
    Do you mean something like this?

    .m file
    Code:
    @interface ViewController : UIViewController{
        IBOutlet UILabel* textlabel;
    NSMutableArray* randomArray;
    }
    @property (nonatomic, retain)			NSMutableArray* randomArray;
    
    .h file
    Code:
    -(IBAction)random:(id)sender {
        int index = (arc4random() % 6) - 1;
        textlabel.text = [[NSBundle mainBundle] pathForResource:@"words" ofType:@"txt"];
        self.textlabel = [NSArray arrayWithContentsOfFile: @"/Users/jimmylind91/Documents/xCode/test3/test3"];
        [randomArray removeLastObject];
        if ([randomArray count] ==0)
    		return nil;
        
    }
    
     
  9. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #9
    Here's the steps you need to do:

    1 - Put the contents of the text file into an NSString. (You're currently putting the contents into a label, which, while it works, violates MVC.)
    2 - Break the NSString into an NSArray of substring NSStrings. (Look through the documentation on NSString; there's a handy method that'll do this for you.)
    3 - Make a mutable copy of the NSArray.
    4 - [array objectAtIndex: (rand() % [array count])]; will give you a random object from your array. Or at least it should. If you don't understand why, looking into the modulus operator (%) would be a good idea.
    5 - Do whatever you need to with the random object from the list.
    6 - NSMutableArray has a handy method that'll remove an object from it for you. Check the documentation.
    7 - Repeat steps 3 through 6 as many times as you need to.

    Oh, and as a zeroth step, you should also seed random when your app starts up, possibly with the current time.
     
  10. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #10
    The OP is already aware of, and using, arc4random().
     
  11. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #11
    Alright... I was unaware of the superiority of arc4random(). I thought it had something to do with trig for some reason...
     
  12. mobilehaathi macrumors G3

    mobilehaathi

    Joined:
    Aug 19, 2008
    Location:
    The Anthropocene
    #12
    I think the Fisher-Yates shuffle is about as good as you'll get in terms of efficiency. What about it don't you like? Given a good random number generator each possible permutation will happen with 1/n! probability. The only problem I see is that I believe you should have

    Code:
    int j = arc4random() % i+1;
    instead of

    Code:
    int j = arc4random() % [array count];
     
  13. Jimmyl1 thread starter macrumors newbie

    Joined:
    Jan 12, 2012
    #13
    How do I use the Fisher-Yates in the IBAction function?
    Something like this?

    Code:
    - (void)shuffleArray:(NSMutableArray *)array {
        //fisher-yates shuffle
        for (int i = [array count] - 1; i > 0; i--) {
            int j = arc4random() % i+1;
            [array exchangeObjectAtIndex:i withObjectAtIndex:j];
        }
    }
    Code:
    -(IBAction)randomWords:(id)sender {
        for (int i = [array count] - 1; i > 0; i--) {
        int j = arc4random() % i+1;
        switch (j) {
            case 0:
                textlabel.text = @"Cat";
                break;
            case 1:
                textlabel.text = @"Dog";
                break;
            case 2:
                textlabel.text = @"Fish";
                break;
            case 3:
                textlabel.text = @"Horse";
                break;
            case 4:
                textlabel.text = @"Bird";
                break;
            case 5:
                textlabel.text = @"f";
                break;    
            default:
                break;
            }
        }
    }  
     
  14. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #14
    Nothing like that.

    You're not breaking it down and thinking it through. Forget the code for a minute. Just think it through.

    You have an array of strings that's already shuffled. That means its items are in random order. What do you have to do to get one random item from the array?

    The answer is simple: take the first item. That's all. Why? Because the items are already in random (shuffled) order.

    Next, to prevent repeats, what do you have to do?

    Again, the answer is simple: remove the first item (the item you took). Think about why this works.

    Finally, since the item is a string, what do you have to do to set a label's text to that item?

    The answer here is also simple. You've already shown code for it. But in your code, you're assigning a string constant (like @"Dog") instead of the item from the array.

    So break the problem down into simple solvable steps, then solve each step, then write the code for each solved step. Notice that this is entirely about logical thinking and simple solutions, except for the last part, which is writing the code. The code you posted makes no logical sense, does way more than necessary (randomly picking items from a shuffled array), and is simply nonsense when it comes to assigning texts to labels. Fisher-Yates won't help you if your code is illogical nonsense. Think first; code later.

    I'm assuming the array holds strings that are intended to be used as actual label text. If the array holds numbers or other things, then you should rethink what to change, or rethink what's in the file you read from disk.
     
  15. Jimmyl1 thread starter macrumors newbie

    Joined:
    Jan 12, 2012
    #15
    Okay let me know if I got this right, first of all I need to break down the problem in different steps, just like ianray said before.

    If I understood it all this is what I need to accomplish?
    • Make the words from the file get in the array/index
    • Make the words from the array/index appear randomly
    • Remove words After the words appears from the array/index
    • If the array/index has come down to 0 words make it return nil?
     
  16. mobilehaathi macrumors G3

    mobilehaathi

    Joined:
    Aug 19, 2008
    Location:
    The Anthropocene
    #16
    Well, once you've exhausted your shuffled array of label names, what would you like to happen?
     
  17. MattInOz macrumors 68030

    MattInOz

    Joined:
    Jan 19, 2006
    Location:
    Sydney
    #17
    It seems pretty expensive to me to create an array of NSStrings just to trash them one by one if your going to need them again.

    I wonder if you could do the same with say a NSMutableIndexSet. Pick an Index at random out of the remaining ones then delete it from the set. Sure you'd need to reboot the NSIndexSet once it was empty but seems a lot cheaper than a new array.
     
  18. mobilehaathi macrumors G3

    mobilehaathi

    Joined:
    Aug 19, 2008
    Location:
    The Anthropocene
    #18
    Actually, I'm not really sure what precisely the OP wants to accomplish. If he is just trashing them as he uses them, then your solution seems better. If the OP wants to keep the set/array of strings and permute again after running through the first random permutation, then he shouldn't trash them at all and the random permutation algorithm he has will work well to generate another random permutation.
     
  19. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #19
    @ MattInOz, those two strategies are exactly the same. Prove me wrong.
     
  20. mobilehaathi macrumors G3

    mobilehaathi

    Joined:
    Aug 19, 2008
    Location:
    The Anthropocene
    #20
    I'm not familiar with the specific implementation of NSMutableIndexSet, but it could potentially be marginally less expensive to pick a random element and delete it versus shuffling an NSMutableArray then deleting the elements in order.

    However, I do agree that the end result is the same.
     
  21. MattInOz macrumors 68030

    MattInOz

    Joined:
    Jan 19, 2006
    Location:
    Sydney
    #21
    Well to me the big fundamental difference is that one is entirely independent of the data. It just works on making sure you have a stream of indexes that don't repeat till all are exhausted. You could apply it to switch/case method if the data is small or in another case that the index would be enough, like say a deck of cards.
    Not sure how I really prove that to you. Other than maybe a test example.:D

    Note: All code is ARC. Taking xCode Command Line Application Templet
    Example will Log out 40 Animal Names without repeating one of the set until all have been used.

    main.m file
    Code:
    #import <Foundation/Foundation.h>
    #import "NSMutableIndexSet+oneTime.h"
    
    int main (int argc, const char * argv[])
    {
    
        @autoreleasepool {
            
            // insert code here...
            NSLog(@"Hello, World!");
            NSRange indexRange = NSMakeRange(0, 6);
            NSMutableIndexSet *newIndexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:indexRange];
            for (int i=0; i<40; i++) 
            {
                NSUInteger x = [newIndexSet oneTimeRandomIndex];
                if (x == NSNotFound) 
                {
                                           // NSLog(@"newIndexSet needs to be refilled");
                    [newIndexSet addIndexesInRange:indexRange];
                    x = [newIndexSet oneTimeRandomIndex];
                    
                }
                switch (x) {
                    case 0:
                        NSLog(@"Cat");
                        break;
                    case 1:
                        NSLog(@"Dog");
                        break;
                    case 2:
                        NSLog(@"Fish");
                        break;
                    case 3:
                        NSLog(@"Horse");
                        break;
                    case 4:
                        NSLog(@"Bird");
                        break;
                    case 5:
                        NSLog(@"Wombat");
                        break;    
                    default:
                        break;
                }
                
            }
    
             NSLog(@"See Ya Later, World!");
        }
        return 0;
    }
    
    Oh yeah important bit a category on NSMutableIndexSet. Although you could do it without this but now that I've thought about it I want to be able to reuse.
    NSMutableIndexSet+oneTime.h
    Code:
    
    #import <Foundation/Foundation.h>
    
    @interface NSMutableIndexSet (oneTime)
    -(NSUInteger)oneTimeRandomIndex;
    //returns one index from set at random, removing the index from the set so that it won't be returned again.
    //As the index set is mutable indexes maybe re-added to the set to become re-eligsble for return by this method.
    //Will return NSNotFound if NSMutableIndexSet is empty.
    @end
    
    NSMutableIndexSet+oneTime.m
    Code:
    #import "NSMutableIndexSet+oneTime.h"
    
    @implementation NSMutableIndexSet (oneTime)
    -(NSUInteger)oneTimeRandomIndex
    {
        if (self.count < 1) 
        {
            return NSNotFound;
        }
        NSUInteger x = [self lastIndex];
        x = arc4random() % x;
        x = [self indexLessThanOrEqualToIndex:x];
        if (x == NSNotFound) 
        {
            x = [self indexLessThanOrEqualToIndex:x];
        }
        [self removeIndex:x];
        return x; 
    }
    
    @end
    
     
  22. Jimmyl1 thread starter macrumors newbie

    Joined:
    Jan 12, 2012
    #22
    I guess I need to make it start over from the beginning?

    MattInOz, That was some advanced coding for me, care for a little more explaining?
     
  23. mobilehaathi, Jan 17, 2012
    Last edited: Jan 17, 2012

    mobilehaathi macrumors G3

    mobilehaathi

    Joined:
    Aug 19, 2008
    Location:
    The Anthropocene
    #23
    Ok I normally don't do this, but you should probably have something like this.

    Code:
    - (void)shuffleArray:(NSMutableArray *)array {
        //fisher-yates shuffle
        for (int i = [array count] - 1; i > 0; i--) {
            int j = arc4random() % i+1;
            [array exchangeObjectAtIndex:i withObjectAtIndex:j];
        }
    }
    
    -(IBAction)randomWords:(id)sender {
        
    	textlabel.text = [array objectAtIndex:currentIndex];
    	
    	currentIndex++;
    	
    	if(currentIndex >= [array count])
    	{
    		[shuffleArray array];
    		currentIndex = 0;
    	}
               
    }
    
    array here is an NSMutableArray filled with your strings. You would shuffle the array once after you've read in your data (not shown above). You'll also want to keep an int (currentIndex above, which you should init to 0 when you read in your data) to track your index in the array. This doesn't trash your strings as you use them, but rather once you've reached the end of your shuffled array it resets your current index and shuffles the array again. Does this make sense? I fear that by giving you the code you might not understand what's going on.
     
  24. Jimmyl1 thread starter macrumors newbie

    Joined:
    Jan 12, 2012
    #24
    By watching the codes and reading your explanation I start to get it but I wonder what
    Code:
    	currentIndex++;
    does and means (the ++)
     
  25. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #25
    x++ is post increment.

    It means add one to x (or whatever variable you have in place of x, in this case currentIndex.)

    I suspect you haven't learned much C yet and would suggest that you do.
     

Share This Page