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


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

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.

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.


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.


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.


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.


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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.