2010-12-21 2 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.ForEach을 호출하는 반복 () 배열이 있으므로이 인스턴스에서는 Parallel.For을 사용하고 싶지 않습니다. 약 두 배 빠른에

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; 
    } 
} 

이 방법은 그러나, 배열은 Enumerable.Range 만들어집니다 당신은 실제로 아무것도 사용하지 않기 때문에 가장 좋은 방법은 AsParallel 및 LINQ (PLINQ) 함께 할 직렬 구현으로 내 컴퓨터. 또한 주 루프를 최적화하여 함수를 호출하는 대신 계산을 인라인하고 곱셈 및 나눗셈 대신 비트 연산을 사용함에 유의하십시오. 이것으로 약 2 배 빠릅니다. x86 대신 x64 용으로 컴파일하면 두 배 이상 빠릅니다.

+0

실행할 때 가장 긴 Collatz 체인을 생성하는 가장 작은 인덱스를 얻을 필요는 없습니다 (즉, 1에서 1500000, 직렬 메서드는 1117065 및 LINQ 메서드 1126015를 반환하며 둘 다 체인 길이가 528입니다.). LINQ를 배우는 중이므로'.Aggregate' 호출을 수정하여이 문제를 해결할 수있는 간단한 방법이 있습니까? – chezy525

+0

어떻게 든 디버깅 할 때 (1117065, 1126015) 별도의 경우에 두 가지 대답을 얻고 있습니다. 이상적으로, 나는 최소 지수를 원합니다. 미리 감사드립니다. – Seany84

+1

이것으로 조금 놀아본 후'.Aggregate'에서 조건문을 변경하기 만하면된다고 생각합니다. 즉,'max.len cur.key)' – chezy525