sum_of_two_squares_solutions.sf 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #!/usr/bin/ruby
  2. # Daniel "Trizen" Șuteu
  3. # Date: 26 October 2017
  4. # https://github.com/trizen
  5. # A recursive algorithm for finding all the non-negative integer solutions to the equation:
  6. # a^2 + b^2 = n
  7. # for any given positive integer `n` for which such a solution exists.
  8. # Example:
  9. # 99025 = 41^2 + 312^2 = 48^2 + 311^2 = 95^2 + 300^2 = 104^2 + 297^2 = 183^2 + 256^2 = 220^2 + 225^2
  10. # Blog post:
  11. # https://trizenx.blogspot.com/2017/10/representing-integers-as-sum-of-two.html
  12. # This algorithm is efficient when the factorization of `n` can be computed.
  13. # See also:
  14. # https://oeis.org/A001481
  15. func sum_of_two_squares_solutions(n) is cached {
  16. n < 0 && return []
  17. n == 0 && return [[0, 0]]
  18. var sqrtmod_cache = Hash()
  19. func find_solutions(n_factor_exp) {
  20. var prod1 = 1
  21. var prod2 = 1
  22. var prod1_factor_exp = []
  23. for p,e in (n_factor_exp) {
  24. if (p % 4 == 3) { # p = 3 (mod 4)
  25. e.is_even || return [] # power must be even
  26. prod2 *= p**(e >> 1)
  27. }
  28. elsif (p == 2) { # p = 2
  29. if (e.is_even) { # power is even
  30. prod2 *= p**(e >> 1)
  31. }
  32. else { # power is odd
  33. prod1 *= p
  34. prod2 *= p**((e - 1) >> 1)
  35. prod1_factor_exp << [p, 1]
  36. }
  37. }
  38. else { # p = 1 (mod 4)
  39. prod1 *= p**e
  40. prod1_factor_exp << [p, e]
  41. }
  42. }
  43. prod1 == 1 && return [[prod2, 0]]
  44. prod1 == 2 && return [[prod2, prod2]]
  45. # All the solutions to the congruence: x^2 = -1 (mod prod1)
  46. var square_roots = gather {
  47. gather {
  48. for p,e in (prod1_factor_exp) {
  49. var pp = p**e
  50. var r = (sqrtmod_cache{pp} \\= sqrtmod(-1, pp))
  51. take([[r, pp], [pp - r, pp]])
  52. }
  53. }.cartesian { |*a|
  54. take(Math.chinese(a...))
  55. }
  56. }
  57. var solutions = []
  58. for r in (square_roots) {
  59. var s = r
  60. var q = prod1
  61. while (s*s > prod1) {
  62. (s, q) = (q % s, s)
  63. }
  64. solutions << [prod2 * s, prod2 * (q % s)]
  65. }
  66. for p,e in (prod1_factor_exp) {
  67. for (var i = e%2 ; i < e ; i += 2) {
  68. var factor_exp = []
  69. for q,e in (prod1_factor_exp) {
  70. if (q == p) {
  71. factor_exp << [p, i] if (i > 0)
  72. }
  73. else {
  74. factor_exp << [q, e]
  75. }
  76. }
  77. var sq = (prod2 * p**((e-i)>>1))
  78. solutions << __FUNC__(factor_exp).map {|pair|
  79. pair.map {|r| r * sq }
  80. }...
  81. }
  82. }
  83. return solutions
  84. }
  85. var solutions = find_solutions(n.factor_exp)
  86. solutions.map {|pair| pair.sort } \
  87. .uniq_by {|pair| pair[0] } \
  88. .sort_by {|pair| pair[0] }
  89. }
  90. 50.times {
  91. var n = 1e10.irand
  92. var solutions = sum_of_two_squares_solutions(n) || next
  93. say %Q(#{n} = #{solutions.map {|a| "#{a[0]}^2 + #{a[1]}^2" }.join(' = ') })
  94. }
  95. assert_eq(sum_of_two_squares_solutions(2025), [[0, 45], [27, 36]])
  96. assert_eq(sum_of_two_squares_solutions(164025), [[0, 405], [243, 324]])
  97. assert_eq(sum_of_two_squares_solutions(99025), [[41, 312], [48, 311], [95, 300], [104, 297], [183, 256], [220, 225]])
  98. assert_eq(
  99. -10 .. 160 -> grep { sum_of_two_squares_solutions(_).len > 0 },
  100. %n[0, 1, 2, 4, 5, 8, 9, 10, 13, 16, 17, 18, 20, 25, 26, 29, 32, 34, 36, 37, 40, 41, 45, 49, 50, 52, 53, 58, 61, 64, 65, 68, 72, 73, 74, 80, 81, 82, 85, 89, 90, 97, 98, 100, 101, 104, 106, 109, 113, 116, 117, 121, 122, 125, 128, 130, 136, 137, 144, 145, 146, 148, 149, 153, 157, 160]
  101. )
  102. assert_eq(
  103. sum_of_two_squares_solutions(11392163240756069707031250),
  104. [[39309472125, 3374998963875], [216763660575, 3368260197225], [477329304375, 3341305130625], [729359177085, 3295481517405], [735019741071, 3294223614297], [907262616645, 3251005657515], [982736803125, 3228992353125], [1151205969375, 3172835964375], [1224793301193, 3145162095999], [1393801568775, 3074000720175], [1622919634875, 2959441687125], [1847545189875, 2824666354125], [1993551800625, 2723584854375], [2056446956025, 2676413487825], [2194367046795, 2564549961435], [2198769707673, 2560776252111], [2386646521875, 2386646521875]]
  105. )