continued_fraction_factorization_method.sf 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #!/usr/bin/ruby
  2. # Daniel "Trizen" Șuteu
  3. # Date: 25 October 2018
  4. # https://github.com/trizen
  5. # Simple implementation of the continued fraction factorization method (CFRAC),
  6. # combined with modular arithmetic (variation of the Brillhart-Morrison algorithm).
  7. # See also:
  8. # https://en.wikipedia.org/wiki/Pell%27s_equation
  9. # https://en.wikipedia.org/wiki/Continued_fraction_factorization
  10. # https://trizenx.blogspot.com/2018/10/continued-fraction-factorization-method.html
  11. # "Gaussian elimination" algorithm from:
  12. # https://github.com/martani/Quadratic-Sieve
  13. define(
  14. B_SMOOTH_METHOD = false, # true to use the B-smooth formula for the factor base
  15. ROUND_DIVISION = true, # true to use round division instead of floor division
  16. )
  17. func gaussian_elimination(A, n) {
  18. var m = A.end
  19. var I = (m+1).of {|i| 1 << i }
  20. var nrow = -1
  21. for col in (0 .. min(m, n)) {
  22. var npivot = -1
  23. for row in (nrow+1 .. m) {
  24. if (A[row].bit(col)) {
  25. npivot = row
  26. nrow++
  27. break
  28. }
  29. }
  30. next if (npivot < 0)
  31. if (npivot != nrow) {
  32. A.swap(npivot, nrow)
  33. I.swap(npivot, nrow)
  34. }
  35. for row in (nrow+1 .. m) {
  36. if (A[row].bit(col)) {
  37. A[row] ^= A[nrow]
  38. I[row] ^= I[nrow]
  39. }
  40. }
  41. }
  42. return I
  43. }
  44. func check_factor (n, g, factors) {
  45. while (g `divides` n) {
  46. n /= g;
  47. factors << g
  48. if (is_prime(n)) {
  49. factors << n
  50. return 1
  51. }
  52. }
  53. return n
  54. }
  55. func next_multiplier (k) {
  56. k += 2
  57. while (!k.is_squarefree) {
  58. ++k
  59. }
  60. return k
  61. }
  62. func cffmm (n, multiplier=1) {
  63. # Check for primes and negative numbers
  64. return [] if (n <= 1)
  65. return [n] if n.is_prime
  66. # Check for perfect powers
  67. if (n.is_power) {
  68. var root = n.perfect_root
  69. var power = n.perfect_power
  70. var factors = __FUNC__(root)
  71. return (factors * power -> sort)
  72. }
  73. var resolve_factor = {|a,b|
  74. var g = gcd(a - b.isqrt, n)
  75. if ((g > 1) && (g < n)) {
  76. return (
  77. __FUNC__(g) +
  78. __FUNC__(n/g) -> sort
  79. )
  80. }
  81. }
  82. var N = n*multiplier
  83. var x = N.isqrt
  84. var y = x
  85. var z = 1
  86. var w = x+x
  87. var r = w
  88. var B = int(exp(sqrt(log(n) * log(log(n))) / 2)) # B-smooth limit
  89. var nf = int(exp(sqrt(log(n) * log(log(n))))**(sqrt(2) / 4) / 1.25)
  90. var factor_base = if (B_SMOOTH_METHOD) {
  91. B.primes.grep {|p| (p <= 2) || (kronecker(N, p) >= 0) }
  92. }
  93. else {
  94. 1..Inf -> lazy.map { .prime }.grep {|p| (p <= 2) || (kronecker(N, p) >= 0) }.first(nf)
  95. }
  96. var factor_prod = factor_base.prod
  97. var factor_index = Hash(factor_base ~Z ^factor_base -> flat...)
  98. var L = factor_base.len+1
  99. var Q = []
  100. var A = []
  101. func exponent_signature(factors) {
  102. var sig = 0
  103. for p,e in factors {
  104. sig.setbit!(factor_index{p}) if e.is_odd
  105. }
  106. return sig
  107. }
  108. var (f1, f2) = (1, x)
  109. while (A.len < L) {
  110. y = (r*z - y)
  111. z = idiv(N - y*y, z) # exact division
  112. r = (ROUND_DIVISION ? idiv_round(x+y, z) : idiv(x+y, z))
  113. (f1, f2) = (f2, addmod(mulmod(r, f2, n), f1, n))
  114. if (z.is_square) {
  115. resolve_factor(f1, z)
  116. }
  117. if (z.abs.is_smooth_over_prod(factor_prod)) {
  118. var c_factors = z.abs.factor_exp
  119. if (c_factors) {
  120. A << exponent_signature(c_factors)
  121. Q << [f1, z.abs]
  122. }
  123. }
  124. if (z.abs == 1) {
  125. return __FUNC__(n, next_multiplier(multiplier))
  126. }
  127. }
  128. if (A.len < L) {
  129. A += (L - A.end + 1 -> of(0))
  130. }
  131. var rem = n
  132. var factors = []
  133. func cfrac_find_factors(solution) {
  134. var X = 1
  135. var Y = 1
  136. for i in (^Q) {
  137. if (solution.bit(i)) {
  138. X *= Q[i][0] %= n #=
  139. Y *= Q[i][1]
  140. var g = gcd(X - Y.isqrt, rem)
  141. if ((g > 1) && (g < rem)) {
  142. rem = check_factor(rem, g, factors)
  143. return true if (rem == 1)
  144. }
  145. }
  146. }
  147. return false
  148. }
  149. var I = gaussian_elimination(A, L-1)
  150. var LR = (A.end - A.rindex_by { !.is_zero })
  151. for solution in (I.last(LR)) {
  152. cfrac_find_factors(solution) && break
  153. }
  154. var final_factors = []
  155. for f in (factors) {
  156. if (f.is_prime) {
  157. final_factors << f
  158. }
  159. else {
  160. final_factors << __FUNC__(f)...
  161. }
  162. }
  163. if (rem != 1) {
  164. if (rem != n) {
  165. final_factors << __FUNC__(rem)...
  166. }
  167. else {
  168. final_factors << rem
  169. }
  170. }
  171. # Failed to factorize n (try again with a multiplier)
  172. if (rem == n) {
  173. return __FUNC__(n, next_multiplier(multiplier))
  174. }
  175. # Return all prime factors of n
  176. final_factors.sort
  177. }
  178. var composites = (ARGV ? ARGV.map{.to_i} : {|k| irand(2, 1<<k) }.map(2..60) )
  179. for n in (composites) {
  180. var factors = cffmm(n)
  181. assert_eq(factors.prod, n)
  182. say "#{n} = #{factors.map {|f| f.is_prime ? f : \"#{f} (composite)\"}.join(' * ')}"
  183. }