我有两个清单:
a = [1, 2, 3, 5]
b = ["a", "b", "c", "d"]
并想用python生成器生成所有可能的组合。我知道我可以做:
combinations = list(itertools.product(a,b))
random.shuffle(combinations)
但是,这一个有一个极端的内存成本,因为我将不得不在内存中保存所有可能的组合,即使只想两个随机的唯一组合。
我的目标是得到一个python生成器,它的内存开销随着向它请求的迭代次数的增加而增加,在最大迭代次数时达到与itertools相同的O内存开销。
我现在有这个:
def _unique_combinations(a: List, b: List):
"""
Creates a generator that yields unique combinations of elements from a and b
in the form of (a_element, b_element) tuples in a random order.
"""
len_a, len_b = len(a), len(b)
generated = set()
for i in range(len_a):
for j in range(len_b):
while True:
# choose random elements from a and b
element_a = random.choice(a)
element_b = random.choice(b)
if (element_a, element_b) not in generated:
generated.add((element_a, element_b))
yield (element_a, element_b)
break
但它的缺陷,因为它可以在理论上永远运行,如果随机选择线是不幸的。
我期待修改现有的发电机,使其产生的索引在一个固定的时间内随机设置,这将是好的,让他们跟踪,因为这将是内存成本的线性增加,而不是指数。
我怎样才能修改随机索引生成器以在时间上绑定?
7条答案
按热度按时间xqnpmsa81#
乱采则始贱而终贵,穷尽则始贵而终贱。这里有一个“两全其美”的方法,我们在中途切换策略:
这种方法的主要潜在问题是,你在中间支付了一个很大的O(n)成本,所以即使当你查看整个运行时,它也会被洗掉,对于某些用例来说,让一个任意的调用者在中间一次性支付整个成本可能是不可取的,而不是预先支付,或者在所有调用者中均匀地分散它。(我可以想到一些方法来避免这种情况,在另一个线程中进行交换,但这会增加很多复杂性。也许有更好的办法)
请注意,就空间而言,这是非常理想的,因为你在中途最大化了空间(内存中有一半的元素),然后空间使用量减少到零,因为现在跟踪你没有分配的元素比你有分配的元素更便宜。
cld4siwp2#
我已经实现了stack overflow answer中建议的算法,它可以有效地完成您的要求,并且可以扩展到任何数量的维度。
我们使用一个素数和它的一个原根模n创建一个序列,该序列访问间隔中的每个数字一次。我们必须选择比乘积
len(a)*len(b)
稍大的素数,所以我们必须考虑索引错误的情况。然后,我们使用从1D -> 2D的Map将我们的序列号“翻译”为元组并产生结果。
我开始对各种方法进行基准测试。对于合并两个长度为1000的列表,@gog的
divmod
解决方案已经表现不佳,所以我将从进一步的测试中排除它:对于其余的算法,我进行了以下基准测试
x1c 0d1x我通过试图找到这个问题的解决方案学到了很多:)
qyuhtwio3#
用数字
(position_in_a * len_a) + position_in_b
表示每个组合。继续随机生成这些数字,一旦一个数字被击中,只需将其递增modlen_a * len_b
:ua4mk5z44#
这是你想要的吗?
您可以使用整数索引从所有可能组合的列表中生成任何组合:
因此,您不需要生成混洗的组合列表。我不认为有任何方法可以在不重复的情况下迭代地对范围内的整数进行采样而不生成列表(如this question的答案中所解释的),但至少现在你只需要生成一个整数数组,这需要更少的内存:
输出:
vs91vp4v5#
Samwise's的变体,但通过将其创建分散在过程的前半部分来避免创建
remaining
的大中间成本,并在过程的后半部分将其随机化。从集合到列表的转换相对较快。我怀疑它比Samwise的整体速度慢(而且它确实使用了更多的内存)。如果中间的延迟是不可接受的,那就更好了。
阶次频率的采样输出(Attempt This Online!):
cnjp1d6j6#
在你写这样一个程序之前,请允许我向你介绍一下“排列与组合”。假设您有一个水果列表(fruits =['apples','mangoes','grapes'])。可以排列列表的次数称为排列。这在数学上表示为(!)。现在,我们的列表包含三个项目。我们可以通过(3!),其等于6。现在,你只有六个移动或可能的 Shuffle 。另一方面,组合基本上是从列表中选择特定项目的一定数量的排列,例如,假设在我们的列表中,你想找出两个项目的组合数量。这可以在数学上表示为(2C 3),其中2是项目的数量,3是项目的总数。这将给予你3。但是,在Python中,我建议你使用itertools。这是一个令人惊叹的模块,将使您的工作更容易。但是,我希望您访问以下链接以获得更多见解。https://www.digitalocean.com/community/tutorials/permutation-and-combinatios-in-python
vfwfrxfs7#
@gog:代码片段在可扩展性方面有限制。它利用集合来跟踪生成的组合,随着可能组合的总数增加,内存使用和性能变得有问题。