sum_of_three_cubes_problem.pl 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. #!/usr/bin/perl
  2. # Author: Daniel "Trizen" Șuteu
  3. # License: GPLv3
  4. # Date: 01 June 2016
  5. # Website: https://github.com/trizen
  6. # An attempt at creating a new algorithm for finding
  7. # integer solutions to the following equation: x^3 + y^3 + z^3 = n
  8. # The concept of the algorithm is to use modular exponentiation,
  9. # based on the relations:
  10. #
  11. # (x^3 mod n) + (y^3 mod n) + (z^3 mod n) = n
  12. # or:
  13. # (x^3 mod n) + (y^3 mod n) + (z^3 mod n) = 2n ; this is more common (?)
  14. #
  15. # This leads to the following conjecture:
  16. # x = a * n + k
  17. # y = b * n + j
  18. #
  19. # for every term x and y in a valid equation: x^3 + y^3 + z^3 = n
  20. # Less generally, we can say:
  21. #
  22. # x = a * n + s1 + psum(P(k))
  23. # y = b * n + s2 + psum(P(k))
  24. # where `s1` and `s2` are the starting points for the corresponding terms
  25. # and `psum(P(k))` is a partial sum of the remainders of n in the form: (k^3 mod n).
  26. # Example:
  27. # 39 = 134476^3 + 117367^3 - 159380^3
  28. #
  29. # 39 = 1 + 13 + 25
  30. #
  31. # P(1) = {15, 6, 18} ; returned by get_pos_steps(39, 1)
  32. # P(13) = {35} ; returned by get_pos_steps(39, 13)
  33. # P(25) = {6, 15, 18} ; returned by get_pos_steps(39, 25)
  34. #
  35. # s1 = 1 ; returned by get_pos_steps(39, 1)
  36. # s2 = 4 ; returned by get_pos_steps(39, 25)
  37. # s3 = 13 ; returned by get_pos_steps(39, 13)
  38. #
  39. # 117367 = a * 39 + s1 + 15
  40. # 134476 = b * 39 + s2 + 0
  41. # -159380 = c * 39 + s3 + 0
  42. #
  43. # then we find:
  44. # a = 3009
  45. # b = 3448
  46. # c = -4087
  47. #
  48. # which results to:
  49. # 117367 = 3009 * 39 + 16
  50. # 134476 = 3448 * 39 + 4
  51. # -159380 = -4087 * 39 + 13
  52. #
  53. # For n=74:
  54. #
  55. # 2*74 = 68 + 29 + 51
  56. #
  57. # P(68) = {2, 52, 20}
  58. # P(29) = {18, 24, 32}
  59. # P(51) = {8, 6, 60}
  60. #
  61. # s1 = 6
  62. # s2 = 5
  63. # s3 = 17
  64. #
  65. # x = a * 74 + s1 + (2 + 52)
  66. # y = b * 74 + s2 + (0)
  67. # z = c * 74 + s3 + (18)
  68. #
  69. # x = a * 74 + 60
  70. # y = b * 74 + 5
  71. # z = c * 74 + 35
  72. #
  73. # a = 894997732304
  74. # b = 3830406833753
  75. # c = -3846625575080
  76. # We can also easily observe that any valid solution satisfies:
  77. #
  78. # is_cube(x^3 + y^3 - n) or
  79. # is_cube(x^3 - y^3 - n)
  80. #
  81. # Currently, in this code, we show how to calculate the steps
  82. # of a given term and how to collect and filter potential valid solutions.
  83. # To actually find a solution, more work is required...
  84. # Inspired by:
  85. # https://www.youtube.com/watch?v=wymmCdLdPvM
  86. # See also:
  87. # https://mathoverflow.net/questions/138886/which-integers-can-be-expressed-as-a-sum-of-three-cubes-in-infinitely-many-ways
  88. use 5.014;
  89. use strict;
  90. use warnings;
  91. #use integer;
  92. #use Math::AnyNum qw(:overload);
  93. use ntheory qw(powmod is_power);
  94. use List::Util qw(pairmap any sum0);
  95. use Data::Dump qw(pp);
  96. # (a^3 % 33) + (b^3 % 33) + (c^3 % 33) = 66
  97. sub get_pos_steps {
  98. my ($n, $k) = @_;
  99. my @steps;
  100. foreach my $i (1 .. 2 * $n) {
  101. if (powmod($i, 3, $n) == $k) {
  102. push @steps, $i;
  103. }
  104. }
  105. ($steps[0], [map { $steps[$_] - $steps[$_ - 1] } 1 .. $#steps]);
  106. }
  107. sub get_neg_steps {
  108. my ($n, $k) = @_;
  109. my @steps;
  110. foreach my $i (1 .. 2 * $n) {
  111. if (powmod(-$i, 3, $n) == $k) {
  112. push @steps, -$i;
  113. }
  114. }
  115. ($steps[0], [map { $steps[$_] - $steps[$_ - 1] } 1 .. $#steps]);
  116. }
  117. sub get_partitions {
  118. my ($n) = @_;
  119. my @p;
  120. my %seen;
  121. foreach my $i (1 .. $n) {
  122. foreach my $j ($i .. $n - $i) {
  123. foreach my $k ($j .. $n - $j - $i) {
  124. if ($i + $j + $k == $n) {
  125. my $v = join(' ', sort { $a <=> $b } ($i, $j, $k));
  126. next if (exists $seen{$v});
  127. $seen{$v} = 1;
  128. push @p, [$i, $j, $k];
  129. }
  130. }
  131. }
  132. }
  133. return @p;
  134. }
  135. #use Math::AnyNum qw(:overload);
  136. #~ my $n = 33;
  137. #~ my $x = 0;
  138. #~ my $y = 0;
  139. #~ my $z = 0;
  140. #~ my $n = 42;
  141. #~ my $x = 0;
  142. #~ my $y = 0;
  143. #~ my $z = 0;
  144. #~ my $n = 74;
  145. #~ my $x = 66229832190556;
  146. #~ my $y = 283450105697727;
  147. #~ my $z = -284650292555885;
  148. # First solution for n=33 was found by Andrew Booker
  149. # See also:
  150. # https://people.maths.bris.ac.uk/~maarb/papers/cubesv1.pdf
  151. # https://www.bradyharanblog.com/blog/33-and-the-sum-of-three-cubes
  152. my $n = 33;
  153. my $x = 8866128975287528;
  154. my $y = -8778405442862239;
  155. my $z = -2736111468807040;
  156. say powmod($x, 3, 33) + powmod($y, 3, 33) + powmod($z, 3, 33);
  157. #~ my $n = 30;
  158. #~ my $x = 2_220_422_932;
  159. #~ my $y = -2_218_888_517;
  160. #~ my $z = -283_059_965;
  161. #~ my $n = 52;
  162. #~ my $x = -61922712865;
  163. #~ my $y = 23961292454;
  164. #~ my $z = 60702901317;
  165. #~ my $n = 75;
  166. #~ my $x = -435203231;
  167. #~ my $y = 435203083;
  168. #~ my $z = 4381159;
  169. #~ my $n = 75;
  170. #~ my $x = 2_576_191_140_760;
  171. #~ my $y = 1_217_343_443_218;
  172. #~ my $z = -2_663_786_047_493;
  173. #~ my $n = 75;
  174. #~ my $x = 59_897_299_698_355;
  175. #~ my $y = -47_258_398_396_091;
  176. #~ my $z = -47_819_328_945_509;
  177. #~ my $n = 87;
  178. #~ my $x = 4271;
  179. #~ my $y =-4126;
  180. #~ my $z = -1972;
  181. #~ my $n = 39;
  182. #~ my $x = -159380;
  183. #~ my $y = 134476;
  184. #~ my $z = 117367;
  185. #$x **= 3;
  186. #$y **= 3;
  187. #$z **= 3;
  188. my @partitions = (get_partitions($n), get_partitions(2 * $n));
  189. my @valid;
  190. F1: foreach my $p (@partitions) {
  191. my @data;
  192. foreach my $k (@{$p}) {
  193. my $ok = 0;
  194. my $data = {k => $k};
  195. {
  196. my ($start, $steps) = get_pos_steps($n, $k);
  197. if (defined($start)) {
  198. $ok ||= 1;
  199. $data->{pos} = {
  200. start => $start,
  201. steps => $steps,
  202. };
  203. }
  204. }
  205. {
  206. my ($start, $steps) = get_neg_steps($n, $k);
  207. if (defined($start)) {
  208. $ok ||= 1;
  209. $data->{neg} = {
  210. start => $start,
  211. steps => $steps,
  212. };
  213. }
  214. }
  215. $ok || next F1;
  216. push @data, $data;
  217. }
  218. push @valid, \@data;
  219. }
  220. #
  221. ## Experimenting with various optimization ideas
  222. #
  223. foreach my $solution (@valid) {
  224. my $count = 0;
  225. foreach my $k ($x, $y, $z) {
  226. ++$count if any {
  227. my $s = $_;
  228. any {
  229. (($k % $n) == sum0(@{$s->{pos}{steps}}[0 .. $_]) + $s->{pos}{start})
  230. or (($k % (-$n)) == sum0(@{$s->{neg}{steps}}[0 .. $_]) + $s->{neg}{start})
  231. }
  232. (-1 .. int(@{$s->{pos}{steps}} / 2) - 1);
  233. #~ any {
  234. #~ ($k % sum(@{$s->{pos}{steps}}[0 .. $_]) == $s->{pos}{start})
  235. #~ or ($k % sum(@{$s->{neg}{steps}}[0 .. $_]) == $s->{neg}{start})
  236. #~ }
  237. #~ int(@{$s->{pos}{steps}} / 2) .. $#{$s->{pos}{steps}};
  238. #(any { $k % $_ == $s->{pos}{start} } @{$s->{pos}{steps}})
  239. #or (any { $k % $_ == $s->{neg}{start} } @{$s->{neg}{steps}})
  240. }
  241. @{$solution};
  242. }
  243. if ($count >= 3) {
  244. pp $solution;
  245. }
  246. }
  247. say scalar @valid;
  248. my %seen;
  249. pp [sort {$a <=> $b} grep{!$seen{$_}++} map { map {$_->{pos}{start}}@{$_} } @valid];