in reply to why does this array take up so much memory?

No one has mentioned yet that your two-dimensional array is really an array of 4001 references to one-dimensional arrays of size 4001 each. You could save space with a flattened representation like this:
$matrix[4000 + 4000*4000] = 1.0; foreach $x (0..4000) { foreach $y (0..4000) { $matrix[$x + 4000*$y] = 1.0; } }
The other thing I did was to pre-set the highest element first. I think you should do that anyway, even if you stick to the two-dimensional style. That prevents perl from doing lots of re-allocations behind the scenes, and pumping up the padding each time.