这是一个经典的Stars and bars 问题,Python 的itertools 模块确实提供了一个既简单又高效的解决方案,不需要任何额外的过滤。
先解释一下:你想以所有可能的方式在 3 只股票之间分配 100 个“点”。出于说明目的,让我们减少到 10 个点而不是 100 个点,每个点值 10% 而不是 1%。想象一下将这些点写成一个由十个* 字符组成的字符串:
**********
这些是“明星和酒吧”中的“明星”。现在要在 3 只股票中划分十颗星,我们插入两个 | 分隔符(“星和条”中的“条”)。例如,这样一种划分可能如下所示::
**|*******|*
这种特殊的星形和条形组合对应于 20% AAPL、70% GOOGL、10% IBM 的部门。另一个部门可能如下所示:
******||****
这相当于 60% AAPL、0% GOOGL、40% IBM。
很容易说服自己,由十个 * 字符和两个 | 字符组成的每个字符串恰好对应于三只股票中十个点的一个可能的划分。
所以要解决您的问题,我们需要做的就是生成包含十个* 星号和两个| 条形字符的所有可能字符串。或者,换一种方式来考虑,我们想在一个总长度为 12 的字符串中找到所有可能的位置对,我们可以放置两个小节字符。 Python 的itertools.combinations 函数可用于为我们提供那些可能的位置(例如itertools.combinations(range(12), 2)),然后将每对位置转换回range(10) 的划分很简单,分为三部分:插入一个额外的假想分隔线字符串开头和结尾的字符,然后找到每对分隔符之间的星数。这个星星的数量只是比两个分隔线之间的距离少一。
代码如下:
import itertools
def all_partitions(n, k):
"""
Generate all partitions of range(n) into k pieces.
"""
for c in itertools.combinations(range(n+k-1), k-1):
yield tuple(y-x-1 for x, y in zip((-1,) + c, c + (n+k-1,)))
对于您在问题中给出的情况,您需要all_partitions(100, 3)。但这会产生5151 分区,以(0, 0, 100) 开头并以(100, 0, 0) 结尾,因此在这里显示结果是不切实际的。相反,以下是较小情况下的结果:
>>> for partition in all_partitions(5, 3):
... print(partition)
...
(0, 0, 5)
(0, 1, 4)
(0, 2, 3)
(0, 3, 2)
(0, 4, 1)
(0, 5, 0)
(1, 0, 4)
(1, 1, 3)
(1, 2, 2)
(1, 3, 1)
(1, 4, 0)
(2, 0, 3)
(2, 1, 2)
(2, 2, 1)
(2, 3, 0)
(3, 0, 2)
(3, 1, 1)
(3, 2, 0)
(4, 0, 1)
(4, 1, 0)
(5, 0, 0)