multiplicative_order_from_phi.sf 1.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. #!/usr/bin/ruby
  2. # Author: Trizen
  3. # Date: 16 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. func prime_znorder(a, p, e) is cached {
  8. var m = p**e
  9. var t = (p-1)*(p**(e-1))
  10. t.divisors.first_by {|q| powmod(a, q, m) == 1 }
  11. }
  12. func my_znorder(a, m) {
  13. gcd(a, m) == 1 || die "#{a} and #{m} are not relatively prime"
  14. m.factor_map {|p,e| prime_znorder(a, p, e) }.lcm
  15. }
  16. ## Run some tests
  17. assert_eq(my_znorder(37, 1000), 100)
  18. assert_eq(my_znorder(54, 100001), 9090)
  19. with (10**20 - 1) {|b|
  20. assert_eq(my_znorder(2, b), 3748806900)
  21. assert_eq(my_znorder(17, b), 1499522760)
  22. }
  23. for a in ([2, 3, 6, 10]) {
  24. var t = 1e7.irand
  25. for n in (t .. t+1e3) {
  26. next if (gcd(a, n) != 1)
  27. assert_eq(my_znorder(a, n), znorder(a, n))
  28. }
  29. }
  30. ## Example
  31. for n in (2..20) {
  32. var a = (20 + 1e2.irand -> next_prime)
  33. var z = my_znorder(a, n!)
  34. assert_eq(z, znorder(a, n!))
  35. say "znorder(#{a}, #{n}!) = #{z}"
  36. }