1- #!/usr/bin/env python3
21# -*- coding: utf-8 -*-
32"""
43Combinatorial Functions
54"""
65
76
8- import sympy
7+ import math
8+ from functools import lru_cache
99from sympy .functions .combinatorial .numbers import stirling
1010from mathics .version import __version__ # noqa used in loading to check consistency.
1111
@@ -80,11 +80,11 @@ def apply(self, values, evaluation):
8080 return Expression ("Times" , * leaves )
8181
8282
83- class Fibonacci (Builtin ):
83+ class Fibonacci (_MPMathFunction ):
8484 """
8585 <dl>
86- <dt>'Fibonacci[$n$]'
87- <dd>computes the $n$th Fibonacci number.
86+ <dt>'Fibonacci[$n$]'
87+ <dd>computes the $n$th Fibonacci number.
8888 </dl>
8989
9090 >> Fibonacci[0]
@@ -97,12 +97,65 @@ class Fibonacci(Builtin):
9797 = 280571172992510140037611932413038677189525
9898 """
9999
100+ nargs = 1
100101 attributes = ("Listable" , "NumericFunction" , "ReadProtected" )
102+ sympy_name = "fibonacci"
103+ mpmath_name = "fibonacci"
104+
105+ # Note: memoizing functions is a big win. For a value of n, more than
106+ # n different values (positive and negative) occur.
107+ # For this function to be effective across top-level calls,
108+ # it needs to be at the module level rather than inside the class.
109+ # Finally, docs say that `maxsize` is best at a power of 2.
110+ # With 1024 we can handle reasonably values of slightly greater than 1024
111+ # values without trouble:
112+ # PartitionsP[1070] = 366665450770488753893927654278831
113+ @lru_cache (maxsize = 1024 )
114+ def number_of_partitions (n : int ) -> int :
115+ """Algorithm NumberOfPartitions from Page 67 of Skiena: Implementing
116+ Discrete Mathematics, using Euler's recurrence"""
117+ if n < 0 :
118+ return 0
119+ elif n == 0 :
120+ return 1
121+ sum = 0
122+ for m in range (math .ceil ((1 + math .sqrt (1.0 + 24 * n )) / 6 ), 0 , - 1 ):
123+ mx3 = m * 3
124+ j = n - m * (mx3 - 1 ) / 2
125+ k = n - m * (mx3 + 1 ) / 2
126+ # Cut down on memoization by filtering negative numbers here.
127+ # In contrast to the multitude of negative numbers, the n==0
128+ # case isn't worth saving memoization checking since that is
129+ # one single entry.
130+ part_j = 0 if j < 0 else number_of_partitions (j )
131+ part_k = 0 if k < 0 else number_of_partitions (k )
132+ if m % 2 :
133+ sum += part_j + part_k
134+ else :
135+ sum += - part_j - part_k
136+ return sum
137+
138+ class PartitionsP (Builtin ):
139+ """
140+ <dl>
141+ <dt>'PartitionsP[$n$]'
142+ <dd>return the number p(n) of unrestricted partitions of the integer $n$.
143+ </dl>
144+
145+ >> Table[PartitionsP[k], {k, -2, 12}]
146+ = {0, 0, 1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42, 56, 77}
147+ """
148+
149+ attributes = ("Listable" , "NumericFunction" , "Orderless" )
101150
102151 def apply (self , n , evaluation ):
103- "Fibonacci [n_Integer]"
152+ "PartitionsP [n_Integer]"
104153
105- return Integer (sympy .fibonacci (n .get_int_value ()))
154+ return Integer (number_of_partitions (n .get_int_value ()))
155+
156+ # Simpler but inefficient.
157+ # from sympy.utilities.iterables import partitions
158+ # return Integer(len(list(partitions(n.get_int_value()))))
106159
107160
108161class _NoBoolVector (Exception ):
@@ -272,12 +325,13 @@ def _compute(self, n, c_ff, c_ft, c_tf, c_tt):
272325 r = 2 * (c_tf + c_ft )
273326 return Expression ("Divide" , r , c_tt + c_ff + r )
274327
328+
275329# Note: WL allows StirlingS1[{2, 4, 6}, 2], but we don't (yet).
276330class StirlingS1 (Builtin ):
277331 """
278332 <dl>
279333 <dt>'StirlingS1[$n$, $m$]'
280- <dd>gives the Stirling number of the first kind $𝒮_n ^m$.
334+ <dd>gives the Stirling number of the first kind $ _n ^m$.
281335 </dl>
282336
283337 Integer mathematical function, suitable for both symbolic and numerical manipulation.
@@ -302,10 +356,10 @@ class StirlingS2(Builtin):
302356 """
303357 <dl>
304358 <dt>'StirlingS2[$n$, $m$]'
305- <dd>gives the Stirling number of the second kind 𝒮_n ^m.
359+ <dd>gives the Stirling number of the second kind _n ^m.
306360 </dl>
307361
308- returns the number of ways of partitioning a set of $n$ elements into $m$ non‐ empty subsets.
362+ returns the number of ways of partitioning a set of $n$ elements into $m$ non empty subsets.
309363
310364 >> Table[StirlingS2[10, m], {m, 10}]
311365 = {1, 511, 9330, 34105, 42525, 22827, 5880, 750, 45, 1}
0 commit comments