【问题标题】:Making itertools.combinations calculations multiprocess in python?在python中使itertools.combinations计算多进程?
【发布时间】:2014-10-06 01:41:21
【问题描述】:

我正在使用这样的算法对小数数组进行一些计算:

fkn = Decimal('0')
for bits in itertools.combinations(decimals_array, elements_count):
    kxn = reduce(operator.mul, bits, Decimal('1'))
    fkn += kxn

我使用的是 Python 3.4 x64。 小数的精度>300(这是必须的)。 len(decimals_array) 大部分时间超过 40。 elements_count 大部分时间是 len(decimals_array)/2。

计算需要很长时间。 我想让它们成为多进程,所以首先我想制作一个包含所有组合的数组并将这个数组的一部分发送到许多进程 - 但是在制作这样的数组时我很快就会得到 MemoryError 异常。

现在我正在寻找更好的方法来使这段代码多进程。

在多核上运行此算法的好方法是什么?

或者也许有更好(更快)的方法来进行此类计算?

提前感谢您的一些想法。

【问题讨论】:

    标签: python decimal multiprocessing combinations itertools


    【解决方案1】:

    为了真正实现并行化,您需要绕过combinations() 的顺序,以便每个进程都可以生成自己的组合。剩下的问题已经可以并行化了。

    40 选择 20 大约是 1380 亿个组合,因此预先生成或在每个过程中生成它会受到伤害。一个包含 20 个元素的列表大约需要 224 个字节(比如sys.getsizeof()),如果你一次性生成整个东西,那就是 30 兆兆字节。难怪你的内存用完了。您也不能真正跨进程拆分生成器。或者更确切地说,如果你这样做了,每个进程都会得到它自己的生成器副本。

    解决方案 1 是拥有一个进程,其唯一工作是生成组合并将它们推送到队列中,可能分批以节省 IPC 开销,并让其他进程使用该队列中的组合。

    解决方案 2 是编写 combinations 的非顺序版本,它返回第 N 个组合而不计算其余组合。这绝对是可能的,因为排列是可能的,并且组合是排列的内部排序子集。然后,Pool 中的每个进程可以根据 N 的开始和步骤生成自己的进程 - 进程一个计数组合 0, 3, 6...,处理两个计数组合 1, 4, 7... 等等,例如。除非您使用 C/Cython,否则这可能会更慢。

    解决方案 3(或者可能是解决方案 0?)是转到数学 stackexchange 并询问是否有针对此问题的数学解决方案而不是计算解决方案。

    【讨论】:

    • 我将尝试使用您的解决方案 2,但稍作修改;我想我找到了如何跨进程“拆分”生成器的解决方案。在 Python3 中,借助 pickle 和 itertools.islice 是有可能的。完成后我会发布我的结果。感谢您的回答。
    • @vac 你成功了吗?你发布结果了吗?
    • @Deepstop 我不知道这是否对您有任何帮助,但github.com/markpiffer/pypourri 有一个功能binom_combi 允许从选定的组合开始,它还具​​有特殊的功能组合之间的单个位置从不假定 > +/-1 位置差异的计数方案。
    【解决方案2】:

    这是一种解决方案,虽然它不是很整洁。 这个想法是使用多个进程,其中一个进程负责一个interval。然而,由于itertools.combinations 是连续的,每个进程都必须循环不必要的组合,直到达到正确的间隔。当处理正确的间隔时,该过程停止。代码来自this book

    import itertools
    from tqdm import tqdm
    from math import factorial
    from multiprocessing import Process
    import itertools
    
    def total_combo(n, r):
        return factorial(n) // factorial(r) // factorial(n-r)
    
    
    def cal_combo(var,noCombo,start,end):
        data = itertools.combinations(range(var),noCombo)
        for i in enumerate(tqdm(data)):
            if i[0] >= start:
                if i[0] < start+10: print(i)
                if i[0] > end: break
    
    if __name__=='__main__':
    
        noCombo=3
        var=1000
    
        print(total_combo(var,noCombo),'combinations for',noCombo,'of',var,'variants')
        noProc=6
        interval=total_combo(var,noCombo)/noProc
        if interval%1==0:
            print(interval)
    
            procs=[]
    
            for pid in range(noProc):
                proc = Process(target=cal_combo, args=(var,noCombo, interval*pid, interval*(pid+1)))
                procs.append(proc)
                proc.start()
    
            for proc in procs:
                proc.join()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-07
      • 1970-01-01
      • 2017-07-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多