添加两个阵列的元素

时间:2013-07-19 07:22:22

标签: arrays multithreading algorithm language-agnostic parallel-processing

你有两个阵列

int[] a = {......} // Total elements 1 million
int[] b = {......} // Total elements 1 million , Length is same for both arrays.

Q1。我必须创建一个数组

int[] c 

其元素是[]和b []的相应索引的总和 防爆。

 c[0] = a[0] + b[0];
 c[1] = a[1] + b[1];
 c[2] = a[2] + b[2];

解决方案: - >我可以利用多线程。将整个数组划分为10个或更多部分,并将每个段分配给线程以执行计算。 注意 - >采访者建议使用多线程

Q2。 现在它有点改变。数组C的元素将具有如下值: - >

c[0] = a[0] + b[0];
c[1] = a[1] + b[1] + c[0]; // At this line c[0] is Sum of a[0] + b[0]; The Above Line
c[2] = a[2] + b[2] + c[1]; // At this line c[0] is Sum of a[1] + b[1]+ a[0] + b[0]; The Above Line

MySolution->解决第1部分(Q1)并创建一个临时数组,之后我们必须执行这样的添加。

C[1] = temp[1]+temp[0]
C[2] = temp[2]+temp[1]

注意: - >我们真的不需要temp [],我们只能使用Array c这样做。只是简单地在SO上解释这个。

Problem->我不认为在问题2中我们可以使用多线程。有没有更好的方法来解决Q2?我们可以利用多线程吗?

5 个答案:

答案 0 :(得分:5)

在我看来,问题二你有两种技巧:

首先应该分两步完成。 步骤1使用您可以添加的线程

 c[0] = a[0] + b[0];
 c[1] = a[1] + b[1];
 c[2] = a[2] + b[2];

正如你的建议。

但是步骤2应该按顺序完成。因为c[ i + 1]值取决于c [i]

的更新值

第二种技术有点复杂但可以很快。

在第二个问题中要求您做的事情是:

 c[0] = a[0] + b[0];
 c[1] = a[1] + b[1] + a[0] + b[0];
 c[2] = a[2] + b[2] + a[1] + b[1] + a[0] + b[0];

这可以是平行的。

c[i] =  thread1( sum(a[0]...a[i] )) + thread2( sum(b[0]...b[i] ))
         for i >= 0

这项技术很好,您可以为所有c[i](它的两个类似级别线程模型)并行计算i

你可以进一步改进thread1 / thread2函数作为多线程与子线程来执行求和 - 但是记住有时多线程代码运行速度比单线程代码慢,因为线程上下文切换时间(所以你应该给出足够的任务量)到每个线程)。

与第二种技术不同的一点是" c[i]的线程的大部分内容与c[i-1]的线程所做的大致相同"。br /> 感谢@ jogojapan让我知道这个缺点。

为了更好的技术阅读,由Mr.Jogojapan更新了answer

答案 1 :(得分:4)

您可以执行第1部分多线程,如您所说 -

temp[0] = a[0] + b[0];
temp[1] = a[1] + b[1];
temp[2] = a[2] + b[2];
etc....

然后第2部分的计算变为 -

c[0] = temp[0];
c[1] = temp[1] + temp[0];
c[2] = temp[2] + temp[1] + temp[0];
c[3] = temp[3] + temp[2] + temp[1] + temp[0];
etc...

虽然这看起来是连续的并且不可能并行化,但实际上这是一种非常常见的操作,称为“前缀总和”或“扫描”。有关详细信息,包括如何并行化,请参阅WikipediaBlelloch

在8个元素的情况下,每个递归阶段都可以并行化,因为每个计算都不依赖于同一阶段的其他计算。

// 1st phase 
u[0] = temp[0] + temp[1];
u[1] = temp[2] + temp[3];
u[2] = temp[4] + temp[5];
u[3] = temp[6] + temp[7];

// 2nd phase
v[0] = u[0] + u[1];
v[1] = u[2] + u[3];

// 3rd phase
w[0] = v[0] + v[1];

// final phase
c[0] = temp[0];
c[1] = u[0];
c[2] = u[0] + temp[2];
c[3] = v[0];
c[4] = v[0] + temp[4];
c[5] = v[0] + u[2];
c[6] = v[0] + u[2] + temp[6];
c[7] = w[0];

