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 random itertools.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】:

另一种相当简单的可能性:生成所有组合,但只保留那些随机变量为&lt; 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。

【讨论】:

我对@9​​87654322@ 很熟悉,但是您如何将其应用于组合? 选项 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.choicesrandom.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限定组合的主要内容,如果未能解决你的问题,请参考以下文章

python中的排列组合

序列(DP)(组合数)

Python 排列组合

python解决组合问题

设计模式-结构型模式,python组合模式

python内置函数-排列组合函数