Programa que conte numeros triangulares

Para somar os dígitos, converta o número para uma string.

O programa a seguir exige um pouco de matemática. Ele roda em 0,348 ms em uma máquina Core2Duo 1.86 GHz.

[code]
import java.util.Map;
import java.util.HashMap;
class Problema12ProjectEuler {

private Map<Integer,Integer> nDiv = new HashMap<Integer,Integer>();
// Bastante ingênuo mas um pouquinho otimizado...
private int nDivisores (int n) {
    if (nDiv.containsKey (Integer.valueOf (n))) 
        return nDiv.get (n);
    else {
        int resultado = 1;
        for (int i = 2; i <= n; ++i) {
            if (n % i == 0)
                resultado++;
        }
        nDiv.put (n, resultado);
        return resultado;
    }
}
public int nDivisoresTriangular;
public int nTriangular (int nMinDivisores) {
    int n = 1;
    nDivisoresTriangular = 0;
    do {
        n++;
        if (n % 2 == 0) {
            nDivisoresTriangular = nDivisores (n / 2) * nDivisores (n+1);
        } else {
            nDivisoresTriangular = nDivisores (n) * nDivisores ((n+1) / 2);
        }
    } while (nDivisoresTriangular < nMinDivisores);
    return n * (n + 1) / 2;
}
public static void main(String[] args) {
    Problema12ProjectEuler e = new Problema12ProjectEuler();
    // Note que um número triangular n(n+1)/2 é composto de 2 números, n e n+1/2. 
// Devemos então determinar o número de divisores de n, de n+1/2, e então multiplicá-los. 
    // Isso dará o número de divisores de n(n+1)/2.

    System.out.println ("O numero de divisores do numero triangular " + e.nTriangular (5) + " eh " + 

e.nDivisoresTriangular);
long t = System.currentTimeMillis();
int resultado = e.nTriangular (500);
t = System.currentTimeMillis() - t;
System.out.println (“O numero de divisores do numero triangular " +
resultado + " eh " + e.nDivisoresTriangular + " e foi calculado em " + t + " ms”);
}
}[/code]

Por que você testa se o número é par dentro do while?
Quer dizer, vi que vc só divide o lado par da equação por 2.

Seria por precisão? Performance? Ou algum outro motivo que não visualizei?

Ah sim, claro, pq vc chama nDivisores, que tem como argumento um int.

A matemática que é necessário saber é :

