partitions_.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. from mpmath.libmp import (fzero, from_int, from_rational,
  2. fone, fhalf, bitcount, to_int, to_str, mpf_mul, mpf_div, mpf_sub,
  3. mpf_add, mpf_sqrt, mpf_pi, mpf_cosh_sinh, mpf_cos, mpf_sin)
  4. from sympy.core.numbers import igcd
  5. from .residue_ntheory import (_sqrt_mod_prime_power,
  6. legendre_symbol, jacobi_symbol, is_quad_residue)
  7. import math
  8. def _pre():
  9. maxn = 10**5
  10. global _factor
  11. global _totient
  12. _factor = [0]*maxn
  13. _totient = [1]*maxn
  14. lim = int(maxn**0.5) + 5
  15. for i in range(2, lim):
  16. if _factor[i] == 0:
  17. for j in range(i*i, maxn, i):
  18. if _factor[j] == 0:
  19. _factor[j] = i
  20. for i in range(2, maxn):
  21. if _factor[i] == 0:
  22. _factor[i] = i
  23. _totient[i] = i-1
  24. continue
  25. x = _factor[i]
  26. y = i//x
  27. if y % x == 0:
  28. _totient[i] = _totient[y]*x
  29. else:
  30. _totient[i] = _totient[y]*(x - 1)
  31. def _a(n, k, prec):
  32. """ Compute the inner sum in HRR formula [1]_
  33. References
  34. ==========
  35. .. [1] https://msp.org/pjm/1956/6-1/pjm-v6-n1-p18-p.pdf
  36. """
  37. if k == 1:
  38. return fone
  39. k1 = k
  40. e = 0
  41. p = _factor[k]
  42. while k1 % p == 0:
  43. k1 //= p
  44. e += 1
  45. k2 = k//k1 # k2 = p^e
  46. v = 1 - 24*n
  47. pi = mpf_pi(prec)
  48. if k1 == 1:
  49. # k = p^e
  50. if p == 2:
  51. mod = 8*k
  52. v = mod + v % mod
  53. v = (v*pow(9, k - 1, mod)) % mod
  54. m = _sqrt_mod_prime_power(v, 2, e + 3)[0]
  55. arg = mpf_div(mpf_mul(
  56. from_int(4*m), pi, prec), from_int(mod), prec)
  57. return mpf_mul(mpf_mul(
  58. from_int((-1)**e*jacobi_symbol(m - 1, m)),
  59. mpf_sqrt(from_int(k), prec), prec),
  60. mpf_sin(arg, prec), prec)
  61. if p == 3:
  62. mod = 3*k
  63. v = mod + v % mod
  64. if e > 1:
  65. v = (v*pow(64, k//3 - 1, mod)) % mod
  66. m = _sqrt_mod_prime_power(v, 3, e + 1)[0]
  67. arg = mpf_div(mpf_mul(from_int(4*m), pi, prec),
  68. from_int(mod), prec)
  69. return mpf_mul(mpf_mul(
  70. from_int(2*(-1)**(e + 1)*legendre_symbol(m, 3)),
  71. mpf_sqrt(from_int(k//3), prec), prec),
  72. mpf_sin(arg, prec), prec)
  73. v = k + v % k
  74. if v % p == 0:
  75. if e == 1:
  76. return mpf_mul(
  77. from_int(jacobi_symbol(3, k)),
  78. mpf_sqrt(from_int(k), prec), prec)
  79. return fzero
  80. if not is_quad_residue(v, p):
  81. return fzero
  82. _phi = p**(e - 1)*(p - 1)
  83. v = (v*pow(576, _phi - 1, k))
  84. m = _sqrt_mod_prime_power(v, p, e)[0]
  85. arg = mpf_div(
  86. mpf_mul(from_int(4*m), pi, prec),
  87. from_int(k), prec)
  88. return mpf_mul(mpf_mul(
  89. from_int(2*jacobi_symbol(3, k)),
  90. mpf_sqrt(from_int(k), prec), prec),
  91. mpf_cos(arg, prec), prec)
  92. if p != 2 or e >= 3:
  93. d1, d2 = igcd(k1, 24), igcd(k2, 24)
  94. e = 24//(d1*d2)
  95. n1 = ((d2*e*n + (k2**2 - 1)//d1)*
  96. pow(e*k2*k2*d2, _totient[k1] - 1, k1)) % k1
  97. n2 = ((d1*e*n + (k1**2 - 1)//d2)*
  98. pow(e*k1*k1*d1, _totient[k2] - 1, k2)) % k2
  99. return mpf_mul(_a(n1, k1, prec), _a(n2, k2, prec), prec)
  100. if e == 2:
  101. n1 = ((8*n + 5)*pow(128, _totient[k1] - 1, k1)) % k1
  102. n2 = (4 + ((n - 2 - (k1**2 - 1)//8)*(k1**2)) % 4) % 4
  103. return mpf_mul(mpf_mul(
  104. from_int(-1),
  105. _a(n1, k1, prec), prec),
  106. _a(n2, k2, prec))
  107. n1 = ((8*n + 1)*pow(32, _totient[k1] - 1, k1)) % k1
  108. n2 = (2 + (n - (k1**2 - 1)//8) % 2) % 2
  109. return mpf_mul(_a(n1, k1, prec), _a(n2, k2, prec), prec)
  110. def _d(n, j, prec, sq23pi, sqrt8):
  111. """
  112. Compute the sinh term in the outer sum of the HRR formula.
  113. The constants sqrt(2/3*pi) and sqrt(8) must be precomputed.
  114. """
  115. j = from_int(j)
  116. pi = mpf_pi(prec)
  117. a = mpf_div(sq23pi, j, prec)
  118. b = mpf_sub(from_int(n), from_rational(1, 24, prec), prec)
  119. c = mpf_sqrt(b, prec)
  120. ch, sh = mpf_cosh_sinh(mpf_mul(a, c), prec)
  121. D = mpf_div(
  122. mpf_sqrt(j, prec),
  123. mpf_mul(mpf_mul(sqrt8, b), pi), prec)
  124. E = mpf_sub(mpf_mul(a, ch), mpf_div(sh, c, prec), prec)
  125. return mpf_mul(D, E)
  126. def npartitions(n, verbose=False):
  127. """
  128. Calculate the partition function P(n), i.e. the number of ways that
  129. n can be written as a sum of positive integers.
  130. P(n) is computed using the Hardy-Ramanujan-Rademacher formula [1]_.
  131. The correctness of this implementation has been tested through $10^{10}$.
  132. Examples
  133. ========
  134. >>> from sympy.ntheory import npartitions
  135. >>> npartitions(25)
  136. 1958
  137. References
  138. ==========
  139. .. [1] https://mathworld.wolfram.com/PartitionFunctionP.html
  140. """
  141. n = int(n)
  142. if n < 0:
  143. return 0
  144. if n <= 5:
  145. return [1, 1, 2, 3, 5, 7][n]
  146. if '_factor' not in globals():
  147. _pre()
  148. # Estimate number of bits in p(n). This formula could be tidied
  149. pbits = int((
  150. math.pi*(2*n/3.)**0.5 -
  151. math.log(4*n))/math.log(10) + 1) * \
  152. math.log(10, 2)
  153. prec = p = int(pbits*1.1 + 100)
  154. s = fzero
  155. M = max(6, int(0.24*n**0.5 + 4))
  156. if M > 10**5:
  157. raise ValueError("Input too big") # Corresponds to n > 1.7e11
  158. sq23pi = mpf_mul(mpf_sqrt(from_rational(2, 3, p), p), mpf_pi(p), p)
  159. sqrt8 = mpf_sqrt(from_int(8), p)
  160. for q in range(1, M):
  161. a = _a(n, q, p)
  162. d = _d(n, q, p, sq23pi, sqrt8)
  163. s = mpf_add(s, mpf_mul(a, d), prec)
  164. if verbose:
  165. print("step", q, "of", M, to_str(a, 10), to_str(d, 10))
  166. # On average, the terms decrease rapidly in magnitude.
  167. # Dynamically reducing the precision greatly improves
  168. # performance.
  169. p = bitcount(abs(to_int(d))) + 50
  170. return int(to_int(mpf_add(s, fhalf, prec)))
  171. __all__ = ['npartitions']