Skip to content

Commit 78407a6

Browse files
committed
improve partition_int (now supports both lower and upper limits)
1 parent 9801ded commit 78407a6

File tree

2 files changed

+29
-10
lines changed

2 files changed

+29
-10
lines changed

unpythonic/it.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -594,35 +594,51 @@ def partition(pred, iterable):
594594
t1, t2 = tee(iterable)
595595
return filterfalse(pred, t1), filter(pred, t2)
596596

597-
def partition_int(n, lower=1):
597+
def partition_int(n, lower=1, upper=None):
598598
"""Yield all ordered sequences of smaller positive integers that sum to `n`.
599599
600600
`n` must be an integer >= 1.
601601
602-
`lower` is an optional lower limit for the members of the sum. Each number
603-
must be `>= lower`. (Most of the splits are a ravioli consisting mostly of
604-
ones, so it is much faster to not generate those than to filter them out
605-
from the result. The default value `lower=1` generates everything.)
602+
`lower` is an optional lower limit for each member of the sum. Each member
603+
of the sum must be `>= lower`.
604+
605+
(Most of the splits are a ravioli consisting mostly of ones, so it is much
606+
faster to not generate such splits than to filter them out from the result.
607+
The default value `lower=1` generates everything.)
608+
609+
`upper` is, similarly, an optional upper limit; each member of the sum
610+
must be `<= upper`. The default `None` means no upper limit (effectively,
611+
in that case `upper=n`).
612+
613+
It must hold that `1 <= lower <= upper <= n`.
606614
607615
Not to be confused with `unpythonic.it.partition`, which partitions an
608616
iterable based on a predicate.
609617
610618
**CAUTION**: The number of possible partitions grows very quickly with `n`,
611-
so in practice this is only useful for small numbers. A possible use case
612-
is determining the number of letters to allocate for the components of an
613-
anagram.
619+
so in practice this is only useful for small numbers, or with a lower limit
620+
that is not too much smaller than `n` itself. A possible use case is
621+
determining the number of letters to allocate for each component of an
622+
anagram that may consist of several words.
614623
615624
See:
616625
https://en.wikipedia.org/wiki/Partition_(number_theory)
617626
"""
618-
# sanity check and fail-fast
627+
# sanity check the preconditions, fail-fast
619628
if not isinstance(n, int):
620629
raise TypeError('n must be integer; got {:s}'.format(str(type(n))))
630+
if not isinstance(lower, int):
631+
raise TypeError('lower must be integer; got {:s}'.format(str(type(lower))))
632+
if upper is not None and not isinstance(upper, int):
633+
raise TypeError('upper must be integer; got {:s}'.format(str(type(upper))))
634+
upper = upper if upper is not None else n
621635
if n < 1:
622636
raise ValueError('n must be positive; got {:d}'.format(n))
637+
if lower < 1 or upper < 1 or lower > n or upper > n or lower > upper:
638+
raise ValueError('it must hold that 1 <= lower <= upper <= n; got lower={:d}, upper={:d}'.format(lower, upper))
623639

624640
def _partition(n):
625-
for k in range(n, lower - 1, -1):
641+
for k in range(min(n, upper), lower - 1, -1):
626642
m = n - k
627643
if m == 0:
628644
yield (k,)

unpythonic/test/test_it.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,11 @@ def dostuff(s):
193193
# partition_int: split a small positive integer, in all possible ways, into smaller integers that sum to it
194194
assert tuple(partition_int(4)) == ((4,), (3, 1), (2, 2), (2, 1, 1), (1, 3), (1, 2, 1), (1, 1, 2), (1, 1, 1, 1))
195195
assert tuple(partition_int(5, lower=2)) == ((5,), (3, 2), (2, 3))
196+
assert tuple(partition_int(5, lower=2, upper=3)) == ((3, 2), (2, 3))
197+
assert tuple(partition_int(10, lower=3, upper=5)) == ((5, 5), (4, 3, 3), (3, 4, 3), (3, 3, 4))
196198
assert all(sum(terms) == 10 for terms in partition_int(10))
197199
assert all(sum(terms) == 10 for terms in partition_int(10, lower=3))
200+
assert all(sum(terms) == 10 for terms in partition_int(10, lower=3, upper=5))
198201

199202
# inn: contains-check with automatic termination for monotonic iterables
200203
evens = imemoize(s(2, 4, ...))

0 commit comments

Comments
 (0)