2010-12-21 62 views
5

我试图从转换下面考拉兹猜想算法:的Parallel.For在C#

public class CollatzConjexture 
    { 
     public static int Calculate(int StartIndex, int MaxSequence) 
     { 
      int ChainLength = 0; 
      int key = 0; 
      bool ContinuteCalulating = true; 
      int LongestChain = 0; 
      Int64 Remainder = 0; 

      for (int i = StartIndex; i <= MaxSequence; i++) 
      { 
       ChainLength = 1; 
       Remainder = i; 
       ContinuteCalulating = true; 

       while (ContinuteCalulating) 
       { 
        Remainder = CalculateCollatzConjecture(Remainder); 
        if (Remainder == 1) 
         ContinuteCalulating = false; 

        ChainLength += 1; 
       } 

       if (ChainLength > LongestChain) 
       { 
        LongestChain = ChainLength; 
        key = i; 
       } 
      } 

      return key; 
     } 

     private static Int64 CalculateCollatzConjecture(Int64 Number) 
     { 
      if (Number % 2 == 0) 
       return Number/2; 
      else 
       return (3 * Number) + 1; 
     } 
    } 

改为使用.NET 4.0的Parallel.For:

int ChainLength = 0; 
      int key = 0; 
      bool ContinuteCalulating = true; 
      int LongestChain = 0; 
      Int64 Remainder = 0; 

      int[] nums = Enumerable.Range(1, 1500000).ToArray(); 
      long total = 0; 

      // Use type parameter to make subtotal a long, not an int 
      Parallel.For<int>(1, nums.Length,() => 1, (j, loop, subtotal) => 
      { 
       Remainder = j; 

       while (ContinuteCalulating) 
       { 
        Remainder = CalculateCollatzConjecture(Remainder); 
        if (Remainder == 1) 
         ContinuteCalulating = false; 

        ChainLength += 1; 
       } 

       if (ChainLength > LongestChain) 
       { 
        LongestChain = ChainLength; 
        key = j; 
       } 
       return key; 


      }, 
       (x) => Interlocked.Add(ref key, x) 
      ); 

我有一种感觉,我”离它不太远,有名的遗言。

在此先感谢。

回答

9

您的问题是,您不希望在此实例中使用Parallel.For,因为您已经有一个要迭代的数组(nums),这需要Parallel.ForEach。然而,你的阵列与Enumerable.Range创建和你实际上并不使用它的任何东西,所以最好的方式做到这一点是AsParallel和LINQ(PLINQ):

public static class CollatzConjexture2 
{ 
    public static int Calculate(int StartIndex, int MaxSequence) 
    { 
     var nums = Enumerable.Range(StartIndex, MaxSequence); 
     return nums.AsParallel() 
        // compute length of chain for each number 
        .Select(n => new { key = n, len = CollatzChainLength(n) }) 
        // find longest chain 
        .Aggregate((max, cur) => cur.len > max.len || 
        // make sure we have lowest key for longest chain 
         max.len == cur.len && cur.key < max.key ? cur : max) 
        // get number that produced longest chain 
        .key; 
    } 

    private static int CollatzChainLength(Int64 Number) 
    { 
     int chainLength; 
     for (chainLength = 1; Number != 1; chainLength++) 
      Number = (Number & 1) == 0 ? Number >> 1 : Number * 3 + 1; 
     return chainLength; 
    } 
} 

此方法两次快速上我的电脑作为串行执行。另外请注意,我优化了主循环,以便它进行内联计算而不是调用函数,并且它使用按位数学计算而不是乘法和除法。这使它再次快了两倍。将它编译为x64而不是x86也使其速度提高了一倍以上。

+0

当我运行这个时,我不一定会得到最长的Collat​​z链的最小索引(即对于1到1500000,串行方法返回1117065和LINQ方法1126015,两者的链长都是528 )。正如我刚刚学习LINQ,有没有一种简单的方法来修改`.Aggregate`调用来解决这个问题? – chezy525 2010-12-21 18:41:57