Python限定组合
Posted
技术标签:
【中文标题】Python限定组合【英文标题】:Python limited combination 【发布时间】:2017-03-10 12:03:54 【问题描述】:我有一个包含 200 个元素的列表。我想随机计算这些元素的长度 k 的所有组合的 10%,并将结果存储在一个列表中。
例如:
假设 'ABCD'
与 ['A', 'B', 'C', 'D']
相同,我想要长度为 2 的组合。在这种情况下,所有可能的组合都是 6 (n! / ((n-k)!x k!))。我想得到 0.6 -> 1(向上取整)的 10%。
我试过itertools.combinations('ABCD', 2)
,但它给了我所有的组合。
这里有一些关于我的问题的更多信息。
我有
all_points_coordinates = [
[-1.6339171050450814, 2.5160117038362722],
[-1.7207293090531386, 2.4574561328669748],
[0.10469849010750323, 2.9981724810572872],
]
我想计算其中 3 个的组合并使用
def all_way(points):
point = len(points)
allrout = []
allrout = list(itertools.permutations(points, point))
return allrout
但它给了我所有观点的组合。当我运行它 100 分时,它非常耗时,所以我只想计算这些组合的有限数量。
【问题讨论】:
为什么在找到 10% 的组合后不停止迭代?我没有清楚地看到这里的问题...你能详细说明一下吗? 前10%?您的问题并不完全清楚。 我的意思是 abcd 在 4 中的所有组合是 abcd abdc acbd acdb adbc adcb bacd badc bdac bdca bcad bcda cabd cadb cbad cbda cdab cdba dabc dacb dbac dbca dcab dcba 但我不想做 callculste all ı只想其中 3 个 badc bdac bdca 和 order not importem just randomitertools.combinations
返回一个迭代器,它不会一次创建所有组合。因此,只需循环组合并在您有足够的组合时跳出循环。它们将是有序的,而不是随机的。这有关系吗?
@OzanTunahanIsmailoglu 您的列表中有重复项吗?
【参考方案1】:
我们可以使用random.sample
生成随机组合,并使用一个集合来确保我们不会多次生成任何组合。这是一个简单的演示。
from random import seed, sample
seed(42)
def random_combinations(seq, size, num):
combos = set()
while len(combos) < num:
item = sample(seq, size)
combos.add(tuple(item))
return list(combos)
# test
data = [
(0, 1), (2, 3), (4, 5), (6, 7), (8, 9),
(10, 11), (12, 13), (14, 15), (16, 17), (18, 19),
]
# Make 20 random 3-element combinations
combos = random_combinations(data, 3, 20)
for i, item in enumerate(combos, 1):
print(':>2: '.format(i, item))
输出
1: ((2, 3), (12, 13), (8, 9))
2: ((6, 7), (18, 19), (4, 5))
3: ((2, 3), (16, 17), (18, 19))
4: ((0, 1), (4, 5), (12, 13))
5: ((14, 15), (10, 11), (4, 5))
6: ((2, 3), (0, 1), (8, 9))
7: ((6, 7), (16, 17), (0, 1))
8: ((12, 13), (2, 3), (8, 9))
9: ((6, 7), (14, 15), (8, 9))
10: ((10, 11), (18, 19), (8, 9))
11: ((0, 1), (14, 15), (2, 3))
12: ((18, 19), (10, 11), (6, 7))
13: ((18, 19), (12, 13), (0, 1))
14: ((10, 11), (8, 9), (4, 5))
15: ((8, 9), (2, 3), (6, 7))
16: ((2, 3), (0, 1), (6, 7))
17: ((16, 17), (6, 7), (12, 13))
18: ((2, 3), (12, 13), (18, 19))
19: ((0, 1), (2, 3), (6, 7))
20: ((6, 7), (10, 11), (2, 3))
正如 tobias_k 在 cmets 中提到的,此代码仅适用于 num
与组合总数不太接近的情况。如果你想要
请注意,此代码认为((2, 3), (12, 13), (8, 9))
与包含这3 对以不同顺序的元组不同,例如((2, 3), (8, 9), (12, 13))
。
如果您不希望这样,我们可以将我们的物品制成套装。为此我们需要使用frozenset
,因为普通集合是可变的,因此不可散列,因此不能设置项。
from random import seed, sample
seed(42)
def random_combinations(seq, size, num):
combos = set()
while len(combos) < num:
item = sample(seq, size)
combos.add(frozenset(item))
return [tuple(u) for u in combos]
# test
data = [
(0, 1), (2, 3), (4, 5), (6, 7), (8, 9),
(10, 11), (12, 13), (14, 15), (16, 17), (18, 19),
]
# Make 20 random 3-element combinations
combos = random_combinations(data, 3, 20)
for i, item in enumerate(combos, 1):
print(':>2: '.format(i, item))
输出
1: ((0, 1), (2, 3), (6, 7))
2: ((0, 1), (2, 3), (8, 9))
3: ((16, 17), (6, 7), (0, 1))
4: ((12, 13), (2, 3), (18, 19))
5: ((12, 13), (2, 3), (8, 9))
6: ((12, 13), (18, 19), (0, 1))
7: ((8, 9), (4, 5), (10, 11))
8: ((16, 17), (2, 3), (18, 19))
9: ((8, 9), (6, 7), (14, 15))
10: ((0, 1), (4, 5), (12, 13))
11: ((8, 9), (10, 11), (18, 19))
12: ((10, 11), (6, 7), (2, 3))
13: ((0, 1), (14, 15), (2, 3))
14: ((10, 11), (18, 19), (6, 7))
15: ((8, 9), (2, 3), (6, 7))
16: ((4, 5), (6, 7), (18, 19))
17: ((8, 9), (4, 5), (2, 3))
18: ((16, 17), (4, 5), (6, 7))
19: ((16, 17), (6, 7), (12, 13))
20: ((4, 5), (10, 11), (14, 15))
【讨论】:
很好,但是您可能会添加一个警告,不要在num
接近组合总数的情况下使用它,因为找到最后几个尚未看到的组合可能需要很长时间。
但它适用于 set ı has list 如何解决这个问题。我有 data=[[9, -3], [5, 8], [-6, 7]]
通常最好将坐标对存储为元组而不是列表。有关详细信息,请参阅here。我的代码需要这种形式的数据,但转换很容易:newdata = [tuple(u) for u in olddata]
。当然你也可以像[list(u) for u in newdata]
这样进行反向转换。【参考方案2】:
另一种相当简单的可能性:生成所有组合,但只保留那些随机变量为< 0.1
的组合以获得(大约)10% 的结果组合。
>>> sum(1 for _ in itertools.combinations(range(100), 3)) # total count for comparison
161700
>>> res = [c for c in itertools.combinations(range(100), 3) if random.random() < 0.1]
>>> len(res)
16227
与使用random.sample
相比,这样做的好处是它不需要保留所有的组合在内存中,虽然它仍然会生成 所有组合,但立即丢弃其中的 90%。此外,结果将只有大约 10% 的组合,但不完全是。不过,对于大量数字,这应该不是什么大问题。
【讨论】:
不错的方法。希望这将满足 OP 的需求。我很快就会使用sample
发布一个版本。
如果sum
耗时较长,可以解析计算组合总数
@Ev.Kounis 当然,只需显示编号以供参考。对于实际获得 10%,我不需要总和。【参考方案3】:
如果您不想在选择其中的一小部分之前预先计算所有组合,您有两种选择:
丑陋,但能胜任
随机播放列表中的元素。将结果添加到集合中 继续直到集合达到所需的长度。美丽但复杂
创建索引列表 计算每个索引的组合(类似于this question。【讨论】:
我对@987654322@ 很熟悉,但是您如何将其应用于组合? 选项 1 并不能保证不会有重复,对吧? @Ev.Kounis:是的,它通过使用集合来实现。这种解决方案的丑陋之处在于,当百分比上升时,它变得非常低效,因为它必须丢弃越来越多的重复项。【参考方案4】:选项 1(不是随机的,而是只生成需要的东西):
取itertools.combinations()
返回的结果的前 10%。
import itertools
from math import factorial, ceil
original = 'ABCD'
k = 2
percentage = 0.1
configurations = factorial(len(original)) / (factorial(len(original) - k) * factorial(k))
take = ceil(percentage * configurations)
res = []
for i, comb in enumerate(itertools.combinations(original, k), 1):
res.append(comb)
if i == take:
break
print(res, len(res))
选项 2(随机但首先生成完整列表):
随机抽取itertools.combinations()
返回的结果的 10%。 需要 Python 3.6 因为random.choices()
# Python 3.6 you can do this
import random
import itertools
from math import factorial, ceil
original = 'ABCD'
k = 2
percentage = 0.1
configurations = factorial(len(original)) / (factorial(len(original) - k) * factorial(k))
take = ceil(percentage * configurations)
res = random.choices([x for x in itertools.combinations(original, k)], k=take)
original
也可以是一个列表。
【讨论】:
但是版本1不是随机的,版本2需要先生成所有的组合。 @tobias_k 是的。在答案中添加了 cmets 以使其清楚。 200 个元素并不多,但 OP 没有指定组合将包含多少个元素,因此无法轻松估计运行时间 不清楚 OP 到底想要什么。我的猜测是他们想要从他们的 200 点列表中(大约)200,000 对点中随机选择 10%。但我可能完全错了。 ;) 顺便说一句,你为什么使用random.choices
? random.sample
不应该也一样吗?
@tobias_k 唯一的区别是替换。sample
没有替换,choices
有替换。【参考方案5】:
我这样解决我的问题
point= len(points)
p=int(point*10/100)
allrout = list(itertools.islice(itertools.permutations(points, point),p ))
print(len(allrout))
return allrout
【讨论】:
以上是关于Python限定组合的主要内容,如果未能解决你的问题,请参考以下文章