C# 使用Task并行计算递归算法

C# 使用Task并行计算递归算法,c#,algorithm,recursion,parallel-processing,task,C#,Algorithm,Recursion,Parallel Processing,Task,如何使用任务将此顺序递归算法转换为并行递归算法 public static List<int> s = new List<int>(); // random integers, array size is n public static List<int> p = new List<int>(); // random integers, array size is n public static int n = 100; public static

如何使用任务将此顺序递归算法转换为并行递归算法

public static List<int> s = new List<int>(); // random integers, array size is n
public static List<int> p = new List<int>(); // random integers, array size is n
public static int n = 100;
public static int w = 10;

static void Main(string[] args)
{
    G1(n, w)
}

private static int G1(int k, int r)
{
    if (k == 0 || r == 0)
    {
        return 0;
    }

    if (s[k - 1] > r)
    {
        return G1(k - 1, r);
    }

    return Math.Max(G1(k - 1, r), p[k - 1] + G1(k - 1, r - s[k - 1]));
}
publicstaticlist s=newlist();//随机整数,数组大小为n
公共静态列表p=new List();//随机整数,数组大小为n
公共静态int n=100;
公共静态int w=10;
静态void Main(字符串[]参数)
{
G1(北,西)
}
私有静态int G1(int k,int r)
{
如果(k==0 | | r==0)
{
返回0;
}
如果(s[k-1]>r)
{
返回G1(k-1,r);
}
返回Math.Max(G1(k-1,r),p[k-1]+G1(k-1,r-s[k-1]);
}
我有一个例子,但它太混乱了,它没有解释,也不像我的例子那么复杂

class CustomData
{
    public int TNum;
    public int TResult;
}

static int F1(int n)
{
    if (n > 1) return F1(n - 1) + F1(n - 2);
    else return 1;
}

static int F2(int n)
{
    int fibnum = 0;
    if (n < 6) fibnum = F1(n);
    else
    {
    //fibnum = F1(n - 3) + 3 * F1(n - 4) + 3 * F1(n - 5) + F1(n - 6);
    int countCPU = 4;
    Task[] tasks = new Task[countCPU];
    for (var j = 0; j < countCPU; j++)
    tasks[j] = Task.Factory.StartNew(
    (Object pp) =>
    {
        var data = pp as CustomData; if (data == null) return;
        data.TResult = F1(n - data.TNum - 3);
    },
    new CustomData() {TNum = j });
    Task.WaitAll(tasks);
    fibnum = (tasks[0].AsyncState as CustomData).TResult + 3 * (tasks[1].AsyncState as CustomData).TResult + 3 * (tasks[2].AsyncState as CustomData).TResult + (tasks[3].AsyncState as CustomData).TResult;
    }
    return fibnum;
}
class自定义数据
{
公共国际;
公共int TResult;
}
静态整数F1(整数n)
{
如果(n>1)返回F1(n-1)+F1(n-2);
否则返回1;
}
静态整数F2(整数n)
{
int fibnum=0;
如果(n<6)fibnum=F1(n);
其他的
{
//fibnum=F1(n-3)+3*F1(n-4)+3*F1(n-5)+F1(n-6);
int countCPU=4;
Task[]tasks=新任务[countCPU];
对于(var j=0;j
{
var data=pp作为CustomData;if(data==null)返回;
data.TResult=F1(n-data.TNum-3);
},
新的CustomData(){TNum=j});
Task.WaitAll(任务);
fibnum=(任务[0]。异步状态作为CustomData)。TResult+3*(任务[1]。异步状态作为CustomData)。TResult+3*(任务[2]。异步状态作为CustomData)。TResult+(任务[3]。异步状态作为CustomData)。TResult;
}
返回fibnum;
}

对于这样一个简单的任务来说,并行化的开销相当高,因此您肯定不想天真地并行化每个迭代。然而,示例任务相当复杂,不仅要将递归函数更改为(某种程度上)并行化的递归函数,还要找到额外的并行化选项以获得四个独立的worker。由于使用过时的多线程结构,它也变得更加复杂。考虑一个更简单的例子:

static int F2(int n)
{
  if (n <= 1) return 1;

  var a = Task.Run(() => F1(n - 1));
  var b = Task.Run(() => F1(n - 2));

  return a.Result + b.Result;
}
但我们希望将其与第二次迭代合并,以允许四路并行化。这很简单,只需将
F1(n)
替换为
F1(n-1)+F1(n-2)

可以简化为

return F1(n - 2) + F1(n - 3) + F1(n - 3) + F1(n - 4);
进一步

return F1(n - 2) + 2 * F1(n - 3) + F1(n - 4);
哎呀!我们失去了一根树枝。因此,我们实际上需要另一种替代品:

return 
  F1((n - 2) - 1) + F1((n - 2) - 2)
  + 2 * (F1((n - 3) - 1) + F1((n - 3) - 2))
  + F1((n - 4) - 1) + F1((n - 4) - 2);
结果是

return
 F1(n - 3) + F1(n - 4)
 + 2 * F1(n - 4) + 2 * F1(n - 5)
 + F1(n - 5) + F1(n - 6);
最终我们找到了我们的四个分支:

return
 F1(n - 3) + 3 * F1(n - 4) + 3 * F1(n - 5) + F1(n - 6);
这些分支中的每一个都可以简单地并行运行,并且您可以很好地利用所有四个线程


现在,您可以对
G1
应用相同的推理,并获得特定递归函数的四路并行化:)

