group_numbers.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. from itertools import chain, combinations
  2. from sympy.external.gmpy import gcd
  3. from sympy.ntheory.factor_ import factorint
  4. from sympy.utilities.misc import as_int
  5. def _is_nilpotent_number(factors: dict) -> bool:
  6. """ Check whether `n` is a nilpotent number.
  7. Note that ``factors`` is a prime factorization of `n`.
  8. This is a low-level helper for ``is_nilpotent_number``, for internal use.
  9. """
  10. for p in factors.keys():
  11. for q, e in factors.items():
  12. # We want to calculate
  13. # any(pow(q, k, p) == 1 for k in range(1, e + 1))
  14. m = 1
  15. for _ in range(e):
  16. m = m*q % p
  17. if m == 1:
  18. return False
  19. return True
  20. def is_nilpotent_number(n) -> bool:
  21. """
  22. Check whether `n` is a nilpotent number. A number `n` is said to be
  23. nilpotent if and only if every finite group of order `n` is nilpotent.
  24. For more information see [1]_.
  25. Examples
  26. ========
  27. >>> from sympy.combinatorics.group_numbers import is_nilpotent_number
  28. >>> from sympy import randprime
  29. >>> is_nilpotent_number(21)
  30. False
  31. >>> is_nilpotent_number(randprime(1, 30)**12)
  32. True
  33. References
  34. ==========
  35. .. [1] Pakianathan, J., Shankar, K., Nilpotent Numbers,
  36. The American Mathematical Monthly, 107(7), 631-634.
  37. .. [2] https://oeis.org/A056867
  38. """
  39. n = as_int(n)
  40. if n <= 0:
  41. raise ValueError("n must be a positive integer, not %i" % n)
  42. return _is_nilpotent_number(factorint(n))
  43. def is_abelian_number(n) -> bool:
  44. """
  45. Check whether `n` is an abelian number. A number `n` is said to be abelian
  46. if and only if every finite group of order `n` is abelian. For more
  47. information see [1]_.
  48. Examples
  49. ========
  50. >>> from sympy.combinatorics.group_numbers import is_abelian_number
  51. >>> from sympy import randprime
  52. >>> is_abelian_number(4)
  53. True
  54. >>> is_abelian_number(randprime(1, 2000)**2)
  55. True
  56. >>> is_abelian_number(60)
  57. False
  58. References
  59. ==========
  60. .. [1] Pakianathan, J., Shankar, K., Nilpotent Numbers,
  61. The American Mathematical Monthly, 107(7), 631-634.
  62. .. [2] https://oeis.org/A051532
  63. """
  64. n = as_int(n)
  65. if n <= 0:
  66. raise ValueError("n must be a positive integer, not %i" % n)
  67. factors = factorint(n)
  68. return all(e < 3 for e in factors.values()) and _is_nilpotent_number(factors)
  69. def is_cyclic_number(n) -> bool:
  70. """
  71. Check whether `n` is a cyclic number. A number `n` is said to be cyclic
  72. if and only if every finite group of order `n` is cyclic. For more
  73. information see [1]_.
  74. Examples
  75. ========
  76. >>> from sympy.combinatorics.group_numbers import is_cyclic_number
  77. >>> from sympy import randprime
  78. >>> is_cyclic_number(15)
  79. True
  80. >>> is_cyclic_number(randprime(1, 2000)**2)
  81. False
  82. >>> is_cyclic_number(4)
  83. False
  84. References
  85. ==========
  86. .. [1] Pakianathan, J., Shankar, K., Nilpotent Numbers,
  87. The American Mathematical Monthly, 107(7), 631-634.
  88. .. [2] https://oeis.org/A003277
  89. """
  90. n = as_int(n)
  91. if n <= 0:
  92. raise ValueError("n must be a positive integer, not %i" % n)
  93. factors = factorint(n)
  94. return all(e == 1 for e in factors.values()) and _is_nilpotent_number(factors)
  95. def _holder_formula(prime_factors):
  96. r""" Number of groups of order `n`.
  97. where `n` is squarefree and its prime factors are ``prime_factors``.
  98. i.e., ``n == math.prod(prime_factors)``
  99. Explanation
  100. ===========
  101. When `n` is squarefree, the number of groups of order `n` is expressed by
  102. .. math ::
  103. \sum_{d \mid n} \prod_p \frac{p^{c(p, d)} - 1}{p - 1}
  104. where `n=de`, `p` is the prime factor of `e`,
  105. and `c(p, d)` is the number of prime factors `q` of `d` such that `q \equiv 1 \pmod{p}` [2]_.
  106. The formula is elegant, but can be improved when implemented as an algorithm.
  107. Since `n` is assumed to be squarefree, the divisor `d` of `n` can be identified with the power set of prime factors.
  108. We let `N` be the set of prime factors of `n`.
  109. `F = \{p \in N : \forall q \in N, q \not\equiv 1 \pmod{p} \}, M = N \setminus F`, we have the following.
  110. .. math ::
  111. \sum_{d \in 2^{M}} \prod_{p \in M \setminus d} \frac{p^{c(p, F \cup d)} - 1}{p - 1}
  112. Practically, many prime factors are expected to be members of `F`, thus reducing computation time.
  113. Parameters
  114. ==========
  115. prime_factors : set
  116. The set of prime factors of ``n``. where `n` is squarefree.
  117. Returns
  118. =======
  119. int : Number of groups of order ``n``
  120. Examples
  121. ========
  122. >>> from sympy.combinatorics.group_numbers import _holder_formula
  123. >>> _holder_formula({2}) # n = 2
  124. 1
  125. >>> _holder_formula({2, 3}) # n = 2*3 = 6
  126. 2
  127. See Also
  128. ========
  129. groups_count
  130. References
  131. ==========
  132. .. [1] Otto Holder, Die Gruppen der Ordnungen p^3, pq^2, pqr, p^4,
  133. Math. Ann. 43 pp. 301-412 (1893).
  134. http://dx.doi.org/10.1007/BF01443651
  135. .. [2] John H. Conway, Heiko Dietrich and E.A. O'Brien,
  136. Counting groups: gnus, moas and other exotica
  137. The Mathematical Intelligencer 30, 6-15 (2008)
  138. https://doi.org/10.1007/BF02985731
  139. """
  140. F = {p for p in prime_factors if all(q % p != 1 for q in prime_factors)}
  141. M = prime_factors - F
  142. s = 0
  143. powerset = chain.from_iterable(combinations(M, r) for r in range(len(M)+1))
  144. for ps in powerset:
  145. ps = set(ps)
  146. prod = 1
  147. for p in M - ps:
  148. c = len([q for q in F | ps if q % p == 1])
  149. prod *= (p**c - 1) // (p - 1)
  150. if not prod:
  151. break
  152. s += prod
  153. return s
  154. def groups_count(n):
  155. r""" Number of groups of order `n`.
  156. In [1]_, ``gnu(n)`` is given, so we follow this notation here as well.
  157. Parameters
  158. ==========
  159. n : Integer
  160. ``n`` is a positive integer
  161. Returns
  162. =======
  163. int : ``gnu(n)``
  164. Raises
  165. ======
  166. ValueError
  167. Number of groups of order ``n`` is unknown or not implemented.
  168. For example, gnu(`2^{11}`) is not yet known.
  169. On the other hand, gnu(99) is known to be 2,
  170. but this has not yet been implemented in this function.
  171. Examples
  172. ========
  173. >>> from sympy.combinatorics.group_numbers import groups_count
  174. >>> groups_count(3) # There is only one cyclic group of order 3
  175. 1
  176. >>> # There are two groups of order 10: the cyclic group and the dihedral group
  177. >>> groups_count(10)
  178. 2
  179. See Also
  180. ========
  181. is_cyclic_number
  182. `n` is cyclic iff gnu(n) = 1
  183. References
  184. ==========
  185. .. [1] John H. Conway, Heiko Dietrich and E.A. O'Brien,
  186. Counting groups: gnus, moas and other exotica
  187. The Mathematical Intelligencer 30, 6-15 (2008)
  188. https://doi.org/10.1007/BF02985731
  189. .. [2] https://oeis.org/A000001
  190. """
  191. n = as_int(n)
  192. if n <= 0:
  193. raise ValueError("n must be a positive integer, not %i" % n)
  194. factors = factorint(n)
  195. if len(factors) == 1:
  196. (p, e) = list(factors.items())[0]
  197. if p == 2:
  198. A000679 = [1, 1, 2, 5, 14, 51, 267, 2328, 56092, 10494213, 49487367289]
  199. if e < len(A000679):
  200. return A000679[e]
  201. if p == 3:
  202. A090091 = [1, 1, 2, 5, 15, 67, 504, 9310, 1396077, 5937876645]
  203. if e < len(A090091):
  204. return A090091[e]
  205. if e <= 2: # gnu(p) = 1, gnu(p**2) = 2
  206. return e
  207. if e == 3: # gnu(p**3) = 5
  208. return 5
  209. if e == 4: # if p is an odd prime, gnu(p**4) = 15
  210. return 15
  211. if e == 5: # if p >= 5, gnu(p**5) is expressed by the following equation
  212. return 61 + 2*p + 2*gcd(p-1, 3) + gcd(p-1, 4)
  213. if e == 6: # if p >= 6, gnu(p**6) is expressed by the following equation
  214. return 3*p**2 + 39*p + 344 +\
  215. 24*gcd(p-1, 3) + 11*gcd(p-1, 4) + 2*gcd(p-1, 5)
  216. if e == 7: # if p >= 7, gnu(p**7) is expressed by the following equation
  217. if p == 5:
  218. return 34297
  219. return 3*p**5 + 12*p**4 + 44*p**3 + 170*p**2 + 707*p + 2455 +\
  220. (4*p**2 + 44*p + 291)*gcd(p-1, 3) + (p**2 + 19*p + 135)*gcd(p-1, 4) + \
  221. (3*p + 31)*gcd(p-1, 5) + 4*gcd(p-1, 7) + 5*gcd(p-1, 8) + gcd(p-1, 9)
  222. if any(e > 1 for e in factors.values()): # n is not squarefree
  223. # some known values for small n that have more than 1 factor and are not square free (https://oeis.org/A000001)
  224. small = {12: 5, 18: 5, 20: 5, 24: 15, 28: 4, 36: 14, 40: 14, 44: 4, 45: 2, 48: 52,
  225. 50: 5, 52: 5, 54: 15, 56: 13, 60: 13, 63: 4, 68: 5, 72: 50, 75: 3, 76: 4,
  226. 80: 52, 84: 15, 88: 12, 90: 10, 92: 4}
  227. if n in small:
  228. return small[n]
  229. raise ValueError("Number of groups of order n is unknown or not implemented")
  230. if len(factors) == 2: # n is squarefree semiprime
  231. p, q = sorted(factors.keys())
  232. return 2 if q % p == 1 else 1
  233. return _holder_formula(set(factors.keys()))