square_root_modulo_n.sf 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #!/usr/bin/ruby
  2. # Daniel "Trizen" Șuteu
  3. # Date: 28 October 2017
  4. # https://github.com/trizen
  5. # Find all the solutions to the congruence equation:
  6. # x^2 = a (mod n)
  7. # Defined for any values of `a` and `n` for which `kronecker(a, n) = 1`.
  8. # When `kronecker(a, n) != 1`, for example:
  9. #
  10. # a = 472
  11. # n = 972
  12. #
  13. # which represents:
  14. # x^2 = 472 (mod 972)
  15. #
  16. # this algorithm is not able to find a solution, although there exist four solutions:
  17. # x = {38, 448, 524, 934}
  18. # Code inspired from:
  19. # https://github.com/Magtheridon96/Square-Root-Modulo-N
  20. func sqrt_mod_n(a, n) is cached {
  21. kronecker(a, n) == 1 || return []
  22. a = (a % n)
  23. if ((n & (n - 1)) == 0) { # n is a power of 2
  24. if (a % 8 == 1) {
  25. var k = n.valuation(2)
  26. k == 1 && return [1]
  27. k == 2 && return [1, 3]
  28. k == 3 && return [1, 3, 5, 7]
  29. if (a == 1) {
  30. return [1, (n>>1) - 1, (n>>1) + 1, n - 1]
  31. }
  32. return gather {
  33. for s in (sqrt_mod_n(a, n >> 1)) {
  34. var i = (((s*s - a) >> (k - 1)) % 2)
  35. var r = (s + (i << (k - 2)))
  36. take(r, n - r)
  37. }
  38. }.uniq.sort
  39. }
  40. return []
  41. }
  42. if (n.is_prime) { # n is a prime
  43. return gather {
  44. var r = a.sqrtmod(n)
  45. take(r, n - r)
  46. }.sort
  47. }
  48. var pe = n.factor_exp # factorize `n` into prime powers
  49. if (pe.len == 1) { # `n` is an odd prime power
  50. var p = pe[0][0]
  51. var k = pe[0][1]
  52. kronecker(a, p) == 1 || return []
  53. var roots = gather {
  54. var r = a.sqrtmod(p)
  55. take(r, n - r)
  56. }
  57. var pk = p
  58. var pi = p*p
  59. (k-1).times {
  60. var x = roots[0]
  61. var y = (invmod(2, pk) * invmod(x, pk))
  62. roots[0] = ((pi + x - y*(x*x - a + pi)) % pi)
  63. roots[1] = (pi - roots[0])
  64. pk *= p
  65. pi *= p
  66. }
  67. return roots.sort
  68. }
  69. var solutions = []
  70. for p,e in (pe) {
  71. var m = p**e
  72. var r = sqrt_mod_n(a, m)
  73. solutions.append(r.map {|r0| [r0, m] })
  74. }
  75. gather {
  76. solutions.cartesian {|*a|
  77. take(Math.chinese(a...))
  78. }
  79. }.uniq.sort
  80. }
  81. for n in (1..1000) {
  82. var a = irand(2, n)
  83. var solutions = sqrt_mod_n(a, n) || next
  84. say "x^2 = #{a} (mod #{n}); x = {#{solutions.join(', ')}}"
  85. }
  86. __END__
  87. x^2 = 455 (mod 838); x = {413, 425}
  88. x^2 = 425 (mod 842); x = {419, 423}
  89. x^2 = 97 (mod 849); x = {83, 200, 649, 766}
  90. x^2 = 202 (mod 859); x = {283, 576}
  91. x^2 = 551 (mod 862); x = {219, 643}
  92. x^2 = 742 (mod 869); x = {128, 425, 444, 741}
  93. x^2 = 628 (mod 871); x = {206, 340, 531, 665}
  94. x^2 = 607 (mod 877); x = {153, 724}
  95. x^2 = 126 (mod 887); x = {112, 775}
  96. x^2 = 841 (mod 905); x = {29, 391, 514, 876}
  97. x^2 = 310 (mod 911); x = {76, 835}
  98. x^2 = 83 (mod 919); x = {177, 742}
  99. x^2 = 441 (mod 920); x = {21, 71, 159, 209, 251, 301, 389, 439, 481, 531, 619, 669, 711, 761, 849, 899}
  100. x^2 = 576 (mod 923); x = {24, 379, 544, 899}
  101. x^2 = 99 (mod 929); x = {429, 500}
  102. x^2 = 884 (mod 937); x = {126, 811}
  103. x^2 = 88 (mod 941); x = {111, 830}
  104. x^2 = 875 (mod 949); x = {119, 392, 557, 830}
  105. x^2 = 836 (mod 953); x = {115, 838}
  106. x^2 = 415 (mod 959); x = {276, 409, 550, 683}
  107. x^2 = 450 (mod 961); x = {779, 182}
  108. x^2 = 289 (mod 974); x = {17, 957}
  109. x^2 = 821 (mod 977); x = {306, 671}
  110. x^2 = 439 (mod 983); x = {173, 810}
  111. x^2 = 574 (mod 991); x = {430, 561}
  112. x^2 = 289 (mod 992); x = {17, 79, 417, 479, 513, 575, 913, 975}
  113. x^2 = 464 (mod 995); x = {128, 327, 668, 867}