Skip to content

Commit 5149cdc

Browse files
committed
rename new class to Partitions_parts_length_restricted
and generalize it to handle also max_part
1 parent d53dccd commit 5149cdc

File tree

1 file changed

+135
-67
lines changed

1 file changed

+135
-67
lines changed

src/sage/combinat/partition.py

Lines changed: 135 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5939,7 +5939,7 @@ class Partitions(UniqueRepresentation, Parent):
59395939
and ``length``::
59405940
59415941
sage: Partitions(5, min_part=2)
5942-
Partitions of 5 having parts at least 2
5942+
Partitions of 5 whose parts are at least 2
59435943
sage: Partitions(5, min_part=2).list()
59445944
[[5], [3, 2]]
59455945
@@ -6030,7 +6030,7 @@ class Partitions(UniqueRepresentation, Parent):
60306030
sage: TestSuite(Partitions(5, min_part=2)).run() # needs sage.libs.flint
60316031
60326032
sage: repr(Partitions(5, min_part=2))
6033-
'Partitions of 5 having parts at least 2'
6033+
'Partitions of 5 whose parts are at least 2'
60346034
60356035
sage: P = Partitions(5, min_part=2)
60366036
sage: P.first().parent()
@@ -6173,7 +6173,7 @@ def __classcall_private__(cls, n=None, **kwargs):
61736173
if 'max_part' in kwargs:
61746174
return PartitionsGreatestLE(n, kwargs['max_part'])
61756175
if 'min_part' in kwargs:
6176-
return PartitionsSmallestGE(n, kwargs['min_part'], 0, n)
6176+
return Partitions_parts_length_restricted(n, kwargs['min_part'], n, 0, n)
61776177
if 'length' in kwargs:
61786178
return Partitions_nk(n, kwargs['length'])
61796179

@@ -6201,15 +6201,17 @@ def __classcall_private__(cls, n=None, **kwargs):
62016201
if 'length' in kwargs and ('min_length' in kwargs or 'max_length' in kwargs):
62026202
raise ValueError("do not specify the length together with the minimal or maximal length")
62036203

