farey_factorization_method.sf 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. #!/usr/bin/ruby
  2. # Daniel "Trizen" Șuteu
  3. # Date: 20 April 2019
  4. # https://github.com/trizen
  5. # Continued-fraction factorization method, combined with the mediant inequality to approximate sqrt(n).
  6. # See also:
  7. # https://en.wikipedia.org/wiki/Continued_fraction_factorization
  8. # https://en.wikipedia.org/wiki/Mediant_(mathematics)#Properties
  9. func gauss_elimination(A, n) {
  10. var m = A.end
  11. var I = (m+1).of {|i| 1 << i }
  12. var nrow = -1
  13. for col in (0 .. min(m, n)) {
  14. var npivot = -1
  15. for row in (nrow+1 .. m) {
  16. if (A[row].bit(col)) {
  17. npivot = row
  18. nrow++
  19. break
  20. }
  21. }
  22. next if (npivot < 0)
  23. if (npivot != nrow) {
  24. A.swap(npivot, nrow)
  25. I.swap(npivot, nrow)
  26. }
  27. for row in (nrow+1 .. m) {
  28. if (A[row].bit(col)) {
  29. A[row] ^= A[nrow]
  30. I[row] ^= I[nrow]
  31. }
  32. }
  33. }
  34. return I
  35. }
  36. func check_factor (n, g, factors) {
  37. while (g `divides` n) {
  38. n /= g;
  39. factors << g
  40. if (is_prime(n)) {
  41. factors << n
  42. return 1
  43. }
  44. }
  45. return n
  46. }
  47. func next_multiplier (k) {
  48. k += 2
  49. while (!k.is_squarefree) {
  50. ++k
  51. }
  52. return k
  53. }
  54. func farey_factorization (n, multiplier=1) {
  55. # Check for primes and negative numbers
  56. return [] if (n <= 1)
  57. return [n] if n.is_prime
  58. # Check for perfect powers
  59. if (n.is_power) {
  60. var root = n.perfect_root
  61. var power = n.perfect_power
  62. var factors = __FUNC__(root)
  63. return (factors * power -> sort)
  64. }
  65. var resolve_factor = {|a,b|
  66. var g = gcd(a - b.isqrt, n)
  67. if ((g > 1) && (g < n)) {
  68. return (
  69. __FUNC__(g) +
  70. __FUNC__(n/g) -> sort
  71. )
  72. }
  73. }
  74. var N = n*multiplier
  75. var x = N.isqrt
  76. var a1 = x
  77. var b1 = 1
  78. var a2 = x+1
  79. var b2 = 1
  80. var y = x
  81. var z = 1
  82. var w = x+x
  83. var r = w
  84. var (e1, e2) = (1, 0)
  85. var (f1, f2) = (0, 1)
  86. #var B = int(exp(sqrt(log(n) * log(log(n))) / 2)) # B-smooth limit
  87. var nf = int(exp(sqrt(log(n) * log(log(n))))**(sqrt(2) / 4))
  88. #var factor_base = B.primes.grep {|p| (p <= 2) || (kronecker(N, p) >= 0) }
  89. var factor_base = (1..Inf -> lazy.map { .prime }.grep {|p| (p <= 2) || (kronecker(N, p) >= 0) }.first(nf))
  90. var factor_prod = factor_base.prod
  91. var factor_index = Hash(factor_base ~Z ^factor_base -> flat...)
  92. var L = factor_base.len+1
  93. var Q = []
  94. var A = []
  95. func exponent_signature(factors) {
  96. var sig = 0
  97. for p,e in factors {
  98. sig.setbit!(factor_index{p}) if e.is_odd
  99. }
  100. return sig
  101. }
  102. while (A.len < L) {
  103. # Continued fraction expansion of sqrt(N)
  104. y = (r*z - y)
  105. z = ((N - y*y) / z)
  106. r = round((x + y) / z)
  107. var a = ((x*f2 + e2) % n)
  108. var b = (a*a % n)
  109. var c = (b > w ? n-b : b)
  110. resolve_factor(a, c) if c.is_square
  111. if (c.is_smooth_over_prod(factor_prod)) {
  112. var c_factors = c.factor_exp
  113. if (c_factors) {
  114. A << exponent_signature(c_factors)
  115. Q << [a, c]
  116. }
  117. }
  118. (f1, f2) = (f2, (r*f2 + f1) % n)
  119. (e1, e2) = (e2, (r*e2 + e1) % n)
  120. if (z == 1) {
  121. return __FUNC__(n, next_multiplier(multiplier))
  122. }
  123. # Mediant inequality
  124. var a3 = a1+a2
  125. var b3 = b1+b2
  126. if (a3*a3 < N*b3*b3) {
  127. (a1, b1) = (a3, b3)
  128. }
  129. else {
  130. (a2, b2) = (a3, b3)
  131. }
  132. # There is a good chance that `numerator(m)^2 (mod n)` is B-smooth
  133. var t = (a3 % n)
  134. var v = (t*t % n)
  135. v = (n-v) if (n-v < v)
  136. # If `v` is a square, then `gcd(t - sqrt(v), n)` is divisor of `n`.
  137. resolve_factor(t, v) if v.is_square
  138. if (v.is_smooth_over_prod(factor_prod)) {
  139. var v_factors = v.factor_exp
  140. if (v_factors) {
  141. A << exponent_signature(v_factors)
  142. Q << [t, v]
  143. }
  144. }
  145. }
  146. if (A.len < L) {
  147. A += (L - A.end + 1 -> of(0))
  148. }
  149. var I = gauss_elimination(A, L-1)
  150. var LR = (A.end - A.rindex_by { !.is_zero })
  151. var rem = n
  152. var factors = []
  153. for solution in (I.last(LR)) {
  154. var X = 1
  155. var Y = 1
  156. var done = false
  157. for i in (^Q) {
  158. if (solution.bit(i)) {
  159. X *= Q[i][0] %= n #=
  160. Y *= Q[i][1]
  161. var g = gcd(X - Y.isqrt, rem)
  162. if ((g > 1) && (g < rem)) {
  163. rem = check_factor(rem, g, factors)
  164. if (rem == 1) { done = true; break }
  165. }
  166. }
  167. }
  168. break if done
  169. }
  170. var final_factors = []
  171. for f in (factors) {
  172. if (f.is_prime) {
  173. final_factors << f
  174. }
  175. else {
  176. final_factors << __FUNC__(f)...
  177. }
  178. }
  179. if (rem != 1) {
  180. if (rem != n) {
  181. final_factors << __FUNC__(rem)...
  182. }
  183. else {
  184. final_factors << rem
  185. }
  186. }
  187. # Failed to factorize n (try again with a multiplier)
  188. if (rem == n) {
  189. return __FUNC__(n, next_multiplier(multiplier))
  190. }
  191. # Return all prime factors of n
  192. final_factors.sort
  193. }
  194. var composites = (ARGV ? ARGV.map{.to_i} : {|k| irand(2, 1<<k) }.map(2..40) )
  195. for n in (composites) {
  196. var factors = farey_factorization(n)
  197. assert_eq(factors.prod, n)
  198. say "#{n} = #{factors.map {|f| f.is_prime ? f : \"#{f} (composite)\"}.join(' * ')}"
  199. }