2010-12-21 4 views
5

ich aus den folgenden Collatz Conjecture Algorithmus konvertieren Ich versuche:Parallel.For in 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; 
     } 
    } 

statt, um das .NET 4.0 Parallel.For zu verwenden:

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

ich das Gefühl, ich habe Ich bin nicht zu weit davon entfernt, berühmte letzte Worte.

Vielen Dank im Voraus.

Antwort

9

Ihr Problem ist, dass Sie Parallel.For in diesem Fall nicht verwenden möchten, da Sie bereits ein Array (nums) durchlaufen haben, das Parallel.ForEach aufruft. Jedoch wird das Array mit Enumerable.Range erstellt und Sie eigentlich nicht für alles benutzen, so dass der beste Weg, es zu tun ist, mit AsParallel und 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; 
    } 
} 

Diese Methode wird etwa doppelt so schnell auf mein Computer als serielle Implementierung. Beachten Sie auch, dass ich die Hauptschleife so optimiert habe, dass sie die Berechnung inline durchführt, anstatt eine Funktion aufzurufen, und sie bitweise Mathematik anstelle von Multiplizieren und Dividieren verwendet. Damit war es wieder doppelt so schnell. Die Kompilierung für x64 anstelle von x86 hat es auch mehr als doppelt so schnell gemacht.

+0

Wenn ich dies ausführen, bekomme ich nicht unbedingt den kleinsten Index, der die längste Collatz-Kette ergibt (dh für 1 bis 1500000 gibt die serielle Methode 1117065 und die LINQ-Methode 1126015, die beide eine Kettenlänge von 528 haben). Da ich gerade LINQ lerne, gibt es eine einfache Möglichkeit, den '.Aggregate'-Aufruf zu ändern, um das zu beheben? – chezy525

+0

Ich bekomme beide Antworten, irgendwie, wenn ich debuggen bekomme ich (1117065, 1126015) bei verschiedenen Gelegenheiten. Idealerweise hätte ich gerne den Mindestindex. Danke im Voraus. – Seany84

+1

Nachdem ich damit ein wenig herumgespielt habe, denke ich, dass Sie nur die Bedingungserklärung im '.Aggregate' ändern müssen. d. h. 'max.len cur.key) ' – chezy525