【问题标题】:Loop-Level Prefix-Sum: Messed up partial sums循环级前缀和:弄乱了部分和
【发布时间】:2021-10-22 10:24:01
【问题描述】:
#include <omp.h>
#include <stdio.h>


void prefix(int n, int A[n]){
    
    //end of recursion
    if(n==1) return;
    
    int n2 = n/2;
    
    //Array split
    int L[n2];
    int R[n-n2];
    
    #pragma omp parallel for
    for(int i=0; i<n2; i++){
        L[i]=A[i];
    }
    

    #pragma omp parallel for
    for(int i=n2; i<n-n2; i++){
        R[i-n2]=A[i];
    }
         
    
    //recursion
    #pragma omp task
    prefix(n2,L);
    
    #pragma omp task
    prefix(n-n2,R);
    
    #pragma omp taskwait
    #pragma omp parallel for
    for(int i = 0; i<n2; i++){
        A[n2+i]=A[n2]+A[n2+i];
    }
    for(int i = 0; i<n; i++){
        printf("%d\n",A[i]);
    }
    printf("END\n");
}

int main(){
    
    int A[4] = {1,2,3,4};
    prefix(4,A);
    
    for(int i=0;i<4;i++){
        printf("%d",A[i]);
    }
}

输出: 1 结尾 32 结尾 1 4 结尾 16 结尾 32 结尾 16 0 结尾 1 2 6 7 结尾 第1267章

所以我知道部分递归和由于前半部分的 32 而被搞砸了。但我目前正在努力寻找原因。初始算法使用从 1 到 n 的数组。与范围有关吗?

感谢您的帮助。

编辑:

现在我明白了:

1 2 结尾 6684773 7864425 结尾 1 2 3 7 结尾 第1237章

什么原因导致前缀和计算失败?

现在解决了:

#include <omp.h>
#include <stdio.h>


void prefix(int n, int *A, int start, int end){
    
    //end of recursion
    if(n==1) return;
    
    int n2 = n/2;
    
    //recursion
    #pragma omp task
    prefix(n2,A, start, end-n2);
    
    #pragma omp task
    prefix(n-n2,A,end-n2+1, end);   
    #pragma omp taskwait
   
    #pragma omp parallel for
    for(int i = 0; i<n2; i++){
        A[end-n2+1+i]=A[end-n2]+A[end-n2+1+i];
    }
    for(int i = 0; i<n; i++){
        printf("%d\n",A[i]);
    }
    printf("END\n");
}

int main(){
    
    int A[4] = {1,1,1,1};
    prefix(4,A,0,3);
    
    for(int i=0;i<4;i++){
        printf("%d",A[i]);
    }
}

【问题讨论】:

  • 如果n 为零,您应该返回零,而不是A[0](在这种情况下,这是一个越界访问,假设零大小的数组是有意义的)。如果n 为一,则可以返回A[0]
  • prefix 函数不应返回任何内容,因为对 prefix 的所有三个调用都忽略了返回值。将函数声明为void prefix(int n, int A[n]);,并将return A[0];更改为return;
  • 代码无法正确处理n 的奇数值。请记住,整数除法会向 0 截断。例如,如果 n=3L[n/2]R[n/2] 实际上是 L[1]R[1]。这是一个问题,因为n的起始值是4,所以递归调用是n=2,然后是n=1,1是奇数。
  • 在函数顶部计算一次n2 = n/2。这两个数组的长度将是n2n - n2。请注意,如果n 是偶数,n - n2 将等于n2,如果n 是奇数,则n2+1
  • prefix 函数到底应该做什么?请澄清一下。除了索引问题,使用本地数组(L,R)的递归调用对main中的数组A没有任何影响。

标签: c recursion openmp prefix-sum


【解决方案1】:
#include <omp.h>
#include <stdio.h>


void prefix(int n, int *A, int start, int end){
    
    //end of recursion
    if(n==1) return;
    
    int n2 = n/2;
    
    //recursion
    #pragma omp task
    prefix(n2,A, start, end-n2);
    
    #pragma omp task
    prefix(n-n2,A,end-n2+1, end);   
    #pragma omp taskwait
   
    #pragma omp parallel for
    for(int i = 0; i<n2; i++){
        A[end-n2+1+i]=A[end-n2]+A[end-n2+1+i];
    }
    for(int i = 0; i<n; i++){
        printf("%d\n",A[i]);
    }
    printf("END\n");
}

int main(){
    
    int A[4] = {1,1,1,1};
    prefix(4,A,0,3);
    
    for(int i=0;i<4;i++){
        printf("%d",A[i]);
    }
}

【讨论】:

    【解决方案2】:

    请注意,如果n 是奇数,则答案中的程序不会给出正确的结果。对于这个简单的任务,您不需要递归算法,除非它是您的家庭作业并且您必须使用递归算法和 OpenMP 任务。您应该使用更简单的算法,例如

    void prefix(int n, int* A){    
        if (n>1) {
            int sum=A[0];
            for(int i=1; i<n;i++){
                sum+=A[i];
                A[i]=sum;
            }
        }
    }
    
    

    【讨论】:

    • 将初始数组放大为零并在最后删除它对奇数不起作用吗?
    • 没有。对于int A[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};,您的代码给出:1,1,1,3,4,4,6,7,7,9,10,10,12,13,13,15,16,16,18,19,19,21,25,26,27
    • 如果您需要并行算法来计算前缀和,您可以在网上找到很多,例如:www2.hawaii.edu/~nodari/teaching/f17/slides/…
    • 可以用 0 放大它,直到长度为 2。
    • 感谢建议的链接。
    猜你喜欢
    • 2012-05-26
    • 2014-03-04
    • 1970-01-01
    • 2014-05-15
    • 2014-08-11
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 2015-09-03
    相关资源
    最近更新 更多