高度可约三角形数
作者:polettix
https://projecteuler.net/problem=12
三角形数序列是通过将自然数相加生成的。所以第 7 个三角形数是
1 + 2 + 3 + 4 + 5 + 6 + 7 = 28.
前十项是
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...
让我们列出前七个三角形数的因数
1: 1 3: 1,3 6: 1,2,3,6
10: 1,2,5,10 15: 1,3,5,15 21: 1,3,7,21 28: 1,2,4,7,14,28
我们可以看到,28 是第一个拥有超过五个因数的三角形数。
第一个拥有超过五百个因数的三角形数是多少?
源代码: prob012-polettix.pl
use v6;
# Minimum number of factors, defaults to challenge's request
my $t = @*ARGS.shift || 500;
# It's easy to tell triangle numbers: they are all of the form
# ($n * ($n + 1)) / 2. Hence, the factors are those of
# $n and ($n + 1), less one "2" because of the division.
#
# We'll iterate through $n, from 2 to... wherever it is needed.
#
# We use a factors_progressively_asked() function that is supposed
# to return factors but requests you to ask factors for numbers
# progressively (i.e. start from 2, then 3, 4, 5, ...). This allows
# an optimisation and is perfect in our case, because we need the
# factors for increasing values of $n.
my $max = 1;
my $n = 2;
my %previous_factors = factors_progressively_asked(2);
# Due to lazyness, we'll talk about ($n - 1) and $n instead of $n and
# ($n + 1).
while ($max <= $t) {
$n++;
my %this_factors = factors_progressively_asked($n);
# Now, %previous_factors has the factors for ($n - 1), and
# %this_factors has the factors for $n. We mix them all into
# %previous_factors and then eliminate one "2" to cope with the
# division.
%previous_factors{$_} += %this_factors{$_} for %this_factors.keys;
%previous_factors{2}--; # divide by 2
# Now, we count the number of different factors
my $p = 1;
$p *= $_ for %previous_factors.values.map({ $_ + 1 });
# a little feedback
say "$n $p ($max)" unless $n % 100;
# prepare for next iteration: update $max and save %this_factors
$max = $p if $max < $p;
%previous_factors = %this_factors;
}
say 'result: ', ($n * ($n - 1)) / 2;
# In rakudo, the "is copy" is needed because of a known bug
# as of Aug 24th, 2009. Otherwise, the @primes.push($x) gets
# "confused".
sub factors_progressively_asked ($x is copy) {
state @primes;
state %factors_for;
my $result;
if (%factors_for{$x}:exists) {
$result = %factors_for{$x};
}
else {
for @primes -> $p {
if ($x % $p) == 0 { # Bingo! A divisor!
# Now, $p is prime, and $x / $p is surely into %factors_for
# because we're calling this function progressively, so
# we're done.
$result = [ $p, %factors_for{$x / $p}.list ];
}
}
if (! $result) { # Ok, there's a new prime in town
@primes.push($x);
$result = [ $x ];
}
%factors_for{$x} = $result;
}
# Return as a hash of (factor, occurrences) pairs
my %factors;
%factors{$_}++ for $result.list;
return %factors;
}
Perl 6 示例