sum_of_triangular_numbers_solutions.pl 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 02 March 2018
  4. # https://github.com/trizen
  5. # Find representations for a given number (n) as a sum of three triangular
  6. # numbers, where the index (k) of one triangular number is also given.
  7. # Equivalent with finding solutions to `x` and `y` in the following equation:
  8. #
  9. # n = k*(k+1)/2 + x*(x+1)/2 + y*(y+1)/2
  10. #
  11. # where `n` and `k` are given.
  12. # Example:
  13. # n = 1234
  14. # k = 42
  15. # Solutions:
  16. # 1234 = 42*(42+1)/2 + 3*( 3+1)/2 + 25*(25+1)/2
  17. # 1234 = 42*(42+1)/2 + 10*(10+1)/2 + 23*(23+1)/2
  18. # 1234 = 42*(42+1)/2 + 12*(12+1)/2 + 22*(22+1)/2
  19. # When k=0, `n` will be represented as a sum of two triangular numbers only (if possible):
  20. # 1234 = 17*(17+1)/2 + 46*(46+1)/2
  21. # See also:
  22. # https://projecteuler.net/problem=621
  23. # https://trizenx.blogspot.com/2017/10/representing-integers-as-sum-of-two.html
  24. use 5.020;
  25. use strict;
  26. use warnings;
  27. use experimental qw(signatures);
  28. use ntheory qw(sqrtmod factor_exp chinese is_polygonal forsetproduct);
  29. sub sum_of_two_squares ($n) {
  30. $n == 0 and return [0, 0];
  31. my $prod1 = 1;
  32. my $prod2 = 1;
  33. my @prime_powers;
  34. foreach my $f (factor_exp($n)) {
  35. if ($f->[0] % 4 == 3) { # p = 3 (mod 4)
  36. $f->[1] % 2 == 0 or return; # power must be even
  37. $prod2 *= $f->[0]**($f->[1] >> 1);
  38. }
  39. elsif ($f->[0] == 2) { # p = 2
  40. if ($f->[1] % 2 == 0) { # power is even
  41. $prod2 *= $f->[0]**($f->[1] >> 1);
  42. }
  43. else { # power is odd
  44. $prod1 *= $f->[0];
  45. $prod2 *= $f->[0]**(($f->[1] - 1) >> 1);
  46. push @prime_powers, [$f->[0], 1];
  47. }
  48. }
  49. else { # p = 1 (mod 4)
  50. $prod1 *= $f->[0]**$f->[1];
  51. push @prime_powers, $f;
  52. }
  53. }
  54. $prod1 == 1 and return [$prod2, 0];
  55. $prod1 == 2 and return [$prod2, $prod2];
  56. my %table;
  57. foreach my $f (@prime_powers) {
  58. my $pp = $f->[0]**$f->[1];
  59. my $r = sqrtmod($pp - 1, $pp);
  60. push @{$table{$pp}}, [$r, $pp], [$pp - $r, $pp];
  61. }
  62. my @square_roots;
  63. forsetproduct {
  64. push @square_roots, chinese(@_);
  65. } values %table;
  66. my @solutions;
  67. foreach my $r (@square_roots) {
  68. my $s = $r;
  69. my $q = $prod1;
  70. while ($s * $s > $prod1) {
  71. ($s, $q) = ($q % $s, $s);
  72. }
  73. push @solutions, [$prod2 * $s, $prod2 * ($q % $s)];
  74. }
  75. foreach my $f (@prime_powers) {
  76. for (my $i = $f->[1] % 2 ; $i < $f->[1] ; $i += 2) {
  77. my $sq = $f->[0]**(($f->[1] - $i) >> 1);
  78. my $pp = $f->[0]**($f->[1] - $i);
  79. push @solutions, map {
  80. [map { $sq * $prod2 * $_ } @$_]
  81. } __SUB__->($prod1 / $pp);
  82. }
  83. }
  84. return sort { $a->[0] <=> $b->[0] } do {
  85. my %seen;
  86. grep { !$seen{$_->[0]}++ } map {
  87. [sort { $a <=> $b } @$_]
  88. } @solutions;
  89. };
  90. }
  91. sub sum_of_triangles ($n, $k) {
  92. my $z = ($n - $k * ($k + 1) / 2) * 8 + 1;
  93. return if $z <= 0;
  94. my @result;
  95. my @solutions = sum_of_two_squares($z + 1);
  96. foreach my $s (@solutions) {
  97. is_polygonal(($s->[0]**2 - 1)/8, 3, \my $x);
  98. is_polygonal(($s->[1]**2 - 1)/8, 3, \my $y);
  99. push @result, [$x, $y];
  100. }
  101. return @result;
  102. }
  103. my $n = 1234;
  104. my $k = 42;
  105. my @solutions = sum_of_triangles($n, $k);
  106. foreach my $s (@solutions) {
  107. say "$n = $k*($k+1)/2 + $s->[0]*($s->[0]+1)/2 + $s->[1]*($s->[1]+1)/2";
  108. }