Um amigo meu está entrevistando para um emprego. Uma das perguntas da entrevista me fez pensar, só queria um feedback.
Existem 2 números inteiros não negativos: iej. Dada a seguinte equação, encontre uma solução (ideal) para iterar sobre iej de maneira que a saída seja classificada.
2^i * 5^j
Portanto, as primeiras rodadas seriam assim:
2^0 * 5^0 = 1
2^1 * 5^0 = 2
2^2 * 5^0 = 4
2^0 * 5^1 = 5
2^3 * 5^0 = 8
2^1 * 5^1 = 10
2^4 * 5^0 = 16
2^2 * 5^1 = 20
2^0 * 5^2 = 25
Por mais que eu tente, não consigo ver um padrão. Seus pensamentos?
algorithm
optimization
hamming-numbers
smooth-numbers
Chris Eberle
fonte
fonte
2^2 < 5
mas2^3 > 5
nesse ponto você aumenta j. Eu acho que você pode produzir a saída em O (n) ao invés de O (nlgn). @ tom-zynch dois loops aninhados são O (n ^ 2). Esta questão é muito válidaRespostas:
Dijkstra deriva uma solução eloquente em "Uma disciplina de programação". Ele atribui o problema a Hamming. Aqui está minha implementação da solução da Dijkstra.
fonte
aqui está uma maneira mais refinada de fazê-lo (mais refinado do que minha resposta anterior, ou seja):
imagine que os números são colocados em uma matriz:
o que você precisa fazer é 'caminhar' nessa matriz, começando em
(0,0)
. Você também precisa acompanhar quais são seus próximos passos possíveis. Quando você inicia,(0,0)
você tem apenas duas opções: ou(0,1)
ou(1,0)
: como o valor de(0,1)
é menor, você escolhe. faça o mesmo para sua próxima escolha(0,2)
ou(1,0)
. Até agora, você tem a seguinte lista:1, 2, 4
. Seu próximo passo é(1,0)
que o valor é menor que(0,3)
. No entanto, agora você tem três opções para a sua próxima jogada: ou(0,3)
, ou(1,1)
ou(2,0)
.Você não precisa da matriz para obter a lista, mas precisa acompanhar todas as suas escolhas (ou seja, quando você tiver mais de 125 anos, terá 4 opções).
fonte
j
verificações para cada saída 1j ~ n^0.5
para o enésimo valor em uma sequência, pois osn
valores preenchem uma área noi x j
plano. Então esse algo éO(n^1.5)
tempo, comO(n^0.5)
espaço. Mas existe um algo de tempo linear com a mesma complacência de espaçon^0.5
e o mini-heap da resposta abaixo é oO(n*log(n))
tempo com o mesmon^0.5
espaço.Use uma pilha mínima.
Coloque 1.
extrair-Min. Diga que você recebe x.
Empurre 2x e 5x na pilha.
Repetir.
Em vez de armazenar x = 2 ^ i * 5 ^ j, você pode armazenar (i, j) e usar uma função de comparação personalizada.
fonte
Uma solução baseada em FIFO precisa de menos capacidade de armazenamento. Código Python.
resultado:
fonte
Isso é muito fácil de fazer
O(n)
em linguagens funcionais. A listal
de2^i*5^j
números pode ser simplesmente definida como1
e depois2*l
e5*l
mesclada. Aqui está como fica em Haskell:A
merge
função fornece um novo valor em tempo constante. O mesmo acontecemap
e, portanto, o mesmo acontecel
.fonte
union
, pois está removendo as duplicatas.merge
, como parte demergesort
, deve preservar duplicatas provenientes de ambas as sequências de entrada. Veja oData.List.Ordered
pacote para coisas relacionadas.Data.List.Ordered.union
. Isso faz com que seja uma linha:xs = 1 : union (map (2*) xs) (map (5*) xs)
[1, 2, 4, 5,...]
e inclui5*4
.Data.List.Ordered.union
função. Não deve ser confundidoData.List.union
.Você deve acompanhar os expoentes individuais deles e quais seriam suas somas
então você começa
f(0,0) --> 1
agora e precisa incrementar um deles:então sabemos que 2 é o próximo - também sabemos que podemos incrementar o expoente de i até que a soma ultrapasse 5.
Você continua indo e voltando assim até chegar ao seu número determinado de rodadas.
fonte
f(*,2)
apenas porque descobriu issof(a1,b+1)>f(a2,b)
. Uma abordagem incremental acabará gerando um número ilimitado de pares vizinhos à região que você já produziu.Usando programação dinâmica, você pode fazer isso em O (n). A verdade fundamental é que nenhum valor de iej pode nos dar 0 e, para obter 1, ambos os valores devem ser 0;
Sempre que você chamar essa função, verifique se iej estão definidos, se não forem nulos, preencha
TwoCount
eFiveCount
Resposta em C ++. Desculpe pelo mau estilo de codificação, mas estou com pressa :(
Obviamente, você pode usar estruturas de dados diferentes da matriz para aumentar dinamicamente seu armazenamento, etc. Este é apenas um esboço para provar que funciona.
fonte
O(exp(sqrt(n)))
, para produzirn
números da sequência. Existe um algoritmo linear , por exemplo, conforme fornecido por ThomasAhle.O(n)
significavan
ser o último valor, não o número de itens impressos, o que não está correto. Eu não sei como as linguagens funcionais trabalhar, ou como merge funciona em tempo constante, mas sua resposta tenho o meu upvotePor que não tentar olhar para isso de outra direção. Use um contador para testar as respostas possíveis em relação à fórmula original. Desculpe pelo pseudo-código.
fonte
O(4^sqrt(n))
porque onth
número da sequência é aproximadamente desse tamanho.Esta é a entrada relevante na OEIS.
Parece ser possível obter a sequência ordenada gerando os primeiros termos, digamos
e, a partir do segundo termo, multiplicando por 4 e 5 para obter os próximos dois
e assim por diante...
Intuitivamente, isso parece correto, mas é claro que falta uma prova.
fonte
Você sabe que log_2 (5) = 2.32. A partir disso, notamos que 2 ^ 2 <5 e 2 ^ 3> 5.
Agora veja uma matriz de respostas possíveis:
Agora, neste exemplo, escolha os números em ordem. A encomenda seria:
Observe que toda linha inicia 2 colunas atrás da linha que a inicia. Por exemplo, i = 0 j = 1 vem diretamente depois de i = 2 j = 0.
Um algoritmo que podemos derivar desse padrão é, portanto, (assuma j> i):
NOTA: O código aqui limita os valores dos expoentes de i e j a serem menores que 10. Você pode estender esse algoritmo facilmente para caber em quaisquer outros limites arbitrários.
NOTA: O tempo de execução desse algoritmo é O (n) para as primeiras n respostas.
NOTA: A complexidade do espaço para este algoritmo é O (1)
fonte
Minha implementação é baseada nas seguintes idéias:
Exemplo:
Código em Java:
fonte
calcular os resultados e colocá-los em uma lista classificada, juntamente com os valores para
i
ej
fonte
2^n*5^n
mas não2^(n+1)*5^(n-1)
qual é menor.i
e oj
seu, não é? Caso contrário, você nunca chegará ao estado de classificação e, portanto, nunca retornará um único valor. Mas, para qualquer limite quen
você escolher, sua lista será falha.i
ej
.2^i*5^j
valores e depois os ordena. Se você não possui um número limitado de "resultados", como chegará à etapa de classificação?O algoritmo implementado pelo usuário515430 por Edsger Dijkstra (http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF) é provavelmente o mais rápido possível. Eu ligo para cada número que é uma forma de
2^i * 5^j
"número especial". Agora, a resposta de vlads seriaO(i*j)
apenas com um algoritmo duplo, um para gerar os números especiaisO(i*j)
e outro para classificá-los (de acordo com o artigo vinculado tambémO(i*j)
.Mas vamos verificar o algoritmo de Dijkstra (veja abaixo). Nesse caso,
n
é a quantidade de números especiais que estamos gerando, tão igual ai*j
. Estamos repetindo uma vez1 -> n
e , em cada repetição, executamos uma ação constante. Portanto, esse algoritmo também éO(i*j)
. E com uma constante rápida e ardente também.Minha implementação em C ++ com GMP (wrapper C ++) e dependência
boost::lexical_cast
, embora isso possa ser facilmente removido (sou preguiçoso e quem não usa o Boost?). Compilado comg++ -O3 test.cpp -lgmpxx -o test
. No Q6600, o Ubuntu 10.10time ./test 1000000
dá1145ms
.fonte
Se você desenhar uma matriz com i como a linha ej como a coluna, poderá ver o padrão. Comece com i = 0 e, em seguida, basta percorrer a matriz subindo 2 linhas e 1 coluna à direita até chegar ao topo da matriz (j> = 0). Então vá i + 1, etc ...
Então, para i = 7, você viaja assim:
E para i = 8:
Aqui está em Java, subindo para i = 9. Ele imprime a posição da matriz (i, j) e o valor.
fonte
Minha Intuição :
Se eu pegar o valor inicial como 1, onde i = 0, j = 0, posso criar os próximos números como (2 ^ 1) (5 ^ 0), (2 ^ 2) (5 ^ 0), (2 ^ 0) * (5 ^ 1), ... ou seja, 2,4,5 ..
Digamos que a qualquer momento meu número seja x. então eu posso criar os próximos números das seguintes maneiras:
Explicação :
Execução de teste
Vamos começar com x = 1.
Os próximos três números são 1 * 2, 1 * 4, 1 * 5 [2,4,5]; Arr [1,2,4,5]
Agora x = 2
Os próximos três números são [4,8,10] {Como 4 já ocorreram, vamos ignorá-lo} [8,10]; Arr [1,2,4,5,8,10]
Agora x = 4
Os próximos três números [8,16,20] {8 já ocorreram ignorá-lo} [16,20] Arr [1,2,4,5,8,10,16,20]
x = 5
Os próximos três números [10,20,25] {10,20} já são adicionados [25] Arr [1,2,4,5,8,10,16,20,25]
Condição de rescisão
Análise
fonte
Só estava curioso para saber o que esperar na próxima semana e encontrou essa pergunta.
Penso que a ideia é 2 ^ i não aumenta tão grande quanto 5 ^ j. Portanto, aumente i enquanto o próximo passo j não for maior.
O exemplo em C ++ (Qt é opcional):
A saída:
fonte
Aqui está a minha solução
Resultado:
fonte
Eu sei que provavelmente estou errado, mas há uma heurística muito simples aqui, pois ela não envolve muitos números como 2,3,5. Sabemos que para qualquer i, j 2 ^ i * 5 ^ j a próxima sequência seria 2 ^ (i-2) * 5 ^ (j + 1). Sendo um google q deve ter uma solução simples.
Isso produz resultados como:
fonte
Se você seguir o que realmente está acontecendo quando incrementamos i ou j na expressão
2^i * 5^j
, você está multiplicando por mais 2 ou outro 5. Se reafirmarmos o problema como - dado um valor específico de i e j, como você encontrará a próxima maior valor, a solução se torna aparente.Aqui estão as regras que podemos enumerar intuitivamente:
i > 1
) na expressão, devemos substituí-los por um 5 para obter o próximo número maior. Assim,i -= 2
ej += 1
.j > 0
), precisamos substituí-lo por três 2s. Entãoj -= 1
ei += 3
.i += 1
.Aqui está o programa em Ruby:
fonte
Se tivermos permissão para usar a coleção java, podemos ter esse número em O (n ^ 2)
Aqui o powerLimit deve ser inicializado com muito cuidado !! Dependendo de quantos números você deseja.
fonte
Aqui está minha tentativa com Scala:
Resultado:
fonte