425 Prime connection.pl 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 20 August 2017
  4. # https://github.com/trizen
  5. # https://projecteuler.net/problem=425
  6. # Runtime: 8 min, 23 sec
  7. use 5.010;
  8. use strict;
  9. use ntheory qw(primes);
  10. use List::Util qw(shuffle);
  11. my $limit = 10**7;
  12. # Array of primes up to limit
  13. my @primes = @{primes($limit)};
  14. # Table of primes up to limit
  15. my %h;
  16. @h{@primes} = ();
  17. my %seen;
  18. my %lens;
  19. # Group by length
  20. foreach my $p (@primes) {
  21. push @{$lens{length($p)}}, $p;
  22. }
  23. # Group by difference of one digit
  24. my %table;
  25. foreach my $len (sort { $b <=> $a } keys %lens) {
  26. foreach my $p (@{$lens{$len}}) {
  27. foreach my $i (0 .. $len - 1) {
  28. my $t = $p;
  29. substr($t, $i, 1, 'x');
  30. push @{$table{$t}}, $p;
  31. }
  32. }
  33. }
  34. sub is_twos_relative {
  35. my ($n, $m) = @_;
  36. # If we reach `n=2`, then `m` is a 2's relative
  37. return 1 if $n == 2;
  38. # If `n` was already seen, then `m` is not a 2's relative
  39. if (exists $seen{$n}) {
  40. return 0;
  41. }
  42. undef $seen{$n}; # mark `n` as seen
  43. # If removing one digit from the left is still a prime
  44. if (exists($h{substr($n, 1)})) {
  45. # Is this prime connected to `m` via 2? Great! We're done!
  46. if (is_twos_relative(substr($n, 1), $m)) {
  47. return 1;
  48. }
  49. }
  50. # Create the keys that differ in exactly one digit from n
  51. my @comb;
  52. foreach my $i (0 .. length($n) - 1) {
  53. my $t = $n;
  54. substr($t, $i, 1, 'x');
  55. push @comb, $t;
  56. }
  57. # For each key, iterate over the corresponding primes
  58. foreach my $c (shuffle(@comb)) {
  59. foreach my $p (@{$table{$c}}) {
  60. # We're not interested in p > m
  61. if ($p > $m) {
  62. last;
  63. }
  64. # Is `p` connected to `m` via 2? Great! We're done!
  65. if (is_twos_relative($p, $m)) {
  66. return 1;
  67. }
  68. }
  69. }
  70. return 0;
  71. }
  72. # Sum the primes which are not 2's relatives.
  73. my $sum = 0;
  74. foreach my $p (@primes) {
  75. if (not is_twos_relative($p, $p)) {
  76. $sum += $p;
  77. }
  78. undef %seen; # reset the `seen` table
  79. }
  80. say $sum;