  • Se dois ou mais números são primos entre si, o número de divisores do seu produto é igual ao produto dos números de divisores de cada um;
  • n e n+1 são primos entre si. Se n for par, isso implica em n/2 e n+1 serem primos entre si.

Como o thingol eu percebi que pra encontrar um número triangular é só usar a fórmula da soma dos termos de uma PA, mas não sabia da fórmula para achar o número de divisores de um número baseado nos expoentes de seus fatores. Com tal ajuda, fiz um programinha que ficou bastante parecido com o do thingol mas que, acreditem ou não, foi terminado antes dele postar o seu.

Aqui após 10 execuções o seu ficou com 359 ms de média e o meu com 274 ms. Entre a minha micro-vantagem no tempo e organização do seu código, eu fico com o seu. Fica como desafio pra alguém que queira otimizar mais ainda. O meu pode se alguém achar um jeito de evitar o Map e fazer a multiplicação da soma dos expoentes no mesmo loop.

[code]import java.util.HashMap;
import java.util.Map;

public class Divisor {
public static void main(String[] args) {
long ini = System.currentTimeMillis();
int d = 0;
int n = 1;
int nt = 2;
while (d < 500) {
nt = (n * (1 + n))/2;
d = numeroDeDivisores(nt);
n++;
}
System.out.println(nt + “\t” + d);
long fim = System.currentTimeMillis();
System.out.println("TEMPO: " + (fim - ini));
}

public static int numeroDeDivisores(int n) {
int divisores = 1;
Map<Integer, Integer> somas = new HashMap<Integer, Integer>();

  while (n > 1) {
     for (int i = 2; i <= n; i++) {
        if (n % i == 0) {
           if (somas.containsKey(i)) {
              somas.put(i, somas.get(i) + 1);
           } else {
              somas.put(i, 2);
           }
           n /= i;
           break;
        }
     }
  }
  for (Integer i : somas.values()) {
     divisores *= i;
  }
  return divisores;

}
}[/code]

O meu programa é mais lento que o do Ozix, obviamente porque o cálculo do número de divisores dele é mais otimizado.
Se juntar os dois programas deve ficar algo mais rápido ainda.

Juntei o do Ozix com o meu e deu 109 ms. Muito legal.

[code]
import java.util.Map;
import java.util.HashMap;
class Problema12ProjectEuler {

Map<Integer,Integer> nDiv = new HashMap<Integer,Integer>();
// Bastante ingênuo mas um pouquinho otimizado...

/*
private int nDivisores (int n) {
if (nDiv.containsKey (Integer.valueOf (n)))
return nDiv.get (n);
else {
int resultado = 1;
for (int i = 2; i <= n; ++i) {
if (n % i == 0)
resultado++;
}
nDiv.put (n, resultado);
return resultado;
}
}
*/
public int nDivisores (int n) {
if (nDiv.containsKey (Integer.valueOf (n)))
return nDiv.get (n);
else {
int m = n;
int divisores = 1;
Map<Integer, Integer> somas = new HashMap<Integer, Integer>();

       while (n > 1) {  
          for (int i = 2; i <= n; i++) {  
             if (n % i == 0) {  
                if (somas.containsKey(i)) {  
                   somas.put(i, somas.get(i) + 1);  
                } else {  
                   somas.put(i, 2);  
                }  
               n /= i;  
                break;  
             }  
          }  
       }
       for (Integer i : somas.values()) {  
          divisores *= i;  
       }  
       nDiv.put (m, divisores);
       return divisores;  
   }  
}
public int nDivisoresTriangular;
public int nTriangular (int nMinDivisores) {
    int n = 1;
    nDivisoresTriangular = 0;
    do {
        n++;
        if (n % 2 == 0) {
            nDivisoresTriangular = nDivisores (n / 2) * nDivisores (n+1);
        } else {
            nDivisoresTriangular = nDivisores (n) * nDivisores ((n+1) / 2);
        }
    } while (nDivisoresTriangular < nMinDivisores);
    return n * (n + 1) / 2;
}
public static void main(String[] args) {
    Problema12ProjectEuler e = new Problema12ProjectEuler();
    // Note que um número triangular n(n+1)/2 é composto de 2 números, n e n+1/2. 
// Devemos então determinar o número de divisores de n, de n+1/2, e então multiplicá-los. 
    // Isso dará o número de divisores de n(n+1)/2.

    System.out.println ("O numero de divisores do numero triangular " + e.nTriangular (5) + " eh " + 

e.nDivisoresTriangular);
long t = System.currentTimeMillis();
int resultado = e.nTriangular (500);
t = System.currentTimeMillis() - t;
System.out.println (“O numero de divisores do numero triangular " +
resultado + " eh " + e.nDivisoresTriangular + " e foi calculado em " + t + " ms”);
}
}[/code]

Eu só faria uma alteração.

O que é mais rápido: fazer uma divisão ou deslocar um bit?
Calcular o módulo de uma divisão ou fazer um E a nível de bit?

Creio que a segunda opção em cada caso, não?

Que tal, ao invés de dividir por dois, deslocar um bit para a direita?

return n * ((n + 1) >> 1);

E fazer um E lógico?

if (((n & 1) == 0) {

Claro que são mudanças sutis (quase insignificativas quando rodando nas máquinas atuais), mas em grandes laços podem fazer alguma diferença. :wink:

Marco, eu fiz as modificações sugeridas, e não vi diferença alguma. Acho que é porque a JVM já faz essas otimizações (trocar divisões por shifts se for o caso) transparentemente.

Legal. Mais uma vez, a colaboração vence. Modifiquei o método nDivisores() para não usar Map. Imaginei que não usando objetos seria mais rápido, mas aqui não houve melhora no desempenho:

[code] public int nDivisores(int n) {
int divisores = 1;
int fator = 2;

    while (n > 1) {
        int soma = 1;
        int t = n % fator;
        while (t == 0) {
            soma++;
            n /= fator;
            t = n % fator;
        }
        divisores *= soma;
        fator++;
    }
    return divisores;
}[/code]

Acabei de perceber que o “t” ali em cima está à toa. Agora sim, melhorou um pouquinho:

[code] public int nDivisores(int n) {
int divisores = 1;
int fator = 2;

    while (n > 1) {
        int soma = 1;
        while (n % fator == 0) {
            soma++;
            n /= fator;
        }
        divisores *= soma;
        fator++;
    }
    return divisores;
}[/code]

no caso do outro problema… de somar os digitos do fatoria de um numero…

para fazer o fatorial é simples… ja fiz, depois tem que converter para string… pode ser através do
String.valueOf(x); ?? sendo que x é o resultado do fatorial…

e para soma os digitos??
digamos que é 5!.. o resultado é 120

eu tenho que fazer 1+2+0

Você fez direitinho o fatorial? 100! é um número de 157 dígitos, e começa por 9332621544394415268169923885626…

Se não for esse número então você não fez direito o fatorial.

EDIT - A propósito, se seu programa funcionar direitinho, a resposta para “soma dos dígitos” é 648. Só para você conferir se você fez tudo direitinho mesmo.

Ainda no problema anterior, usando um array com os 1476 primeiros números primos (eu sei, é bem específico pra esse problema) eu consegui rodar o programa em 15 ms! Sem o array consegui fazer um tempo de 72 ms.

que ferramenta vcs utilizam para medir a velocidade de execução utilizo o netbeans e o tempo de execução so calcula a partir de segundos

Use System.currentTimeMillis () ou System.nanoTime().