CRT_polynomial_multiplication.sf 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. #!/usr/bin/ruby
  2. # Polynomial multiplication, using the Chinese Remainder Theorem (CRT).
  3. # Reference:
  4. # Lecture 14, Week 8 (2hrs) - Towards polynomial factorization
  5. # https://youtube.com/watch?v=KNyHz0eoAMA (from 1 hour and 7 minutes)
  6. func CRT (*congruences) {
  7. var c = 0
  8. var m = congruences.lcm { _[1] }
  9. for a,n in (congruences) {
  10. var t = m/n
  11. var u = (t * invmod(t, n))
  12. c += ((a.lift*u) % m)
  13. }
  14. return (c % m)
  15. }
  16. func poly_height(x) {
  17. x.coeffs.map { .tail.abs }.max
  18. }
  19. func CRT_poly_mul(a,b) {
  20. var c_height = 2*(poly_height(a) * poly_height(b) * min(a.coeffs.len, b.coeffs.len))
  21. var m = 1
  22. var P = []
  23. Math.seq(3, {.tail.next_prime}).each {|p|
  24. m *= p
  25. P << p
  26. break if (m > c_height)
  27. }
  28. var c = lift(CRT(P.map{|p| [Mod(a,p) * Mod(b,p), p] }...))
  29. var t = (m>>1)
  30. return Poly(c.coeffs.map_2d {|x,y| [x, (y > t) ? (y-m) : y] })
  31. }
  32. func CRT_poly_mul_space_optimized(a,b) {
  33. var c_height = 2*(poly_height(a) * poly_height(b) * min(a.coeffs.len, b.coeffs.len))
  34. var m = 1
  35. var c = [0, 1]
  36. Math.seq(3, {.tail.next_prime}).each {|p|
  37. m *= p
  38. c = [CRT(c, [Mod(a,p)*Mod(b,p), p]), m]
  39. break if (m > c_height)
  40. }
  41. c = c[0].lift
  42. var t = (m>>1)
  43. return Poly(c.coeffs.map_2d {|x,y| [x, (y > t) ? (y-m) : y] })
  44. }
  45. assert_eq(CRT([2, 3], [3, 5], [2, 7]), 23)
  46. var x = Poly(1)
  47. assert_eq(CRT_poly_mul(3*x - 4, 6*x + 5), (3*x - 4)*(6*x + 5))
  48. var a = (17*x**3 + 7*x**2 - x + 65)
  49. var b = (34*x**4 - 23*x**2 + 8*x - 12)
  50. say (a*b)
  51. say CRT_poly_mul(a,b)
  52. say CRT_poly_mul_space_optimized(a,b)
  53. __END__
  54. 578*x^7 + 238*x^6 - 425*x^5 + 2185*x^4 - 125*x^3 - 1587*x^2 + 532*x - 780