multiplicative_order.sf 1.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. #!/usr/bin/ruby
  2. # Author: Trizen
  3. # Date: 15 November 2021
  4. # https://github.com/trizen
  5. # Compute the multiplicative order of `a` modulo `n`: znorder(a, n).
  6. # This is the smallest positive integer k such that a^k == 1 (mod n).
  7. # Another approach would be to compute the Carmichael lambda function of n and
  8. # then find the smallest divisor d of this value that satisfy a^d == 1 (mod n).
  9. func my_znorder(a, n) is cached {
  10. gcd(a, n) == 1 || die "#{a} and #{n} are not relatively prime"
  11. if (n.is_prime) {
  12. return (n.dec.divisors.first {|d| powmod(a, d, n) == 1 })
  13. }
  14. if (n.is_prime_power) {
  15. var p = n.prime_root
  16. var z = __FUNC__(a, p)
  17. while (powmod(a, z, n) != 1) {
  18. z *= p
  19. }
  20. return z
  21. }
  22. n.factor_map {|p,e| __FUNC__(a, p**e) }.lcm
  23. }
  24. ## Run some tests
  25. assert_eq(my_znorder(37, 1000), 100)
  26. assert_eq(my_znorder(54, 100001), 9090)
  27. with (10**20 - 1) {|b|
  28. assert_eq(my_znorder(2, b), 3748806900)
  29. assert_eq(my_znorder(17, b), 1499522760)
  30. }
  31. for a in ([2, 3, 6, 10]) {
  32. var t = 1e7.irand
  33. for n in (t .. t+1e3) {
  34. next if (gcd(a, n) != 1)
  35. assert_eq(my_znorder(a, n), znorder(a, n))
  36. }
  37. }
  38. ## Example
  39. for n in (2..20) {
  40. var a = (20 + 1e2.irand -> next_prime)
  41. var z = my_znorder(a, n!)
  42. assert_eq(z, znorder(a, n!))
  43. say "znorder(#{a}, #{n}!) = #{z}"
  44. }