并行生成分区

时间:2012-02-01 16:24:52

标签: algorithm numbers parallel-processing combinatorics

我正在使用生成集合分区的算法(在C中实现)。 (代码在这里:http://www.martinbroadhurst.com/combinatorial-algorithms.html#partitions)。

我想知道是否有办法修改此算法以并行运行而不是线性运行?

我的CPU上有多个内核,并希望将分区的生成分成多个正在运行的线程。

4 个答案:

答案 0 :(得分:0)

初始化包含前k个元素的每个分区的共享集合。每个线程,直到集合为空,重复从集合中删除一个分区,并使用您链接到的算法为剩余的n-k元素生成所有可能性(当递增当前n元素分区时,获取另一个k元素前缀将改变第一个k元素之一。)

答案 1 :(得分:0)

正如您所看到的,您推荐的算法在基础n中创建计数器,并且每次将具有相同编号的项目放在一个组中,并以这种方式分区输入。

每个计数器从0 to (0,1,2,...,n-1)计数,这意味着A= n-1 +(n-2)* n + ... + 1 * n n-1 +0个数字。因此,您可以在k个不同的线程上运行算法,在第一个线程中,您应该从0到A / k计数,在第二个线程中,您应该从(A / k)+1到2 * A / k计数,依此类推。意味着您应该添加long变量并使用上限(在for循环条件中)进行检查。同时为n计算基本r*A/k for 0 <= r <= k格式的值和相关数字。

答案 2 :(得分:0)

首先,请考虑以下串行算法的变体。取元素a,并将其分配给子集#0(这始终有效,因为分区内子集的顺序无关紧要)。下一个元素b可能属于与a相同的子集,也可能属于不同的子集,即属于#1子集。然后,元素c属于#0(与a)或#1(与b一起,如果它与a分开),或属于其自己的子集(如果#0 = {ab},则为#1;如果#0 = {a}且#1 = {b},则为#2 。等等。因此,您逐个向部分构建的分区添加新元素,为每个输入生成一些可能的输出 - 直到您放置所有元素。并行化的关键是每个不完整的分区可以独立地附加新元素,即与所有其他变体并行。

该算法可以以不同的方式实现。我将使用递归方法,其中函数被赋予部分填充的数组及其当前长度,复制数组的次数与下一个元素的可能值一样多(这比数组的当前最后一个值多一个) ),将下一个元素设置为每个可能的值,并为每个新数组递归调用自身,增加长度。这种方法似乎特别适用于工作窃取并行引擎,例如。类似于@swen建议的实现也是可能的:你使用所有不完整分区和一个线程池的集合,每个线程从集合中获取一个分区,产生所有可能的扩展并将它们放回集合中;添加了所有元素的分区显然应该进入不同的集合。

答案 3 :(得分:0)

这是我使用swen的建议获得的c ++实现。线程数取决于r的值。对于r = 6,分区数是第六个响铃数,它等于203.对于r = 0,我们只得到一个正常的非并行程序。

#include "omp.h"
#include <bits/stdc++.h>
using namespace std;
typedef long long lli;

const int MAX=10010;
const int MX=100;
int N,r=6;

int F[MAX]; // partitions first r
int Fa[MAX][MX]; // complete partitions
int P[MAX]; // first appearances first r
int Pa[MAX][MX]; // first appearances complete

int next(){// iterates to next partition of first r
    for(int i=r-1;i>=0;i--){
        P[F[i]]=i;
    }
    for(int i=r-1;i>=0;i--){
        if( P[F[i]]!=i ){
            F[i]++;
            for(int j=i+1;j<r;j++){
                F[j]=0;
            }
            return(1);
        } 
    }
    return(0);
}

int sig(int ID){// iterates to next partition in thread
    for(int i=N-1;i>=0;i--){
        Pa[ID][Fa[ID][i]]=i;
    }
    for(int i=N-1;i>=r;i--){
        if( Pa[ID][Fa[ID][i]]!=i){
            Fa[ID][i]++;
            for(int j=i+1;j<N;j++){
                Fa[ID][j]=0;
            }
            return(1);
        } 
    }
    return(0);
}

int main(){
    int N;
    scanf("%d",&N);
    int t=1,partitions=0;
    while(t || next() ){// save the current partition so we can use it for a thread later
        t=0;
        for(int i=0;i<r;i++){
            Fa[partitions][i]=F[i];
        }
        partitions++;
    }
    omp_set_num_threads(partitions);
        #pragma omp parallel
    {
        int ID = omp_get_thread_num();
        int t=1;
        while(t || sig(ID) ){// iterate through each partition in the thread
            // the current partition in the thread is found in Fa[ID]
        }
    }
}