問題
解答
ある数が3の倍数のとき、その各桁を足し合わせた数もまた3の倍数であり、その逆もいえます:
n = am-1am-2...a0
= am-1×10m-1 + am-2×10m-2 + ... + a0×100
= am-1×(1 + 999...9) + am-2×(1 + 99...9) + ... + a1×(1 + 9) + a0×1
= (am-1 + am-2 + ... + a0) + am-1×(999...9) + am-2×(99...9) + ... + a1×9
∴ nが3の倍数のとき、am-1 + am-2 + ... + a0は3の倍数
つまり1 + 2 + ... + mが3の倍数であったとすると、m桁のPandigital数の中に解は存在し得ないことが分かります。m = 8, 9のときがこの場合に該当するので、探索範囲を大きく減らせます。
Pandigital数を作る際に数値を重複なく選ぶため、Set::ObjectというCPANモジュールを使って簡単な集合演算を行っています。
#!/usr/bin/env perl;
use strict;
use warnings;
use feature qw/say state/;
use List::Util qw/sum/;
use List::MoreUtils qw/none/;
use Set::Object qw/set/;
sub is_prime($) {
state %memos;
my $n = shift;
return 0 if $n < 2;
return 1 if $n == 2;
return 1 if $n == 3;
return $memos{$n} if exists $memos{$n};
$memos{$n} = none { $n % $_ == 0 } 2 .. sqrt $n;
}
my $prime_tails = set(1, 3, 7, 9);
sub max_pandigital_prime($;$$);
sub max_pandigital_prime($;$$) {
my ($ndigits, $num, $rest_digits) = @_;
$num //= '';
$rest_digits //= set(1 .. $ndigits);
return is_prime $num ? $num : undef if $rest_digits->is_null;
return undef if ($rest_digits * $prime_tails)->is_null;
for my $n (sort { $b <=> $a } $rest_digits->members) {
$rest_digits->remove($n);
my $answer = max_pandigital_prime($ndigits - 1, $num . $n, $rest_digits);
return $answer if defined $answer;
$rest_digits->insert($n);
}
return undef;
}
my $answer;
for my $ndigits (reverse 1 .. 9) {
next if sum(1 .. $ndigits) % 3 == 0;
last if defined ($answer = max_pandigital_prime($ndigits));
}
say $answer;
コメント
コメントを投稿