This really takes two passes unless you want to load the whole thing into memory. The first pass counts how many words there are corresponding to each letter, and the second pass retrieves the chosen words from the file.
use strict;
use warnings;
my ($words, $file, $handle, %range, %c, %n, $c, @words);
$words = 100;
$file = 'dictionary.txt';
$range{$_} = () for 'a'..'z';
open ($handle, $file);
while (<$handle>) {
$c = substr($_, 0, 1);
next if !exists $range{$c};
$c{$c}++;
}
close ($handle);
for (values %c) {
choose($_);
}
open ($handle, $file);
while (<$handle>) {
$c = substr($_, 0, 1);
next if !exists $c{$c} || $n{$c}++ < $c{$c}[-1];
chomp; push @words, $_;
pop @{$c{$c}};
delete $c{$c} if $#{$c{$c}} == -1;
}
close ($handle);
print join "\n", sort @words;
### Pick random numbers in range
sub choose {
my @c = 0..($_[0]-1);
for (0..($words-1)) {
swap(\@c, $_, rand ($_[0] - $_) + $_);
}
$_[0] = [sort {$b <=> $a} @c[0..($words-1)]];
}
### Swap two array items
sub swap {
my ($r, $x, $y, $t) = @_;
$t = $r->[$x];
$r->[$x] = $r->[$y];
$r->[$y] = $t;
}