根据集合

时间:2016-06-13 17:17:17

标签: python algorithm

虽然这个问题是使用Python编程语言制定的,但我认为它更像是编程逻辑问题。

我列出了所有可能的组合,即:n选择k

我可以使用

准备这样的列表
import itertools
bits_list = list(itertools.combinations(range(n), k))

如果' n'是100,而'k'是5,然后是' bits_list'的长度。将是75287520。

现在,我想修剪这个列表,这样数字就会出现在群组中,或者他们不会。我们使用以下几组作为示例:

设置1:[0,1,2]
第2集:[57,58]
第3集:[10,15,20,25]
第4集:[10,11,12,13,14,15,16,17,18,19]

这里每个集合需要一起出现在bits_list的任何成员中,或者根本不出现。

到目前为止,我只能想到一个解决这个问题的蛮力if-else方法,但if-else条件的数量会非常大。

以下是我所拥有的:

bits_list = [x for x in list(itertools.combinations(range(n), k)) 
             if all(y in x for y in [0, 1, 2]) or
             all(y not in x for y in [0, 1, 2])]

现在,这只涵盖了Set 1.我想为很多集合做这个。如果集合的长度长于k的值,我们可以忽略该集合(例如,k = 5和Set 4)。

请注意,最终目标是让人感到高兴。迭代一个范围,比如[5:25]并对附加列表进行处理。列表的大小在这里以指数方式增长,从计算上讲,非常昂贵!

用''如图10所示,python解释器在任何普通的16 GB RAM笔记本电脑上完成之前会中断该过程。我需要找到一个适合相对现代服务器(不是集群或服务器场)的内存的解决方案。

非常感谢任何帮助!

P.S。:直观地说,将这个问题视为为登上公共巴士或火车系统的人们提供所有可能的案例。通常,你登上整个团队,或者你不登上任何人。

<小时/> UPDATE:

  1. 对于上面给定的集合,如果k = 5,则bits_list的有效成员将是[0,1,2,57,58],即:Set1和Set2的组合。如果k = 10,那么我们可以将Set1 + Set2 + Set3 + NoSetElement构建为可能的成员。 @ DonkeyKong的解决方案让我意识到我没有在我的问题中明确提到这一点。

  2. 我有很多套;我打算使用足够的集合来修剪完整的组合列表,以使bits_list最终适合内存。

  3. @ 9000的建议在这里完全有效,在每次迭代中,我可以将组合保存为实际位。

4 个答案:

答案 0 :(得分:2)

这仍然会被记忆错误(,如果你坚持列表,我不知道你是如何远离它)在某一点(n左右) = 90,k = 5),但它比你当前的实现快得多。对于n=80k=5,我的基本基准测试的解决方案是2.6秒,而你的解决方案大约需要52秒。

这个想法是分别构建过滤器的不相交和子集部分。不相交的部分是微不足道的,子集部分是通过获取长度为k - set_len的所有不相交组合的itertools.product和集合中的各个元素来计算的。

from itertools import combinations, product, chain
n = 80
k = 5
set1 = {0,1,2}

nots = set(range(n)) - set1
disj_part = list(combinations(nots, k))
subs_part = [tuple(chain(x, els)) for x, *els in 
              product(combinations(nots, k - len(set1)), *([e] for e in set1))]
full_l = disj_part + subs_part

答案 1 :(得分:1)

如果您实际将比特表示为比特,即整数n位长度的二进制表示中的0/1值,并且设置了k位,那么您需要存储数据的RAM量将会非常大小。

此外,您还可以使用位操作来查看mask中的所有位是否实际设置(value & mask == mask),还是全部未设置(value | ~mask == value)。< / p>

蛮力可能会花费更短的时间来考虑更聪明的算法,因此对于一次性过滤来说完全没问题。

如果你必须经常快速地执行这个,并且n只有几百或更少,我宁愿使用cython来有效地描述蛮力算法,而不是考虑算法的改进。现代CPU可以有效地操作64位数字;你不会因为不比较一部分数字而受益匪浅。

OTOH如果您的n非常大,并且要比较的集合数量也很大,您可以对您的位进行分区以进行有效比较。

假设您可以有效地比较64位的块,并且您的位列表包含例如每个100个块。然后你可以用字符串做同样的事情:将chunk与chunk进行比较,如果其中一个块无法匹配,则不要比较其余部分。

答案 2 :(得分:0)

更快的实现是替换:

中的if和all()语句
bits_list = [x for x in list(itertools.combinations(range(n), k)) 
             if all(y in x for y in [0, 1, 2]) or
             all(y not in x for y in [0, 1, 2])]

使用python的设置操作isdisjoint()issubset()操作。

bits_generator = (set(x) for x in itertools.combinations(range(n), k))
first_set = set([0,1,2])
filter_bits = (x for x in bits_generator 
             if x.issubset(first_set) or
             x.isdisjoint(first_set))
answer_for_first_set = list(filter_bits)

我可以继续使用发电机和发电机,你不会耗尽内存,但你会等待并加速宇宙的热量死亡。不是因为python的运行时或其他实现细节,而是因为如果你选择一个大的N和K值,即使在计算机时间内也存在一些不可行的问题。

答案 3 :(得分:0)

基于@Mitch's answer的想法,我创建了一个解决方案,其思维略微不同于最初在问题中提出的解决方案。我没有创建所有组合的列表(bits_list),然后修剪那些与列出的集合不匹配的组合,而是从集合中构建了bits_list

import itertools
all_sets = [[0, 1, 2], [3, 4, 5], [6, 7], [8], [9, 19, 29], [10, 20, 30], 
            [11, 21, 31], [12, 22, 32], ...[57, 58], ... [95], [96], [97]]
bits_list = [list(itertools.chain.from_iterable(x)) for y in [1, 2, 3, 4, 5] 
             for x in itertools.combinations(all_sets, y)]

在这里,我没有找到n选择k,然后循环所有k,并找到与集合匹配的组合,而是从集合开始,甚至将各个成员自己包含在集合中,因此不需要2个组件 - 不相交和子集部分 - 在@Mitch's answer中讨论。