Advertisement
JoelSjogren

roll.py (ver5)

Aug 31st, 2016
175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.15 KB | None | 0 0
  1. # There is a simple connection between natural numbers and DFAs on a singleton
  2. # alphabet. The importance of this connection is easily illustrated in modular
  3. # arithmetic.
  4.  
  5. # Def: A roll is a pair of natural numbers, the second one of which is nonzero.
  6.  
  7. # (Thus a roll is a fraction, but we won't make this connection.)
  8.  
  9. # To every roll [p, q] we associate a DFA which goes through p states and then
  10. # goes into a loop of length q. Addition and multiplication of rolls, as well
  11. # as of their states, is easily defined. The states of this DFA are
  12. # conventionally taken to be the natural range [0, p+q).
  13.  
  14. # Good picture: https://pixabay.com/p-147631/?no_redirect
  15.  
  16. # (Actually, the theory of rolls should require no prior theory of natural
  17. # numbers if a roll is defined more like a DFA directly, but that's a more
  18. # beautiful story.)
  19.  
  20. # Here's some arithmetic aided by rolls.
  21.  
  22. import fractions
  23. import collections
  24. import functools
  25. import operator
  26.  
  27. """ Compute what state the DFA of b will be in after natural input a. """
  28. def nat_mod_roll(a, b):
  29.     flat, loop = b
  30.     return a if a < flat else (a - flat) % loop + flat
  31.  
  32. def prod(a, one=1):
  33.     return functools.reduce(operator.mul, a, one)
  34.  
  35. """
  36. Compute many factorizations, primes, totient factorizations and totients.
  37.  
  38. The return value can be used as the 'cache' parameter when calling functions
  39. that accept a cache. The size n of the cache must be strictly greater than the
  40. cyclic part of the modulus or these functions may not function.
  41. """
  42. def make_cache(n):
  43.     # factorize all numbers below n
  44.     facts = [collections.Counter() for _ in range(n)]
  45.     for i in range(2, n):
  46.         if not facts[i]:  # i is a prime; mark its multiples
  47.             k = i
  48.             while k < n:
  49.                 for j in range(k, n, k):
  50.                     facts[j][i] += 1
  51.                 k *= i
  52.     # find all primes below n
  53.     primes = [i for i, j in enumerate(facts) if sum(j.values())==1]
  54.     # compute euler_phi of all numbers below n, in factored form
  55.     phi_facts = [sum((sum((facts[i] for _ in range(j-1)),
  56.                           collections.Counter())+facts[i-1]
  57.         for i, j in c.items()), collections.Counter()) for c in facts]
  58.     # compute euler_phi of all numbers below n
  59.     phis = [prod(j**k for j, k in i.items()) for i in phi_facts]
  60.     phis[0] = 0
  61.     return facts, primes, phi_facts, phis
  62.  
  63. """
  64. Compute the smallest roll whose DFA states can index the states that the DFA
  65. of b ends up in after any input a^i.
  66. """
  67. def roll_of_powers_small(a, b):
  68.     powers = [1]
  69.     while True:
  70.         c = nat_mod_roll(a*powers[-1], b)
  71.         if c in powers:
  72.             return powers.index(c), len(powers) - powers.index(c)
  73.         powers.append(c)
  74.  
  75. """
  76. Compute a small roll whose DFA states can index the states that the DFA of b
  77. ends up in after any input a^i.
  78. """
  79. def roll_of_powers_big(a, b, cache):
  80.     if a == 0: return 1, 1
  81.     if a == 1: return 0, 1
  82.     m = next(iter(i for i in range(b[0]+b[1]) if a**i >= b[0]))
  83.     n = 0
  84.     d = b[1] // fractions.gcd(b[1], a**m % b[1])
  85.     c = fractions.gcd(d, a % b[1])
  86.     while c != 1:
  87.         d = d // c
  88.         c = fractions.gcd(d, c)
  89.         n += 1
  90.     _, _, _, phi = cache
  91.     return m + n, phi[d]
  92.  
  93. def roll_of_powers(a, b, cache=None):
  94.     if cache:
  95.         return roll_of_powers_big(a, b, cache)
  96.     else:
  97.         return roll_of_powers_small(a, b)
  98.  
  99. """ Compute what state the DFA of b will be in after tower input a. """
  100. def tower_mod_roll(a, b, cache=None):
  101.     if len(a) == 0:
  102.         return nat_mod_roll(1, b)
  103.     return nat_mod_roll(a[0]**tower_mod_roll(a[1:], roll_of_powers(a[0], b, cache), cache), b)
  104.  
  105. def mod(a, b, cache=None):
  106.     if isinstance(a, int): a = a,
  107.     if isinstance(b, int): b = 0, b
  108.     return tower_mod_roll(a, b, cache)
  109.  
  110. def test():
  111.     arg_a = [2, 10], [100, 1000, 10000], [104, 2], [2, 3]
  112.     arg_b = 100, 100000, 5, 112
  113.     expect = 24, 0, 1, 8
  114.     for a, b, good in zip(arg_a, arg_b, expect):
  115.         c = mod(a, b)
  116.         print("mod({}, {}) = {} -- ".format(a, b, c), end="")
  117.         print("OK" if c == good else "FAIL (expected {})".format(good))
  118.  
  119. if __name__ == "__main__":
  120.     test()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement