我正在使用生成集合分区的算法(在C中实现)。 (代码在这里:http://www.martinbroadhurst.com/combinatorial-algorithms.html#partitions)。
我想知道是否有办法修改此算法以并行运行而不是线性运行?
我的CPU上有多个内核,并希望将分区的生成分成多个正在运行的线程。
答案 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 = {a
,b
},则为#1;如果#0 = {a
}且#1 = {b
},则为#2 。等等。因此,您逐个向部分构建的分区添加新元素,为每个输入生成一些可能的输出 - 直到您放置所有元素。并行化的关键是每个不完整的分区可以独立地附加新元素,即与所有其他变体并行。
该算法可以以不同的方式实现。我将使用递归方法,其中函数被赋予部分填充的数组及其当前长度,复制数组的次数与下一个元素的可能值一样多(这比数组的当前最后一个值多一个) ),将下一个元素设置为每个可能的值,并为每个新数组递归调用自身,增加长度。这种方法似乎特别适用于工作窃取并行引擎,例如cilk或tbb。类似于@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]
}
}
}