Qual seria um algoritmo apropriado para fatorar números na faixa de alguns bilhões?

8

No momento, estou aprendendo Python e me dando motivos para aplicar o que estou aprendendo. Estou tendo problemas com alguns dos problemas do Project Euler

Atualmente, estou no número 3, que é determinar o fator primordial mais alto desse número.

Deduzi que provavelmente preciso ter dois algoritmos, um para determinar a primalidade, e o segundo que envolveria a descoberta de fatores do número.

Então, eu tenho lido sobre artigos da Wiki . Tentando determinar qual pode ser o melhor algoritmo a ser usado e como fazê-lo.

Mas já faz um tempo desde que eu fiz alguma programação hardcore baseada em matemática e estou lutando para começar em algum lugar.

Eu estava olhando para usar o método de fatoração de Fermat com a inclusão de Trial by Division, mas não quero complicar demais. Não quero quebrar o RSA. Quero apenas dois algoritmos adequados para o meu problema, e aí está a minha pergunta.

Quais algoritmos você usaria para testar a primalidade / fatoração de um número adequado ao problema em questão?

Editar

Obrigado a todos por suas respostas e idéias, eles foram muito úteis. Eu votei em todos os que foram úteis, seja por conselhos ou por meio das próprias experiências de Euler. O que eu marquei como certo foi simplesmente o mais útil, pois me deu um lugar adequado para começar, do qual foi um empurrão na direção certa. Obrigado novamente =)

Chris
fonte
Esses problemas podem usar melhor o processamento paralelo.
NoChance
Talvez você esteja certo em geral, mas para o projeto euler é geralmente mais importante encontrar um algoritmo "inteligente". Eles são muito mais rápidos que as abordagens de força bruta em paralelo.
sebastiangeiger
Este é um problema matematicamente difícil e você não encontrará a solução ideal .
DeadMG

Respostas:

5

Minha abordagem para esses problemas geralmente é a seguinte: construa o algoritmo mais simples possível para resolvê-lo, que geralmente é uma abordagem ingênua de força bruta e, em seguida, teste / calcule matematicamente se é muito lento ou não. Na maioria das vezes é bom o suficiente. Quando não é, você tem um ponto de partida claro para trabalhar e otimizar as coisas até que o algoritmo seja eficiente o suficiente.

Aqui está um algoritmo simples para resolver o Problema 3 no Projeto Euler (em C, mas a tradução para o Python deve ser trivial):

#include <stdio.h>
#include <math.h>

int isPrime(int n){
    int i;

    if (n==2)
        return 1;

    if (n%2==0)
        return 0;
    for (i=3;i<sqrt(n);i+=2)
        if (n%i==0)
            return 0;
    return 1;
}

int main(){
    long long int n = 600851475143;
    int i = 3;

    while (i<50000){
        if (isPrime(i))
            if (n%i==0)
                printf("%d\n",i);
        i+=2;
    }
    return 0;
}
Daniel Scocco
fonte
1
Eu diria que usar isPrimeé um exagero. Basta fazer um n/=2tempo n%2==0e depois começar icom 3 e depois fazer um loop if (n%i==0) n/=i; else i+=2;é suficiente (bem, pode ser interrompido uma vez i*i > n).
usar o seguinte
1
Minha experiência na solução de projetos euler é que essa abordagem está funcionando para os problemas anteriores, mas você provavelmente precisará refine-la ao resolver problemas mais complicados.
sebastiangeiger
@Sebastian, estou com o problema 73 e, no caminho, as abordagens mais ingênuas param de funcionar. Mas ei, por que complicar as coisas antes que você precise?
Daniel Scocco
2
@ daniels: Eu posso estar sendo burro aqui. Como isso resolve o problema? a) 50000 parece terrivelmente arbitrário. b) Você está imprimindo todos os fatores principais, não apenas os mais altos. c) Verificar se é primo antes de verificar se é um fator parece um desperdício. d) 2 é um número primo.
Pd
@pdr, a) os fatores principais não aumentam tão rápido, então imaginei que 50000 seriam grandes o suficiente. Caso contrário, você sempre pode repetir com 100000 ou sqrt (n). b) certo, o que significa que você só precisa ver o último impresso (imprimi tudo apenas para ver o que estava acontecendo). c) Concordo que a inversão dos testes seria mais eficiente, mas não faz diferença neste caso (ou seja, você ganharia alguns milissegundos) d) sim, esqueci esse caso especial na minha função isPrime ().
Daniel Scocco
4

