Skip to content

Instantly share code, notes, and snippets.

@jonathanpenn
Last active December 11, 2015 14:09
Show Gist options
  • Save jonathanpenn/4612637 to your computer and use it in GitHub Desktop.
Save jonathanpenn/4612637 to your computer and use it in GitHub Desktop.
Here's how I'm generating a set of 1,000 random strings with between 4 and 14 random lowercase characters. Is there a better way?
@implementation SomeGenerater
- (NSArray *)generateAThousandRandomStrings
{
NSMutableArray *strings = [[NSMutableArray alloc] initWithCapacity:1000];
for (int count = 0; count < 1000; count++) {
int length = arc4random_uniform(10) + 4;
unichar buf[length];
for (int idx = 0; idx < length; idx++) {
buf[idx] = (unichar)('a' + arc4random_uniform(26));
}
[strings addObject:[NSString stringWithCharacters:buf length:length]];
}
return strings;
}
@end
@jonathanpenn
Copy link
Author

Speed isn't that important. I'm more looking for theoretical purity using Foundation. Is there a better way to do this?

@jonathanpenn
Copy link
Author

Update to use arc4random_uniform(...). Thanks to https://twitter.com/LordLobo/status/294179567743168512

@jonathanpenn
Copy link
Author

Updated to just add integer value of character from https://twitter.com/thejohnturner/status/294179919167102976.

@LordLobo
Copy link

arc4random_uniform(10) + 4 is range 4-14

@aglee
Copy link

aglee commented Jan 23, 2013

I was going to suggest using an array of unichar, along with stringWithCharacters:length:, but it's no "purer" than your approach, and the speed difference in my test was like 0.003 seconds, and besides you said speed doesn't matter.

@kconner
Copy link

kconner commented Jan 23, 2013

- (NSArray *)generateAThousandRandomStrings
{
    NSRange ranges[1000];
    int totalLength = 0;
    for (int i = 0; i < 1000; i++) {
        int length = 4 + arc4random_uniform(10);
        ranges[i] = NSMakeRange(totalLength, length);
        totalLength += length;
    }

    int const len = totalLength + 1;
    char bytes[len];
    for (int i = 0; i < totalLength; i++) {
        bytes[i] = 'a' + arc4random_uniform(26);
    }
    bytes[totalLength] = '\0';

    // I *think* this will copy out of bytes.
    NSString *string = [NSString stringWithCString:bytes encoding:NSASCIIStringEncoding];

    NSMutableArray *substrings = [NSMutableArray arrayWithCapacity:1000];
    for (int i = 0; i < 1000; i++) {
        // I *think* this will reuse the backing store of the string for all the substrings.
        [substrings addObject:[string substringWithRange:ranges[i]]];
    }

    return substrings;
}

And then I saw your comment about speed not mattering… but this is more about storage :)

@eraserhd
Copy link

Here's what I came up with. Not purer, but definitely funner. Based on the fact that the count of each length should be pretty close.

- (NSArray *)generateAThousandStrings
{
    int characters_per_cycle = 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13;
    int total_characters = 10*characters_per_cycle;
    NSMutableString *data = [[NSMutableString alloc] initWithCapacity:1000 + total_characters];
    for (int i  = 0; i < total_characters; ++i) {
        [data appendFormat:@"%c", arc4random_uniform(26) + 'a'];
        switch (i%characters_per_cycle) {
        case 4-1:
        case 4+5-1:
        case 4+5+6-1:
        case 4+5+6+7-1:
        case 4+5+6+7+8-1:
        case 4+5+6+7+8+9-1:
        case 4+5+6+7+8+9+10-1:
        case 4+5+6+7+8+9+10+11-1:
        case 4+5+6+7+8+9+10+11+12-1:
        case 4+5+6+7+8+9+10+11+12+13-1:
            [string appendString:@"/"];
        }
    }

    return [data componentsSeparatedByString:@"/"];
}

@aglee
Copy link

aglee commented Jan 23, 2013

FWIW here's the array-of-unichar code I tested with:

NSTimeInterval timeBefore = [NSDate timeIntervalSinceReferenceDate];
NSMutableArray *strings = [[NSMutableArray alloc] initWithCapacity:1000];

for (NSUInteger count = 0; count < 1000; count++)
{
    NSUInteger length = arc4random_uniform(10) + 4;
    unichar buf[length];

    for (NSUInteger idx = 0; idx < length; idx++)
    {
        buf[idx] = (unichar)('a' + arc4random_uniform(26));
    }

    [strings addObject:[NSString stringWithCharacters:buf length:length]];
}

NSTimeInterval timeAfter = [NSDate timeIntervalSinceReferenceDate];
NSLog(@"time 1: %f", (timeAfter - timeBefore));

@jonathanpenn
Copy link
Author

@kconner ha! If I was going for speed I might even try to use malloc() instead. :) I'm going for purity more as part of an exercise in readable, idiomatic Objective C. The speed and memory concerns didn't matter. I just didn't want to bog down the reader of the example with extra stuff.

@jonathanpenn
Copy link
Author

@aglee, this is MUCH better! It's just enough C that it won't detract from the larger concept I'm trying to get at. I think I'm going with this.

@eraserhd
Copy link

Shortest solution:

t(a,b) { return arc4random_uniform(a)+b; }

- (NSArray *)generateAThousandStrings
{
    NSMutableString *data = [[NSMutableString alloc] initWithCapacity:1000*15];
    for (int i = 1000, e = t(10,5); i || e; --e || --i)
        [data appendFormat:@"%c", e ? t(26,'a') : (e = t(10,5), '/')];
    return [data componentsSeparatedByString:@"/"];
}

@jonathanpenn
Copy link
Author

@eraserhd you have proven yourself with this one. :)

@eraserhd
Copy link

@jonathanpenn There, I extracted a function. Shorter now. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment