ashaman has asked for the wisdom of the Perl Monks concerning the following question:

i'm having a little problem. i wrote this random mnemonic password generator that's supposed to make an 8 digit password following the (very simple) pattern of "consanant, vowel, consanent, vowel, . . ." the problem is, on occasion, it only makes a 7 digit pass, and sometimes it will break the intended pattern and and put two vowels in a row. i have no idea what's causing this. here's the code:
print "gimme a seed: "; $s = <STDIN>; srand ($s ^ time); @chars = qw(b c d f g h j k l m n p r s t v w x z); @vowels = qw(a e i o u y); for($i = 1; $i <= 4; $i++) { print $chars[int(rand(20))], $vowels[int(rand(5))]; }

Replies are listed 'Best First'.
Re: random passgen
by Aighearach (Initiate) on May 04, 2001 at 07:02 UTC
    The problem is all relating to your hard coding the length of your arrays into the rand() calls. Never count your lists by hand! Never count anything that relies on parts of your code; if it relies on the code, make it dynamic. Try this instead:
    print "gimme a seed: "; $s = <STDIN>; srand ($s ^ time); @chars = qw(b c d f g h j k l m n p r s t v w x z); @vowels = qw(a e i o u y); for($i = 1; $i <= 4; $i++) { print $chars[int(rand(@chars))], $vowels[int(rand(@vowels))]; }
    Notice that all i did was replace the hard coded values with the arrays, which will be correctly used by rand.
    Yay! for context!
Re: random passgen
by bent (Scribe) on May 04, 2001 at 06:42 UTC
    I suspect that your problem is in the indexing - you've used rand(20) and rand(5) although there are 19 consonants and 6 vowels. Here's my version of your code:
    print "gimme a seed: "; my $s = <STDIN>; my @chars = qw(b c d f g h j k l m n p r s t v w x z); my @vowels = qw(a e i o u y); my $length = 4; #Should be (length of password)/2 srand ($s ^ time); print $chars[int(rand(@chars))], $vowels[int(rand(@vowels))] while($le +ngth--);
    update: Thanks to chipmunk and Aighearach change int(rand(length(@vowels))) to <int(rand(@vowels)) - As Aighearach says, "Yay! for context!" :) hth, bent
      The int's are redundant as well. The idiom for grabbing a random item of an array is just:
      my $one_char_at_random = $chars[rand @chars];
      No point in adding noise parens or noisy int functions.

      -- Randal L. Schwartz, Perl hacker

Re: random passgen
by chipmunk (Parson) on May 04, 2001 at 06:43 UTC
    You've only got 18 consonants (you're missing q), but you're generating random indexes between 0 and 19 (inclusive). On the other side, you've got 6 vowels, but you're only generating indexes between 0 and 4 (inclusive).

    Add in q, and change your code to rand 19 and rand 6.

    Update: bent makes a great point; instead of hard-coding the numbers in the calls to rand, use the size of the array: rand @chars and rand @vowels.

(tye)Re: random passgen
by tye (Sage) on May 04, 2001 at 19:03 UTC
      indeed, it does work better with that. what exactly does the ~0 do, though? i've never seen that before.

      btw, thanks everyone for you help, it works great now.

        ~0 is bit-wise compliment of 0, that is, it gives back an unsigned integer with all bits 1 (since 0 has all bits 0). So rand(~0) gives you a random number between 0 and the largest unsigned integer value. This gives you about 32 bits of randomness from the seed that Perl probably already chose for you to add in with the seed that you chose.

        It should really be rand(1+~0) since the ^ that follows converts to the floating-point value returned by rand() into an unsigned integer which means that the possible values used in the ^ are only from 0..(~0-1).

                - tye (but my friends call me "Tye")