Pergunta

Codifiquei um programa em C# para encontrar números perfeitos em um determinado intervalo como parte de um desafio de programação. No entanto, percebi que é muito lento ao calcular números perfeitos mais de 10000. Existem métodos de otimização que existem para encontrar números perfeitos? Meu código é o seguinte:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleTest
{
 class Program
 {
  public static List<int> FindDivisors(int inputNo)
  {
   List<int> Divisors = new List<int>();
   for (int i = 1; i<inputNo; i++)
   {
    if (inputNo%i==0)
     Divisors.Add(i);
   }
   return Divisors;
  }

  public static void Main(string[] args)
  { 
   const int limit = 100000;

   List<int> PerfectNumbers = new List<int>();
   List<int> Divisors=new List<int>();
   for (int i=1; i<limit; i++)
   {
    Divisors = FindDivisors(i);
    if (i==Divisors.Sum())
     PerfectNumbers.Add(i);
   }

   Console.Write("Output =");

   for (int i=0; i<PerfectNumbers.Count; i++)
   {
    Console.Write(" {0} ",PerfectNumbers[i]);
   }

   Console.Write("\n\n\nPress any key to continue . . . ");
   Console.ReadKey(true);
  }
 }
} 
Foi útil?

Solução

Use a fórmula

testPerfect = 2N-1(2n - 1)

Para gerar possibilidades, verifique se o número é de fato perfeito.

Experimente isso para uma leitura de hora de dormir

Outras dicas

Os números perfeitos mudam? Não. Olhe aqui. Certamente, eles devem ser calculados uma vez e depois armazenados. No seu caso, os únicos resultados serão

6
28
496
8128

O próximo é 33550336. Fora do seu alcance.

Apenas o óbvio de mim: você não precisa verificar todos os divisores. Não há sentido em procurar divisores passados inputNo/2. Isso reduz metade dos cálculos, mas essa não é uma ordem de magnitude mais rápida.

Uma maneira de resolver coisas como essa envolve a construção de uma enorme variedade em memória de todos os números e, em seguida, cruzar os números.

Se você ainda está procurando algo para calcular números perfeitos. Isso passa pelos primeiros dez mil rapidamente, mas o número de 33 milhões é um pouco mais lento.

public class Perfect {
private static Perfect INSTANCE = new Perfect();

public static Perfect getInstance() {
    return INSTANCE;
}

/**
 * the method that determines if a number is perfect;
 * 
 * @param n
 * @return
 */
public boolean isPerfect(long n) {
    long i = 0;
    long value = 0;
    while(++i<n){
        value = (0 == n%i?value+i:value);
    }
    return n==value;
}
}

Para qualquer pessoa interessada em uma abordagem baseada em LINQ, o método a seguir funcionou muito bem e com eficiência para mim para determinar se um valor inteiro fornecido por um chamador é ou não um número perfeito.

bool IsPerfectNumber(int value)
{
    var isPerfect = false;

    int maxCheck = Convert.ToInt32(Math.Sqrt(value));
    int[] possibleDivisors = Enumerable.Range(1, maxCheck).ToArray();
    int[] properDivisors = possibleDivisors.Where(d => (value % d == 0)).Select(d => d).ToArray();
    int divisorsSum = properDivisors.Sum();

    if (IsPrime(divisorsSum))
    {
        int lastDivisor = properDivisors.Last();
        isPerfect = (value == (lastDivisor * divisorsSum));
    }

    return isPerfect;
}

Para simplificar e clareza, minha implementação para ISPrime (), que é usada no isperFectNumber (), é omitida.

Para continuar com a resposta de Charles Gargent, há uma maneira muito rápida de verificar se um número de Mersenne, também conhecido como 2^n - 1, é o Prime. É chamado de Teste de Lucas-LehmerO pseudocódigo básico (retirado da página da Wikipedia) é:

// Determine if Mp = 2p − 1 is prime for p > 2
Lucas–Lehmer(p)
    var s = 4
    var M = 2p − 1
    repeat p − 2 times:
        s = ((s × s) − 2) mod M
    if s == 0 return PRIME else return COMPOSITE
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top