Vale a pena escrever algum código que fatorize e encontre o primeiro (basicamente a mesma coisa), porque você provavelmente o reutilizará em muitas outras perguntas de Euler. Você poderá melhorar o código para perguntas posteriores e, talvez, examinar testes de primalidade não exaustivos, se achar que não é mais eficiente o suficiente, por isso sugiro que a abordagem mais simples no momento seja:

  • Escreva um loop simples que encontre todos os números primos (por exemplo, para cada número, teste sua divisibilidade por cada primo encontrado anteriormente e, se todos falharem, adicione-o à lista de primos).
  • Tente dividir o número que você está tentando fatorar por cada primo até a raiz quadrada do número.
Jack V.
fonte
4

Na verdade, esta é uma área de pesquisa ativa em Matemática e Ciência da Computação. O artigo da wikipedia oferece uma boa visão geral:

http://en.wikipedia.org/wiki/Integer_factorization

Escolha qualquer algoritmo que você goste / ache interessante e experimente.

Você provavelmente terá que fazer uma troca: a maioria dos algoritmos "bons" exige um pouco de experiência em matemática para realmente entender (embora você possa implementá-los sem entendê-los completamente).

Se você não sabe por onde começar, recomendo a peneira quadrática:

http://en.wikipedia.org/wiki/Quadratic_sieve

Não requer conhecimentos matemáticos insanos, mas funciona bem.

sleske
fonte
2

Eu resolvi alguns problemas do ProjectEuler há algum tempo em Ruby usando a divisão de teste com números primos .

Eu descobri que gerar os números primos era muito mais crítico do que o algoritmo de fatoração real. Assim que substituí minha abordagem ingênua de geração de números primos por uma peneira, meus tempos de execução diminuíram para uma quantidade razoável.

sebastiangeiger
fonte
1

Mantendo-o muito simples ...

Encontrando os fatores de X: eu começaria (n) em 2 e trabalharia até o número inteiro (piso, não arredondado) da raiz quadrada de X. Se dividir X por n, produzimos Y e Y é um número inteiro, ambos n e Y são fatores. Os valores mais baixos de n produzirão os valores mais altos de Y.

Primalidade de Y: Novamente, faça um loop (m) de 2 à raiz quadrada de Y e veja se Y / m é um número inteiro. Se for, então Y não é primo. Volte para encontrar outro fator.

Se m atinge a raiz de Y, você tem o seu número primo. Pare de olhar. Y é a resposta.

Se n atingir a raiz do X, não haverá fatores primos.

pdr
fonte
0

Como já existe uma solução completa, vou postar esta Haskell ...

--  Problem is to find the largest prime factor of 600851475143
module Factor (testval, bigfactor) where
  testval = 600851475143

  bf' :: Integer -> Integer -> Integer

  bf' f x | (f == x)         = f
          | ((mod x f) == 0) = bf' f (div x f)
          | True             = bf' (f+1) x

  bigfactor :: Integer -> Integer

  bigfactor x | (x <  1) = error "Parameter less than 1"
              | (x == 1) = 1
              | True     = bf' 2 x

Basicamente, não há necessidade de testar a primalidade. Se você dividir os fatores que encontrar (e se certificar de lidar com os fatores de repetição), nunca poderá ocorrer um fator não primário, porque um fator não primário é um produto de fatores primos menores.

Carregou e executou isso com o GHCi - é instantâneo e agora tenho um total geral de 4 (sim, quatro!) Problemas de Euler resolvidos.

Steve314
fonte
0

Também estou moldando meu conhecimento em Python e também comecei a responder aos problemas do Project Euler no meu repositório do github: https://github.com/rentes/Euler .

Para o Problema 3, programei uma solução simples, baseada nas seguintes premissas:

1) dado um número inteiro positivo n, começo a dividi-lo por 2, 3, ..., m, e se eu achar que m é um fator primordial, adiciono-o a uma lista. Não estou adicionando à lista múltiplos de fatores primos já descobertos. Por exemplo, 4 é um múltiplo de 2, portanto 4 não é adicionado a esta lista.

2) Multiplico cada primo da lista para ver se é igual a n. Se iguais, encontramos todos os fatores primos de n. Caso contrário, continue dividindo n pelo próximo número m, até que todos os fatores primos sejam iguais a n ou m atinja n.

Consulte https://github.com/rentes/Euler/blob/master/problem3.py para obter mais detalhes. Adicionei comentários que ajudarão você a entender o que eu programei. É uma solução simples e tenho certeza de que não é a solução mais rápida, mas funciona e é simples o suficiente para entender.

Cumprimentos

Miguel Rentes
fonte