2010-12-21 8 views
5

Estoy intentando convertir el algoritmo siguiente conjetura de Collatz de:Parallel.For en 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; 
     } 
    } 

Para utilizar en su lugar el Parallel.For .NET 4.0:

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

tengo la sensación de que' No estoy muy lejos de eso, famosas últimas palabras.

Gracias de antemano.

Respuesta

9

Su problema es que no desea utilizar Parallel.For en este caso porque ya tiene una matriz (nums) para iterar, que llama a Parallel.ForEach. Sin embargo, la matriz se crea con Enumerable.Range y que en realidad no lo utilizan para nada, por lo que la mejor manera de hacerlo es con AsParallel y 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; 
    } 
} 

Este método es aproximadamente el doble de rápido en mi computadora como la implementación en serie. También tenga en cuenta que optimicé el bucle principal para que haga el cálculo en línea en lugar de llamar a una función y usa matemáticas en modo bit en lugar de multiplicar y dividir. Esto hizo que el doble de rápido otra vez. Compilarlo para x64 en vez de x86 también lo hizo más del doble de rápido.

+0

Cuando ejecuto esto, no necesariamente obtengo el índice más pequeño que produce la cadena Collatz más larga (es decir, de 1 a 1500000, el método serial devuelve 1117065 y el método LINQ 1126015, ambos con una longitud de cadena de 528) Como recién estoy aprendiendo LINQ, ¿hay alguna manera simple de modificar la llamada '.Aggregate' para arreglar esto? – chezy525

+0

Estoy obteniendo ambas respuestas, de alguna manera, cuando depuro me sale (1117065, 1126015) en ocasiones separadas. Idealmente, me gustaría el índice mínimo. Gracias por adelantado. – Seany84

+1

Después de jugar con esto un poco, creo que solo necesitas cambiar la declaración de condición en el '.Agregado'. es decir, 'max.len cur.key) ' – chezy525