#!/usr/bin/env perl
use warnings;
use strict;
use List::Util qw(shuffle);

# freq table extracted from Ubuntu's /usr/share/dict/words wordlist
my %freq = (
    a => 64439, 
    b => 15526, 
    c => 31872, 
    d => 28531, 
    e => 88833, 
    f => 10675, 
    g => 22712, 
    h => 19320, 
    i => 66986, 
    j => 1948, 
    k => 8409, 
    l => 41107, 
    m => 22508, 
    n => 57144, 
    o => 48944, 
    p => 22274, 
    q => 1524, 
    r => 57347, 
    s => 90113, 
    t => 53006, 
    u => 26118, 
    v => 7989, 
    w => 7530, 
    x => 2124, 
    y => 12652, 
    z => 3281,
);

my @letters = sort keys %freq;

my $sum = 0;
my %running_sum;
for(@letters) {
    $running_sum{$_} = $sum;
    $sum += $freq{$_};
}

my $curmax = 1;
my $curletter = $#letters;
my $i = 100; # the number of letters we want to generate
my @result;
while ($i > 0) {
    # curmax generates a uniformly distributed decreasing random number in [0,1)
    # see http://r...content-available-to-author-only...u.edu/cgi/viewcontent.cgi?article=3483&context=compsci
    $curmax = $curmax * (1-rand())**(1. / $i);

    # scale the random number to [0,$sum)
    my $num = int ($curmax * $sum);

    # find the range that contains $num
    while ($num < $running_sum{$letters[$curletter]}) {
        $curletter--;
    }

    push(@result, $letters[$curletter]);

    $i--;
}

# since $result is sorted, you may want to use shuffle it first
print "", join('', shuffle(@result));
