Here it is, fast and accurate. The comments should describe the algorithm pretty well.
use strict;
use warnings;
use List::Util qw[ reduce ];
my $num; # Used in sub, but could be passed if you wanted
sub closest {
# Args are target and factor-list
my ($target, @factors) = @_;
# Take the biggest factor
my $top_factor = pop @factors;
# Find multiple of that factor closest to (and above) target
my $guess = int($target) - $target % $top_factor + $top_factor;
# Oscillate around the target, looking at multiples of top_factor
# until you get one that divides the product
for (my $i=$top_factor; $num % $guess; $i += $top_factor) {
$guess += ( $target <=> $guess ) * $i;
}
# Check the complementary factor
my $complement = $num / $guess;
abs($target - $complement) < abs($target - $guess)
? $complement
: $guess
;
}
my @pfs = (2,2,2,3,3,3,5,5,5,17,19,19,19,19);
# Compute product of factors
our ($a, $b);
$num = reduce { $a * $b } @pfs;
my $root = sqrt $num;
print "N=$num, R=$root\n";
print closest($root, 1, @pfs), "\n";
Caution: Contents may have been coded under pressure.