@Luaan这是我的解决方案,使用两个线程。我不知道这是否正确,但是顺序和并行算法的结果是一致的,但是时间上几乎没有减少,也许需要使用更多的线程

private static int G2(int k, int r)
{
    if (k == 0 || r == 0) return 0;

    if (s[k - 1] > r) // this part gives the wrong results :(
    {
        Task<int> task1 = Task.Run(() => G1(k - 2, r));
        Task<int> task2 = Task.Run(() => G1(k - 3, r));
        return task1.Result + task2.Result;
    }

    Task<int> max1 = Task.Run(() => G1(k - 1, r));
    Task<int> max2 = Task.Run(() => p[k - 1] + G1(k - 1, r - s[k - 1]));

    return Math.Max(max1.Result, max2.Result);
}
私有静态int G2(int k,int r)
{
如果(k==0 | | r==0)返回0;
如果(s[k-1]>r)//这部分给出了错误的结果:(
{
task1=Task.Run(()=>G1(k-2,r));
tasktask2=Task.Run(()=>G1(k-3,r));
返回task1.Result+task2.Result;
}
taskmax1=Task.Run(()=>G1(k-1,r));
taskmax2=Task.Run(()=>p[k-1]+G1(k-1,r-s[k-1]);
返回Math.Max(max1.Result,max2.Result);
}

如果(s[k-1]>r)block给出了错误的结果:(

方法中的p和s是什么?这些是随机生成的整数数组,数组大小是k(原始)@PanagiotisKanavos实际上,请注意,并行化只在“第一次迭代”中完成-它启动了四个独立的非平凡任务,因此对于足够大的
n
,您将获得非常接近完美的效率。为第二次练习复制相同的内容将需要相同的数学洞察力(允许几乎相等的工作负载分区)。但是如果
G1(k-1,r)
的工作负载与G1类似(k-1,r-s[k-1]),最简单的并行化至少会给两个线程一些工作要做。检查。斐波那契不能并行化,这段代码就是一个不应该对任务做什么的例子——手动创建任务,使用硬编码的核数阻塞任务。@Luaan我在尝试破译代码后意识到了这一点。使用Parallel.ForEac使用本地状态的h重载将简单得多,而且可能更快。这对我来说更清楚,但我不知道如何合并G1(k-1,r)还有其他的迭代。我对Math.Max完全迷茫了,我该如何替换它呢?我可能可以将Math.Max参数分成两个分支,这可以帮助我用至少两个线程来并行化算法。但是我仍然不知道如何将G1(k-1,r)分成不同的任务。
private static int G2(int k,int r){if(k==0 | r==0)返回0;如果(s[k-1]>r){return G1(k-1,r);}Task max1=Task.Run(()=>G1(k-1,r));Task max2=Task.Run(()=>p[k-1]+G1(k-1,r-s[k-1]);返回Math.Max(max1.Result,max2.Result)}
@PetroCepura用“body”替换函数“call”函数的一部分;对结果起作用的运算符是什么并不重要。对于
Max(a,b)
Plus(a,b)的替换效果完全相同
-你在
a
b
中替换
G1
,这周围发生的一切都无关紧要。@PetroCepura尝试按照
F1
示例的推理进行操作;完全相同的逻辑适用于
G1
。恐怕我不能在不给出答案的情况下给你更多的提示,这是很重要的er-productive:)
返回G1(k-1,r)
返回G1((k-1)-1,r)+G1((k-2)-1,r)Math.Max部分是正确的,但是
if(s[k-1]>r
return
 F1(n - 3) + 3 * F1(n - 4) + 3 * F1(n - 5) + F1(n - 6);
private static int G2(int k, int r)
{
    if (k == 0 || r == 0) return 0;

    if (s[k - 1] > r) // this part gives the wrong results :(
    {
        Task<int> task1 = Task.Run(() => G1(k - 2, r));
        Task<int> task2 = Task.Run(() => G1(k - 3, r));
        return task1.Result + task2.Result;
    }

    Task<int> max1 = Task.Run(() => G1(k - 1, r));
    Task<int> max2 = Task.Run(() => p[k - 1] + G1(k - 1, r - s[k - 1]));

    return Math.Max(max1.Result, max2.Result);
}