【问题标题】:Fastest way to search through iterable chain combinations?搜索可迭代链组合的最快方法?
【发布时间】:2013-11-19 01:01:06
【问题描述】:

我可以找到具有 10k 及以上整数的幂集和的最快方法是什么?

long_list = [randrange(0,10000) for r in xrange(10000)]

desired_sum = sum(randint(0,10000)+randint(0,10000)+randint(0,10000))

def powerset(iterable):
    # notice the '3' in combinations(s,3), taking all combinations of 3. 
    s = list(iterable)
    return chain.from_iterable(combinations(s, 3) for r in range(len(s)+1))

ps = powerset(set(long_list))

if desired_sum > 29994:
    print "cannot compute"
else:
    for i in ps:
        if sum(i) == desired_sum: # last combination of chain 9999+9998+9997
            print i

我的电脑计算时间太长,我想请教一些关于如何处理此类大型组合的提示。

为了像这样进行搜索,我的 for 循环必须在计算组合总和之前耗尽整个列表。

【问题讨论】:

  • 你想要的输出到底是什么?如果您的目标是迭代 所有 组合,那么......这很多。如果你只需要其中的一部分,你可以想出一些预处理来修剪初始范围。
  • @shx2 我想要的输出是打印等于我的总和 29994 的组合,但是这个数字会改变。你有什么建议对变量总和值执行预处理修剪?
  • 但你已经知道答案了。这是最有效的解决方案:print (9999,9998,9997)
  • @shx2 是的,但它是一个变量,这些数字加起来就是我的总和,我会用它们来生成其他东西
  • 同时,您的问题是要求以最快的方式总结您的巨型迭代的所有元素;确实没有比迭代它更快的方法了。 (好吧,您可以并行化或矢量化块以实现 8 倍的改进,但除此之外……)但是是什么让您认为这是做任何事情的有用方法?你到底想做什么?如果只是总和,你一开始就不需要集合。

标签: python search iterator set combinations


【解决方案1】:

找到最快的解决方案。

from random import randrange
long_list = [randrange(0,10000) for r in xrange(10000)]

def main():
    myList, result = sorted(set(long_list), reverse = True), []
    myLen = len(myList)
    for i in xrange(myLen):
        for j in xrange(i + 1, myLen):
            if 29994 - (myList[i] + myList[j]) > myList[j]: break
            for k in xrange(j + 1, myLen):
                tsum = myList[i] + myList[j] + myList[k]
                if tsum < 29994:
                    break
                elif tsum == 29994:
                    result.append((myList[i], myList[j], myList[k]))
    print result
    return result

import cProfile
cProfile.run("main()")

这会在一秒钟内在我的机器上运行。这个解决方案的美妙之处在于,它可以通过递归来推广到与总和匹配的任意数量的项目。

【讨论】:

  • 对 gnibbler 的解决方案反复运行它,它似乎没有做正确的事情。当只有一个答案时,它不会返回任何答案。只有当恰好有四个答案时,它才会返回其中一个。我不确定你的或 gnibbler 是否实现了 OP 真正想要的东西,但他们绝对没有实现相同的东西,而 gnibbler 的实现了 OP 的代码所做的相同的事情。
  • 第一个问题是你(本质上)在迭代排序组合的集合,而 OP 和 gnibbler 正在迭代所有组合,所以他们会选择 ( 9997、9998、9999)作为有效答案。
  • 第二个问题是,通过将集合组合的三个副本链接在一起,OP 的代码允许像 (9996, 9999, 9999) 这样的答案。无论这是一个错误,还是只有在long_list 中有多个 9999 时才应该这样做(事实上,他被转换为一个集合并返回一个列表而被丢弃),或者这是他想要的,我不能说.但同样,gnibbler 保留了这种行为,而你的只搜索集合的组合。
  • @abarnert 输入是随机生成的。因此,我们不能为相同的输入运行两个程序。并且 OP 也只使用唯一的数字,ps = powerset(set(long_list))。所以,9996, 9999, 9999 不是一个选项。但是,正如您正确指出的那样,这不会获取9997, 9998, 9999 的所有排列。需要与 OP 确认。
  • @abarnert 有了这个改变,我可以看到这个程序生成了[(9999, 9999, 9996), (9998, 9998, 9998), (9999, 9998, 9997)]
【解决方案2】:

如果您知道所需的总和,则只需遍历所有 pairs (combinations(s, 2)),并测​​试缺少的元素是否在集合中

感谢@thefourtheye

from random import randrange
from itertools import combinations
long_list = [randrange(0,10000) for r in xrange(10000)]

def powerset(it):
    return [(i[0], i[1], 29994 - sum(i)) for i in combinations(it, 2) if 29994 - sum(i) in it]

def main():
    print powerset(set(long_list))

import cProfile
cProfile.run("main()")

【讨论】:

  • 成对是什么意思?
  • @czl 看看这个程序。这是按照他的建议。这将在合理的时间内产生结果。 ideone.com/nrPzvr
  • @thefourtheye,希望您不介意我将其添加到我的答案中
  • @gnibbler 找到了最快的解决方案,请查看stackoverflow.com/a/20062162/1903116
【解决方案3】:

这个基因expr:

(combinations(s, 3) for r in range(len(s)+1))

... 只是一遍又一遍地生成相同的组合,len(s) 次。那不是动力装置。更重要的是,这只是白费力气;如果没有任何组合匹配,则这些组合的各个副本中的任何组合都不匹配。

因此,您可以通过不添加这些额外工作来进行优化:

def powerset(iterable):
    return combinations(iterable, 3)

由于您的可迭代对象预计大约有 6300 个成员(这大约是您应该在 10000 个列表中期望多少个唯一值,因此调用 set 然后 list 将为您提供一个这么长的列表),这将使速度大约提高 6300 倍。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-03
    • 1970-01-01
    • 1970-01-01
    • 2019-04-08
    • 2023-04-11
    • 2017-11-28
    • 2017-03-28
    相关资源
    最近更新 更多