答案 2 :(得分:1)

实际上你可以在这个任务中使用多线程。您的算法将包含两部分:

  1. 应用Q1算法 - 这部分将利用多线程。

  2. 只需在一个循环中应用formyla:c[n] = c[n] + c[n-1]n=1...999999

答案 3 :(得分:1)

您可以在第1个问题的方式中使用多线程。我的意思是你可以计算

sumA0B0 = a[0] + b[0];

在单独的线程中甚至等待计算(同步,即在[i]上)。然后在单独的线程中,您可以计算c[i] = sumAiBi + c[i-1];

答案 4 :(得分:0)

对Q2使用多线程的一种方法是两次传递(每次使用T线程,其中T可以自由选择):

  1. 以通常的多线程方式计算所有单元格的c[i] = a[i] + b[i] + c[i-1],即将输入分为[0,r1],[r1,r2],... [rk,n)并将一个线程应用于每个范围。是的,除了第一个范围外,所有范围都不正确,第2步将更正它。

  2. 再次以多线程方式计算更正。为此,我们会查找每个范围的最右侧值,即corr1:=c[r1-1]corr2:=corr1+c[r2-1]corr3:=corr2+c[r3-1]等,这为我们提供了更正值每个线程,然后再次使用具有与之前相同的范围的多线程计算c[i] += corrk,其中corrk是第k个线程的线程特定的校正值。 (对于第0个线程,我们可以使用corr0:=0,这样线程就不需要做任何事情了。)

  3. 这将理论运行时间提高了一个因子T,其中T是线程数(可以自由选择),因此就多线程而言,它是一种最佳解决方案。


    为了说明这是如何工作的,这里有一个例子,我们假设长度为n==30的数组。我们进一步假设我们使用3个线程:一个用于计算范围c[0..9],一个用于c[10..19],一个用于c[20..29]

    显然,目标是在单元格c[i]中,对于任何0<i<n,我们都会得到

    c[i] == a[0]+...+a[i]+b[0]+...+b[i]
    
    算法完成后

    (即所有a[0..i]和所有b[0..i]的总和)。让我们看看算法是如何实现的,例如单元格i==23。该单元由第三个线程处理,即负责范围c[20..29]的线程。

    第1步:线程集

    c[20] = a[20]+b[20]
    c[21] = c[20]+a[21]+b[21] == a[20]+a[21]+b[20]+b[21]
    c[22] = c[21]+a[22]+b[22] == a[20]+a[21]+a[22]+b[20]+b[21]+b[22]
    c[23] = c[22]+a[23]+b[23] == a[20]+a[21]+a[22]+a[23]+b[20]+b[21]+b[22]+b[23]
    ...
    

    因此,在第1步完成后,我们在单元格a[20..23]中有一些b[20..23]c[23]。遗漏的是a[0..19]b[0..19]的总和。

    同样,第一个和第二个线程已将值设置为c[0..9]c[10..19],以便

    c[0] = a[0]+b[0]
    c[1] = c[0]+a[1]+b[1] == a[0]+a[1]+b[0]+b[1]
    ...
    c[9] = a[0]+...+a[9]+b[0]+...+b[9]
    

    c[10] = a[10]+b[10]
    ...
    c[19] = a[10]+...+a[19]+b[10]+...+b[19]
    

    步骤2:第三个线程的校正值corr2corr1和第二个线程计算的最右边值的总和,而corr1是正确的 - 最大值由第一个线程计算。因此

    corr2 == c[9]+c[19] == (a[0]+...+a[9]+b[0]+...+b[9]) + (a[10]+...+a[19]+b[10]+...+b[19])
    

    这确实是在步骤1之后c[23]的值缺失的总和。在步骤2中,我们将此值添加到所有元素c[20..29],因此,在步骤2完成后,{{ 1}}是正确的(所有其他单元格也是如此)。


    这样做的原因是单元格值的计算是从左到右的严格增量操作,并且计算单个单元格的操作顺序无关紧要(因为{{1}是联想和交换)。因此,在步骤1之后,任何给定线程的最终结果(“最右边的值”)可以用于纠正在第2步中负责其范围的线程的结果。