678 Fermat-like Equations.pl 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 24 September 2019
  4. # https://github.com/trizen
  5. # https://projecteuler.net/problem=678
  6. # Runtime: ~6 minutes.
  7. use 5.020;
  8. use strict;
  9. use warnings;
  10. use experimental qw(signatures);
  11. use ntheory qw(:all);
  12. use List::Util qw(uniq);
  13. # Number of solutions to `n = a^2 + b^2, with 0 < a < b.
  14. # OEIS: https://oeis.org/A025441
  15. sub r2 ($n) {
  16. my $B = 1;
  17. foreach my $p (factor_exp($n)) {
  18. my $r = $p->[0] % 4;
  19. if ($r == 3) {
  20. $p->[1] % 2 == 0 or return 0;
  21. }
  22. if ($r == 1) {
  23. $B *= $p->[1] + 1;
  24. }
  25. }
  26. return ($B >> 1);
  27. }
  28. # Number of solutions to `n = a^3 + b^3, with 0 < a < b.
  29. # OEIS: https://oeis.org/A025468
  30. sub r2_cubes ($n) {
  31. my $count = 0;
  32. foreach my $d (divisors($n)) {
  33. my $l = $d * $d - $n / $d;
  34. ($l % 3 == 0) || next;
  35. my $t = $d * $d - 4 * ($l / 3);
  36. if ($d * $d * $d >= $n and $d * $d * $d <= 4 * $n and $l >= 3 and $t > 0 and is_square($t)) {
  37. ++$count;
  38. }
  39. }
  40. return $count;
  41. }
  42. # Returns true if n can be represented as a sum of two cubes.
  43. sub is_sum_of_two_cubes ($n) {
  44. my $L = rootint($n - 1, 3) + 1;
  45. my $U = rootint(4 * $n, 3);
  46. foreach my $m (divisors($n)) {
  47. if ($L <= $m and $m <= $U) {
  48. my $l = $m * $m - $n / $m;
  49. $l % 3 == 0 or next;
  50. is_square($m * $m - 4 * ($l / 3)) && return 1;
  51. }
  52. }
  53. return;
  54. }
  55. # Count the number of representations as sums of two squares.
  56. sub count_sum_of_squares ($N) {
  57. my $count = 0;
  58. my $root = rootint($N, 3);
  59. say ":: First stage...";
  60. foreach my $k (2 .. $root) {
  61. my $t = $k * $k * $k;
  62. while ($t <= $N) {
  63. $count += r2($t);
  64. $t *= $k;
  65. }
  66. }
  67. say ":: There are $count solutions to `n^k = a^2 + b^2`, with k >= 3.";
  68. return $count;
  69. }
  70. sub generate_other_powers ($N, $k) {
  71. # Generate all numbers of the form n^k with k >= 4 and n^k <= N.
  72. my @nums;
  73. foreach my $n (2 .. rootint($N, $k)) {
  74. my $t = powint($n, $k);
  75. while ($t <= $N) {
  76. push @nums, $t;
  77. $t *= $n;
  78. }
  79. }
  80. return uniq(@nums);
  81. }
  82. # Count the number of representations as sums of two cubes.
  83. sub count_sum_of_cubes ($N) {
  84. my @nums = generate_other_powers($N, 4);
  85. say ":: There are ", scalar(@nums), " numbers of the form n^k <= $N, with k >= 4.";
  86. my $count = 0;
  87. # Count the number of representations as sums of two cubes.
  88. foreach my $n (@nums) {
  89. is_sum_of_two_cubes($n) || next;
  90. foreach my $k (1 .. rootint($n, 3)) {
  91. my $r = $k * $k * $k;
  92. my $t = $n - $r;
  93. last if ($t < $r);
  94. if (is_power($t, 3) and $t > $r) {
  95. my $pow = is_power($n);
  96. $count += divisor_sum($pow, 0) - ($pow % 2 == 0) - 1;
  97. }
  98. }
  99. }
  100. say ":: There are $count solutions to `n^k = a^3 + b^3`, with k >= 4.";
  101. return $count;
  102. }
  103. # Count the number of representations as sums of two cubes (faster solution).
  104. sub count_sum_of_cubes_fast ($N) {
  105. say ":: Second stage...";
  106. my $count = 0;
  107. foreach my $f (4 .. logint($N, 2)) {
  108. foreach my $c (2 .. rootint($N, $f)) {
  109. $count += r2_cubes(powint($c, $f));
  110. }
  111. }
  112. say ":: There are $count solutions to `n^k = a^3 + b^3`, with k >= 4.";
  113. return $count;
  114. }
  115. sub count_other_powers ($N) {
  116. # The first part of this function is very fast, but misses some solutions, like:
  117. # 264^5 + 528^5 = 34848^3
  118. my $count = 0;
  119. my @nums = generate_other_powers($N, 4);
  120. # Count the number of representations as sums of two powers n^k with k >= 4.
  121. foreach my $n (@nums) {
  122. foreach my $p (4 .. logint($n, 2)) {
  123. next if is_power($n, $p); # Fermat's last theorem
  124. foreach my $k (1 .. rootint($n, $p)) {
  125. my $r = powint($k, $p);
  126. my $t = $n - $r;
  127. last if ($t < $r);
  128. if (is_power($t, $p) and $t > $r) {
  129. my $pow = is_power($n);
  130. $count += divisor_sum($pow, 0) - ($pow % 2 == 0) - 1;
  131. }
  132. }
  133. }
  134. }
  135. # Count the missed solutions of the form: n^3 = a^e + b^e, for e >= 4.
  136. foreach my $u (1 .. rootint($N >> 1, 4)) {
  137. foreach my $v ($u + 1 .. $N) {
  138. my $x = $u * $u * $u * $u;
  139. my $y = $v * $v * $v * $v;
  140. last if ($x + $y > $N);
  141. while ($x + $y <= $N) {
  142. my $pow = is_power($x + $y);
  143. $count += 1 if ($pow == 3);
  144. $x *= $u;
  145. $y *= $v;
  146. }
  147. }
  148. }
  149. return $count;
  150. }
  151. # Count the number of representations as sums of powers a^e with e >= 4.
  152. sub count_other_powers_fast ($N) {
  153. say ":: Third stage..."; # most of the time is spent here
  154. my $count = 0;
  155. foreach my $u (1 .. rootint($N >> 1, 4)) {
  156. foreach my $v ($u + 1 .. $N) {
  157. my $x = $u * $u * $u * $u;
  158. my $y = $v * $v * $v * $v;
  159. last if ($x + $y > $N);
  160. while ($x + $y <= $N) {
  161. my $pow = is_power($x + $y);
  162. if ($pow > 2) {
  163. $count += divisor_sum($pow, 0) - ($pow % 2 == 0) - 1;
  164. }
  165. $x *= $u;
  166. $y *= $v;
  167. }
  168. }
  169. }
  170. say ":: There are $count solutions to `n^k = a^e + b^e`, with k >= 3 and e >= 4.";
  171. return $count;
  172. }
  173. sub F ($N) {
  174. my $x = count_sum_of_squares($N);
  175. #my $y = count_sum_of_cubes($N);
  176. my $y = count_sum_of_cubes_fast($N);
  177. #my $z = count_other_powers($N);
  178. my $z = count_other_powers_fast($N);
  179. my $total = $x + $y + $z;
  180. return $total;
  181. }
  182. say F(powint(10, 18));
  183. __END__
  184. # F(10^10) = 3231
  185. # F(10^11) = 7212
  186. # F(10^12) = 16066
  187. # F(10^13) = 35816
  188. # F(10^14) = 80056
  189. # F(10^15) = 178578