modular_lucas_sequence_V.sf 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. #!/usr/bin/ruby
  2. # Algorithm due to Aleksey Koval for efficiently computing the modular Lucas V sequence.
  3. # See also:
  4. # https://en.wikipedia.org/wiki/Lucas_sequence
  5. # https://file.scirp.org/pdf/IJCNS20101200011_90376712.pdf
  6. func lucasVmod(P, Q, n, m) {
  7. var(V1 = 2, V2 = P)
  8. var(Q1 = 1, Q2 = 1)
  9. var s = n.valuation(2)
  10. for bit in ((n>>(s+1)).as_bin.chars) {
  11. Q1 = mulmod(Q1, Q2, m)
  12. if (bit) {
  13. Q2 = mulmod(Q1, Q, m)
  14. V1 = mulsubmulmod(V2, V1, P, Q1, m)
  15. V2 = mulsubmulmod(V2, V2, 2, Q2, m)
  16. }
  17. else {
  18. Q2 = Q1
  19. V2 = mulsubmulmod(V2, V1, P, Q1, m)
  20. V1 = mulsubmulmod(V1, V1, 2, Q2, m)
  21. }
  22. }
  23. Q1 = mulmod(Q1, Q2, m)
  24. Q2 = mulmod(Q1, Q, m)
  25. V1 = mulsubmulmod(V2, V1, P, Q1, m)
  26. Q1 = mulmod(Q1, Q2, m)
  27. s.times {
  28. V1 = mulsubmulmod(V1, V1, 2, Q1, m)
  29. Q1 = mulmod(Q1, Q1, m)
  30. }
  31. return V1
  32. }
  33. #--------------------------------------------------------
  34. say {|n| lucasVmod(1, -1, n, n) }.map(1..30) # Lucas(n) modulo n
  35. say 25.by {|n| lucasVmod(1, -1, n, n) == 1 } # First 25 prime numbers
  36. #--------------------------------------------------------
  37. for k in (1..20) { # run some tests
  38. var p = 1e30.random_prime
  39. assert_eq(lucasVmod(1, -1, p, p), 1)
  40. var n = 1e30.irand
  41. var m = 1e30.irand
  42. var P = (100.irand * [-1,1].rand)
  43. var Q = (100.irand * [-1,1].rand)
  44. var V1 = lucasVmod(P, Q, n, m)
  45. var V2 = ::LucasVmod(P, Q, n, m)
  46. assert_eq(V1, V2)
  47. }