6204-
if set(kwargs).issubset(['length', 'min_part',
6204+
if set(kwargs).issubset(['length', 'min_part', 'max_part',
62056205
'min_length', 'max_length']):
62066206
if 'length' in kwargs:
6207-
return PartitionsSmallestGE(n, kwargs.get('min_part', 1),
6208-
kwargs['length'],
6209-
kwargs['length'])
6210-
return PartitionsSmallestGE(n, kwargs.get('min_part', 1),
6211-
kwargs.get('min_length', 0),
6212-
kwargs.get('max_length', n))
6207+
return Partitions_parts_length_restricted(n, kwargs.get('min_part', 1),
6208+
kwargs.get('max_part', n),
6209+
kwargs['length'],
6210+
kwargs['length'])
6211+
return Partitions_parts_length_restricted(n, kwargs.get('min_part', 1),
6212+
kwargs.get('max_part', n),
6213+
kwargs.get('min_length', 0),
6214+
kwargs.get('max_length', n))
62136215

62146216
# FIXME: should inherit from IntegerListLex, and implement repr, or _name as a lazy attribute
62156217
kwargs['name'] = "Partitions of the integer {} satisfying constraints {}".format(n, ", ".join(["{}={}".format(key, kwargs[key]) for key in sorted(kwargs)]))
@@ -7445,7 +7447,7 @@ def subset(self, **kwargs):
74457447
sage: P = Partitions(5, length=2); P
74467448
Partitions of the integer 5 of length 2
74477449
sage: P.subset(max_part=3)
7448-
Partitions of the integer 5 satisfying constraints length=2, max_part=3
7450+
Partitions of 5 having length 2 and whose parts are at most 3
74497451
"""
74507452
return Partitions(self.n, length=self.k, **kwargs)
74517453

@@ -8831,110 +8833,176 @@ def cardinality(self):
88318833
return ZZ(ans)
88328834

88338835

8834-
##########################
8835-
# Partitions Smallest GE #
8836-
##########################
8836+
######################################
8837+
# Partitions_parts_length_restricted #
8838+
######################################
88378839

8838-
class PartitionsSmallestGE(UniqueRepresentation, IntegerListsLex):
8840+
class Partitions_parts_length_restricted(UniqueRepresentation, IntegerListsLex):
88398841
r"""
8840-
The class of all partitions of the integer `n` having parts
8841-
at least `k` and restricted length.
8842+
The class of all integer partitions having parts and length in a
8843+
given range.
8844+
8845+
This class is strictly more general than
8846+
:class:`PartitionsGreatestLE`.
8847+
8848+
INPUT:
8849+
8850+
- ``n`` -- the size of the partition
8851+
- ``min_part`` -- the bound on the smallest part
8852+
- ``max_part`` -- the bound on the largest part
8853+
- ``min_length`` -- the lower bound on the number of parts
8854+
- ``max_length`` -- the upper bound on the number of parts
88428855
88438856
EXAMPLES::
88448857
8845-
sage: from sage.combinat.partition import PartitionsSmallestGE
8846-
sage: PartitionsSmallestGE(10, 2, 0, 10)
8847-
Partitions of 10 having parts at least 2
8848-
sage: list(PartitionsSmallestGE(9, 2, 3, 4))
8849-
[[5, 2, 2], [4, 3, 2], [3, 3, 3], [3, 2, 2, 2]]
8858+
sage: from sage.combinat.partition import Partitions_parts_length_restricted
8859+
sage: Partitions_parts_length_restricted(10, 2, 5, 0, 10)
8860+
Partitions of 10 whose parts are between 2 and 5
8861+
sage: list(Partitions_parts_length_restricted(9, 2, 4, 3, 4))
8862+
[[4, 3, 2], [3, 3, 3], [3, 2, 2, 2]]
88508863
8851-
sage: [4,3,2,1] in PartitionsSmallestGE(10, 2, 0, 10)
8864+
sage: [4,3,2,1] in Partitions_parts_length_restricted(10, 2, 10, 0, 10)
88528865
False
8853-
sage: [2,2,2,2,2] in PartitionsSmallestGE(10, 2, 0, 10)
8866+
sage: [2,2,2,2,2] in Partitions_parts_length_restricted(10, 2, 10, 0, 10)
88548867
True
88558868
"""
88568869
@staticmethod
8857-
def __classcall_private__(cls, n, min_part, min_length, max_length):
8870+
def __classcall_private__(cls, n, min_part, max_part, min_length, max_length):
88588871
"""
88598872
Normalize the input to ensure a unique representation.
88608873
88618874
TESTS::
88628875
8863-
sage: from sage.combinat.partition import PartitionsSmallestGE
8864-
sage: P1 = PartitionsSmallestGE(9, 0, -1, 10)
8865-
sage: P2 = PartitionsSmallestGE(9, 1, 0, 10)
8876+
sage: from sage.combinat.partition import Partitions_parts_length_restricted
8877+
sage: P1 = Partitions_parts_length_restricted(9, 0, 20, -1, 10)
8878+
sage: P2 = Partitions_parts_length_restricted(9, 1, 9, 0, 9)
88668879
sage: P1 is P2
88678880
True
88688881
"""
88698882
n = ZZ(n)
8870-
min_part = max(min_part, ZZ.one())
8871-
min_length = max(min_length, ZZ.zero())
8872-
max_length = min(max_length, n)
8873-
return super().__classcall__(cls, n, min_part, min_length, max_length)
8874-
8875-
def __init__(self, n, min_part, min_length, max_length):
8883+
if min_part <= 0:
8884+
min_part = ZZ.one()
8885+
if max_part > n:
8886+
max_part = n
8887+
if max_part < 0:
8888+
max_part = ZZ.zero()
8889+
if min_length < 0:
8890+
min_length = ZZ.zero()
8891+
if max_length > n:
8892+
max_length = n
8893+
return super().__classcall__(cls, n, min_part, max_part, min_length, max_length)
8894+
8895+
def __init__(self, n, min_part, max_part, min_length, max_length):
88768896
"""
88778897
Initialize ``self``.
88788898
88798899
TESTS::
88808900
8881-
sage: from sage.combinat.partition import PartitionsSmallestGE
8882-
sage: p = PartitionsSmallestGE(10, 2, 0, 10)
8901+
sage: from sage.combinat.partition import Partitions_parts_length_restricted
8902+
sage: p = Partitions_parts_length_restricted(10, 2, 5, 3, 4)
88838903
sage: TestSuite(p).run()
88848904
"""
88858905
self._n = n
8886-
self._min_part = min_part
8887-
self._min_length = min_length
8888-
self._max_length = max_length
88898906

88908907
IntegerListsLex.__init__(self, self._n, max_slope=0,
8891-
min_part=self._min_part,
8892-
min_length=self._min_length,
8893-
max_length=self._max_length)
8908+
min_part=min_part,
8909+
max_part=max_part,
8910+
min_length=min_length,
8911+
max_length=max_length)
8912+
8913+
self._min_part = ZZ.one() if min_part is None else min_part
8914+
self._max_part = self._n if max_part is None else max_part
8915+
self._min_length = ZZ.zero() if min_length is None else min_length
8916+
self._max_length = self._n if max_length is None else max_length
88948917

88958918
def _repr_(self):
88968919
"""
88978920
Return a string representation of ``self``.
88988921
88998922
TESTS::
89008923
8901-
sage: from sage.combinat.partition import PartitionsSmallestGE
8902-
sage: PartitionsSmallestGE(9, 2, 0, 10)
8903-
Partitions of 9 having parts at least 2
8904-
sage: PartitionsSmallestGE(9, 2, 3, 5)
8905-
Partitions of 9 having length between 3 and 5 and parts at least 2
8906-
"""
8907-
if self._min_length == self._max_length:
8908-
return f"Partitions of {self._n} having length {self._min_length} and parts at least {self._min_part}"
8909-
if not self._min_length or (self._n and self._min_length == 1):
8910-
if self._max_length >= self._n:
8911-
return f"Partitions of {self._n} having parts at least {self._min_part}"
8912-
return f"Partitions of {self._n} having length at most {self._max_length} and parts at least {self._min_part}"
8913-
if self._max_length >= self._n:
8914-
return f"Partitions of {self._n} having length at least {self._min_length} and parts at least {self._min_part}"
8915-
return f"Partitions of {self._n} having length between {self._min_length} and {self._max_length} and parts at least {self._min_part}"
8924+
sage: from sage.combinat.partition import Partitions_parts_length_restricted
8925+
sage: Partitions_parts_length_restricted(9, 2, 9, 0, 10)
8926+
Partitions of 9 whose parts are at least 2
8927+
sage: Partitions_parts_length_restricted(9, 2, 9, 3, 5)
8928+
Partitions of 9 having length between 3 and 5 and whose parts are at least 2
8929+
"""
8930+
if not self._min_length and self._max_length == self._n:
8931+
length_str = ""
8932+
elif self._min_length == self._max_length:
8933+
length_str = f"having length {self._min_length}"
8934+
elif not self._min_length:
8935+
length_str = f"having length at most {self._max_length}"
8936+
elif self._max_length == self._n:
8937+
length_str = f"having length at least {self._min_length}"
8938+
else:
8939+
length_str = f"having length between {self._min_length} and {self._max_length}"
8940+
8941+
if self._min_part == ZZ.one() and self._max_part == self._n:
8942+
parts_str = ""
8943+
elif self._min_part == self._max_part:
8944+
parts_str = f"having parts equal to {self._min_part}"
8945+
elif self._min_part == ZZ.one():
8946+
parts_str = f"whose parts are at most {self._max_part}"
8947+
elif self._max_part == self._n:
8948+
parts_str = f"whose parts are at least {self._min_part}"
8949+
else:
8950+
parts_str = f"whose parts are between {self._min_part} and {self._max_part}"
8951+
8952+
if length_str:
8953+
if parts_str:
8954+
return f"Partitions of {self._n} " + length_str + " and " + parts_str
8955+
return f"Partitions of {self._n} " + length_str
8956+
if parts_str:
8957+
return f"Partitions of {self._n} " + parts_str
8958+
return f"Partitions of {self._n}"
89168959

89178960
def cardinality(self):
89188961
"""
89198962
Return the cardinality of ``self``.
89208963
89218964
EXAMPLES::
89228965
8923-
sage: from sage.combinat.partition import PartitionsSmallestGE
8924-
sage: list(PartitionsSmallestGE(9, 3, 0, 2))
8966+
sage: from sage.combinat.partition import Partitions_parts_length_restricted
8967+
sage: list(Partitions_parts_length_restricted(9, 3, 9, 0, 2))
89258968
[[9], [6, 3], [5, 4]]
8926-
sage: PartitionsSmallestGE(9, 3, 0, 2).cardinality()
8969+
sage: Partitions_parts_length_restricted(9, 3, 9, 0, 2).cardinality()
89278970
3
89288971
89298972
TESTS::
89308973
8931-
sage: all(PartitionsSmallestGE(n, a, b, c).cardinality() ==
8932-
....: len(list(PartitionsSmallestGE(n, a, b, c)))
8933-
....: for n in range(6) for a in range(1, 6) for b in range(6) for c in range(6))
8934-
True
8935-
"""
8936-
return sum(number_of_partitions_length(self._n - (self._min_part-1)*ell, ell)
8937-
for ell in range(self._min_length, self._max_length + 1))
8974+
sage: from itertools import product
8975+
sage: P = Partitions_parts_length_restricted
8976+
sage: all(P(n, a, b, k, m).cardinality() == len(list(P(n, a, b, k, m)))
8977+
....: for n, a, b, k, m in product(range(-1, 5), repeat=5))
8978+
True
8979+
"""
8980+
if not self._min_length and self._max_length == self._n and self._min_part == 1:
8981+
# unrestricted length, parts smaller max_part
8982+
return ZZ.sum(number_of_partitions_length(self._n, i)
8983+
for i in range(self._max_part + 1))
8984+
8985+
def partitions_len_max_part(n, b, l):
8986+
r"""
8987+
Return the number of partitions of `n` with exactly `l` parts and
8988+
the largest part at most `b`.
8989+
"""
8990+
if not n:
8991+
if not l:
8992+
return ZZ.one()
8993+
return ZZ.zero()
8994+
if not l or l > n or n > b * l:
8995+
return ZZ.zero()
8996+
if b >= n:
8997+
return number_of_partitions_length(n, l)
8998+
8999+
return ZZ.sum(partitions_len_max_part(n - m, m, l - 1)
9000+
for m in range(1, b+1))
9001+
9002+
return ZZ.sum(partitions_len_max_part(self._n - (self._min_part-1)*ell,
9003+
self._max_part - self._min_part + 1,
9004+
ell)
9005+
for ell in range(self._min_length, self._max_length + 1))
89389006

89399007
Element = Partition
89409008
options = Partitions.options

0 commit comments

Comments
 (0)