Pergunta complicada da entrevista do Google

169

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?

Chris Eberle
fonte
63
O algoritmo ideal em termos de tempo do programador é gerar com dois loops aninhados e depois classificar. Por que eles fazem perguntas como essa?
Tom Zych
21
Você pode determinar pontos de transição observando qual número é maior. 2^2 < 5mas 2^3 > 5nesse 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álida
Mikhail
1
Há apenas uma saída, portanto a solução ideal é O (n). Leia a minha solução abaixo
Mikhail
3
Aparentemente, uma pergunta semelhante foi abordada antes: stackoverflow.com/questions/4600048/nth-ugly-number .
1
... e o OP provavelmente já deve escolher uma resposta. Afinal, ele já tem muitos bons.
Abeln

Respostas:

123

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.

int main()
{
    const int n = 20;       // Generate the first n numbers

    std::vector<int> v(n);
    v[0] = 1;

    int i2 = 0;             // Index for 2
    int i5 = 0;             // Index for 5

    int x2 = 2 * v[i2];     // Next two candidates
    int x5 = 5 * v[i5];

    for (int i = 1; i != n; ++i)
    {
        int m = std::min(x2, x5);
        std::cout << m << " ";
        v[i] = m;

        if (x2 == m)
        {
            ++i2;
            x2 = 2 * v[i2];
        }
        if (x5 == m)
        {
            ++i5;
            x5 = 5 * v[i5];
        }
    }

    std::cout << std::endl;
    return 0;
}
user515430
fonte
18
Link relevante: en.wikipedia.org/wiki/Regular_number#Algorithms . A propósito, não acho que seja uma pergunta muito boa de entrevista. Aqui está uma (papel escrito à mão) por Dijkstra onde ele fornece e prova um algoritmo para este problema: cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF
Elian Ebbing
Quando o objetivo é "percorrer iej", você precisa de menos capacidade de armazenamento, um FIFO é suficiente. Veja minha solução Python.
GaBorgulya
7
Quando o objetivo é "iterar sobre iej", não é o mesmo problema.
Mhum 01/04
Esta é uma implementação muito boa, usando um mínimo de memória. É memória linear, mesmo que você queira apenas um número.
Thomas Ahle
1
@ Thomashole Não sei se você viu isso, mas ele tem um código no final capaz de calcular o n-ésimo número isoladamente. Como, por exemplo, um bilionésimo número .
Will Ness
47

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:

     0    1    2    3    4    5   -- this is i
----------------------------------------------
0|   1    2    4    8   16   32
1|   5   10   20   40   80  160
2|  25   50  100  200  400  800
3| 125  250  500 1000 2000 ...
4| 625 1250 2500 5000 ...
j on the vertical

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

vlad
fonte
Votei nisso porque estava pensando na mesma linha, mas no caso geral, isso não seria algo como O (i ^ 2 * j)? Você teria que verificar vários números para cada número que você emitir.
Tom Zych
1
@ Tom, você precisa verificar mais de um número, mas não é tão ruim assim: quando você gera números entre 125 e 625, é necessário observar 4 valores. entre 625 e 3025, você olha para 5 valores. realmente, é jverificações para cada saída 1
vlad 31/03
+1: combine com esta pergunta: stackoverflow.com/questions/5000836/search-algorithm e parece que temos uma solução O (n).
@ Moron, não quero pagar US $ 25 por esse algoritmo, mas parece interessante.
Vlad
1
na verdade, j ~ n^0.5para o enésimo valor em uma sequência, pois os nvalores preenchem uma área no i x jplano. Então esse algo é O(n^1.5)tempo, com O(n^0.5)espaço. Mas existe um algo de tempo linear com a mesma complacência de espaço n^0.5e o mini-heap da resposta abaixo é o O(n*log(n))tempo com o mesmo n^0.5espaço.
Will Ness
25

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
1
Uma pilha daria lg n tempo em suas operações, o que aumenta a complexidade para n lg n.
corsiKa
@glow: Sim, eu não ver qualquer O (n) soluções postadas até agora, embora :-)
@abel: Esse comentário é antigo :-) Parece que ele também terá problemas para passar de (1,1) para (4,0). Mas vê-lo como uma matriz de jovens (veja a resposta de vlad) na verdade permite um algoritmo de tempo O (n).
@Moron: Eu não acho que haja algo errado com essa solução. Certamente nada de errado nos 30 primeiros elementos, que acabei de verificar agora (que cobririam o caso (1,1) -> (4,0)).
Abeln
@abel: Sim, na verdade, não tentei executá-lo :-) Talvez haja uma prova fácil de sua correção também. FWIW, ele já tem o meu +1.
13

Uma solução baseada em FIFO precisa de menos capacidade de armazenamento. Código Python.

F = [[1, 0, 0]]             # FIFO [value, i, j]
i2 = -1; n2 = n5 = None     # indices, nexts
for i in range(1000):       # print the first 1000
    last = F[-1][:]
    print "%3d. %21d = 2^%d * 5^%d" % tuple([i] + last)
    if n2 <= last: i2 += 1; n2 = F[i2][:]; n2[0] *= 2; n2[1] += 1
    if n5 <= last: i2 -= 1; n5 = F.pop(0); n5[0] *= 5; n5[2] += 1
    F.append(min(n2, n5))

resultado:

  0.                     1 = 2^0 * 5^0
  1.                     2 = 2^1 * 5^0
  2.                     4 = 2^2 * 5^0
 ...
998. 100000000000000000000 = 2^20 * 5^20
999. 102400000000000000000 = 2^27 * 5^17
GaBorgulya
fonte
6

Isso é muito fácil de fazer O(n)em linguagens funcionais. A lista lde 2^i*5^jnúmeros pode ser simplesmente definida como 1e depois 2*le 5*lmesclada. Aqui está como fica em Haskell:

merge :: [Integer] -> [Integer] -> [Integer]
merge (a:as) (b:bs)   
  | a < b   = a : (merge as (b:bs))
  | a == b  = a : (merge as bs)
  | b > a   = b : (merge (a:as) bs)

xs :: [Integer]
xs = 1 : merge (map(2*)xs) (map(5*)xs)

A mergefunção fornece um novo valor em tempo constante. O mesmo acontece mape, portanto, o mesmo acontece l.

Thomas Ahle
fonte
Eu acho que 'k' não está definido
ither
2
vamos chamar essa função de "mesclagem" union, pois está removendo as duplicatas. merge, como parte de mergesort, deve preservar duplicatas provenientes de ambas as sequências de entrada. Veja o Data.List.Orderedpacote para coisas relacionadas.
Will Ness
1
+1 para Data.List.Ordered.union. Isso faz com que seja uma linha:xs = 1 : union (map (2*) xs) (map (5*) xs)
phob
@GaBorgulya Sim, inclui cinco vezes a lista [1, 2, 4, 5,...]e inclui 5*4.
Thomas Ahle
1
@ Phob Sim, esta é a Data.List.Ordered.unionfunção. Não deve ser confundido Data.List.union.
Thomas Ahle
5

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:

f(1,0) = 2
f(0,1) = 5

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.

corsiKa
fonte
Sim, ele é. Você faz uma operação O (1) para cada rodada. Às vezes, você faz a ronda mais cedo, mas quando chega a essa ronda, não precisa fazê-la lá, para que ela funcione.
corsiKa
19
Como você vai de (1,1) para (4,0)? Por favor, elabore exatamente qual é o seu algoritmo.
O problema é que você não tem apenas duas possibilidades incrementais - por exemplo, você não terminou f(*,2)apenas porque descobriu isso f(a1,b+1)>f(a2,b). Uma abordagem incremental acabará gerando um número ilimitado de pares vizinhos à região que você já produziu.
comingstorm 31/03
O @ user515430 forneceu uma implementação que era mais do que eu poderia fazer no meu horário de almoço, mas era nisso que eu estava tentando chegar.
corsiKa
4

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;

TwoCount[1] = 0
FiveCount[1] = 0

// function returns two values i, and j
FindIJ(x) {
    if (TwoCount[x / 2]) {
        i = TwoCount[x / 2] + 1
        j = FiveCount[x / 2]
    }
    else if (FiveCount[x / 5]) {
        i = TwoCount[x / 2]
        j = FiveCount[x / 5] + 1
    }
}

Sempre que você chamar essa função, verifique se iej estão definidos, se não forem nulos, preencha TwoCounteFiveCount


Resposta em C ++. Desculpe pelo mau estilo de codificação, mas estou com pressa :(

#include <cstdlib>
#include <iostream>
#include <vector>

int * TwoCount;
int * FiveCount;

using namespace std;

void FindIJ(int x, int &i, int &j) {
        if (x % 2 == 0 && TwoCount[x / 2] > -1) {
                cout << "There's a solution for " << (x/2) << endl;
                i = TwoCount[x / 2] + 1;
                j = FiveCount[x / 2];
        } else if (x % 5 == 0 && TwoCount[x / 5] > -1) {
                cout << "There's a solution for " << (x/5) << endl;
                i = TwoCount[x / 5];
                j = FiveCount[x / 5] + 1;
        }    
}

int main() {
        TwoCount = new int[200];
        FiveCount = new int[200];

        for (int i = 0; i < 200; ++i) {
                TwoCount[i] = -1;
                FiveCount[i] = -1;
        }

        TwoCount[1] = 0;
        FiveCount[1] = 0;

        for (int output = 2; output < 100; output++) {
                int i = -1;
                int j = -1;
                FindIJ(output, i, j);
                if (i > -1 && j > -1) {
                        cout << "2^" << i << " * " << "5^" 
                                     << j << " = " << output << endl;
                        TwoCount[output] = i;
                        FiveCount[output] = j;
                }
        }    
}

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.

Mikhail
fonte
4
Parece uma resposta interessante, mas não consigo ver como realmente funciona. Você poderia adicionar mais detalhes?
David Brunelle
Depois de estudá-lo, eu realmente não vejo como isso funciona. Assumindo a divisão inteira, ele fornecerá exatamente o mesmo resultado para 3 e para 2. Além disso, se as condições se forem testes para diferente de zero, nunca funcionará, pois não há entradas diferentes de zero.
David Thornley 31/03
Publicou uma versão em C ++ para todos os que não dizem. @ David Seus comentários estão corretos, mas meu código original era pseudo-código e eu estava pensando em termos de script, portanto divisão não inteira e distinguindo entre entrada nula e entrada de valor 0
Mikhail
esse código enumera todos os números naturais; portanto, por comentário de @ThomasAhle na resposta de "Lost in Alabama" abaixo, é necessário O(exp(sqrt(n))), para produzir nnúmeros da sequência. Existe um algoritmo linear , por exemplo, conforme fornecido por ThomasAhle.
Will Ness
1
Você está certo. No meu entendimento, O(n)significava nser 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 upvote
Mikhail
2

Por 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.

for x = 1 to n
{
  i=j=0
  y=x
  while ( y > 1 )
  {
    z=y
    if y divisible by 2 then increment i and divide y by 2
    if y divisible by 5 then increment j and divide y by 5

    if y=1 then print i,j & x  // done calculating for this x

    if z=y then exit while loop  // didn't divide anything this loop and this x is no good 
  }
}
Perdido no Alabama
fonte
Isso ocorre aproximadamente O(4^sqrt(n))porque o nthnúmero da sequência é aproximadamente desse tamanho.
Thomas Ahle
2

Esta é a entrada relevante na OEIS.

Parece ser possível obter a sequência ordenada gerando os primeiros termos, digamos

1 2 4 5

e, a partir do segundo termo, multiplicando por 4 e 5 para obter os próximos dois

1 2 4 5 8 10

1 2 4 5 8 10 16 20

1 2 4 5 8 10 16 20 25

e assim por diante...

Intuitivamente, isso parece correto, mas é claro que falta uma prova.

abeln
fonte
2
Errado :( [1 2 4 5 8 10 16 20 25 32 40 50 64 80 100 125 128 160 200 250 256 320 400 500 625 ] No entanto, 500 <512 = 2 ^ 9 <625.
GaBorgulya
1
@NateKerkhofs, 512 é gerado, mas está fora de ordem, pois 512 é menor que o 625 já gerado; o algoritmo precisaria de lógica adicional para colocar a saída em ordem - Portanto, o algoritmo não é tão simples quanto o proposto e nem o mesmo algoritmo.
precisa saber é o seguinte
1

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:

j/i  0   1   2   3   4   5
 0   1   2   4   8  16  32
 1   5  10  20  40  80 160 
 2  25  50 100 200 400 800
 3 125 250 500 ...

Agora, neste exemplo, escolha os números em ordem. A encomenda seria:

j/i  0   1   2   3   4   5
 0   1   2   3   5   7  10
 1   4   6   8  11  14  18
 2   9  12  15  19  23  27
 3  16  20  24...

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

int i = 2;
int j = 5;
int k;
int m;

int space = (int)(log((float)j)/log((float)i));
for(k = 0; k < space*10; k++)
{
    for(m = 0; m < 10; m++)
    {
        int newi = k-space*m;
        if(newi < 0)
            break;
        else if(newi > 10)
            continue;
        int result = pow((float)i,newi) * pow((float)j,m);
        printf("%d^%d * %d^%d = %d\n", i, newi, j, m, result);
    }
}   

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)

KLee1
fonte
Você escreveu "toda linha inicia 2 colunas atrás da linha que inicia". Contudo 2 ^ 9 = 512 e 5 ^ 4 = 625, de modo que este não é verdadeiro para a linha 4.
GaBorgulya
@ user678105 Você está certo. Este código não funciona. Desculpe tudo. Esse código não funciona por causa do arredondamento do log e da minha suposição de que não importava.
KLee1
1
Veja como você corrige isso. No plano (x, y) cheio de pontos com coeficientes integrais, desenhe uma linha de (0,1) a (log2 (5), 0). (0,0) está no canto superior esquerdo. O eixo X vai para a direita, o eixo Y desce. Agora desenhe uma linha do ponto de origem (0,0) que é perpendicular à 1ª linha. Agora deslize a primeira linha ao longo da segunda, cada vez mais longe da origem e colete os pontos de coordenadas inteiras à medida que são cruzados. Para a sequência gerada por {2,3,5}, será um plano movendo-se no espaço (i, j, k). Se você pode traduzir essa idéia em código, dê-me uma mensagem. :)
Will Ness
1

Minha implementação é baseada nas seguintes idéias:

  • Use duas filas Q2 e Q5, ambas inicializadas com 1. Manteremos as duas na ordem classificada.
  • A cada passo, retire da fila o menor número do elemento MIN de Q2 ou Q5 e imprima-o. Se Q2 e Q5 tiverem o mesmo elemento - remova os dois. Imprima este número. Isso é basicamente a mesclagem de duas matrizes classificadas - em cada etapa, escolha o menor elemento e avance.
  • Coloque MIN * 2 em Q2 e MIN * 5 em Q5. Essa alteração não interrompe a invariante de Q2 / Q5 sendo classificada, porque MIN é maior que o número MIN anterior.

Exemplo:

Start with 1 and 1 (to handle i=0;j=0 case):
  Q2: 1
  Q5: 1
Dequeue 1, print it and enqueue 1*2 and 1*5:
  Q2: 2
  Q5: 5
Pick 2 and add 2*2 and 2*5:
  Q2: 4
  Q5: 5 10
Pick 4 and add 4*2 and 4*5:
  Q2: 8
  Q5: 5 10 20
....

Código em Java:

public void printNumbers(int n) {
    Queue<Integer> q2 = new LinkedList<Integer>();
    Queue<Integer> q5 = new LinkedList<Integer>();
    q2.add(1);
    q5.add(1);
    for (int i = 0; i < n; i++) {
        int a = q2.peek();
        int b = q5.peek();
        int min = Math.min(a, b);
        System.out.println(min);
        if (min == a) {
            q2.remove();
        }
        if (min == b) {
            q5.remove();
        }
        q2.add(min * 2);
        q5.add(min * 5);
    }
}
ejboy
fonte
0

calcular os resultados e colocá-los em uma lista classificada, juntamente com os valores para iej

vlad
fonte
Provavelmente isso vai te dar buracos no final da sua sequência. Por exemplo, você terá, 2^n*5^nmas não 2^(n+1)*5^(n-1)qual é menor.
Thomas Ahle 31/03
@ Thomas Não tenho certeza se sigo sua lógica aqui. Se você calcular um, por que você também não calcularia o outro?
Vlad
2
@ vlad Você precisa ter um limite para o seu ie o jseu, não é? Caso contrário, você nunca chegará ao estado de classificação e, portanto, nunca retornará um único valor. Mas, para qualquer limite que nvocê escolher, sua lista será falha.
Thomas Ahle
@ Thomas seu argumento ainda não faz sentido. O OP nunca especificou o fim de sua lista de resultados. Se ele faz, você pode encontrar o max ie j.
Vlad
1
@ vlad Enquanto eu leio sua resposta, você primeiro calcula os "resultados" / os 2^i*5^jvalores e depois os ordena. Se você não possui um número limitado de "resultados", como chegará à etapa de classificação?
Thomas Ahle
0

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 seria O(i*j)apenas com um algoritmo duplo, um para gerar os números especiais O(i*j)e outro para classificá-los (de acordo com o artigo vinculado também O(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 a i*j. Estamos repetindo uma vez 1 -> ne , 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 com g++ -O3 test.cpp -lgmpxx -o test. No Q6600, o Ubuntu 10.10 time ./test 10000001145ms.

#include <iostream>
#include <boost/lexical_cast.hpp>
#include <gmpxx.h>

int main(int argc, char *argv[]) {
    mpz_class m, x2, x5, *array, r;
    long n, i, i2, i5;

    if (argc < 2) return 1;

    n = boost::lexical_cast<long>(argv[1]);

    array = new mpz_class[n];
    array[0] = 1;

    x2 = 2;
    x5 = 5;
    i2 = i5 = 0;

    for (i = 1; i != n; ++i) {
        m = std::min(x2, x5);

        array[i] = m;

        if (x2 == m) {
            ++i2;
            x2 = 2 * array[i2];
        }

        if (x5 == m) {
            ++i5;
            x5 = 5 * array[i5];
        }
    }

    delete [] array;
    std::cout << m << std::endl;

    return 0;
}
orlp
fonte
0

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:

7, 0 -> 5, 1 -> 3, 2 -> 1, 3

E para i = 8:

8, 0 -> 6, 1 -> 4, 2 -> 2, 3 -> 0, 4

Aqui está em Java, subindo para i = 9. Ele imprime a posição da matriz (i, j) e o valor.

for(int k = 0; k < 10; k++) {

    int j = 0;

    for(int i = k; i >= 0; i -= 2) {

        int value = (int)(Math.pow(2, i) * Math.pow(5, j));
        System.out.println(i + ", " + j + " -> " + value);
        j++;
    }
}
Cubículo
fonte
0

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:

  • x * 2
  • x * 4
  • x * 5

Explicação :

Since new numbers can only be the product with 2 or 5.
But 4 (pow(2,2)) is smaller than 5, and also we have to generate 
Numbers in sorted order.Therefore we will consider next numbers
be multiplied with 2,4,5.
Why we have taken x*4 ? Reason is to pace up i, such that it should not 
be greater than pace of j(which is 5 to power). It means I will 
multiply my number by 2, then by 4(since 4 < 5), and then by 5 
to get the next three numbers in sorted order.

Execução de teste

We need to take an Array-list of Integers, let say Arr.

Also put our elements in Array List<Integers> Arr.
Initially it contains Arr : [1]
  • 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

 Terminating condition when Arr last number becomes greater 
 than (5^m1 * 2^m2), where m1,m2 are given by user.

Análise

 Time Complexity : O(K) : where k is numbers possible between i,j=0 to 
 i=m1,j=m2.
 Space Complexity : O(K)
bharatj
fonte
0

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

QFile f("out.txt"); //use output method of your choice here
f.open(QIODevice::WriteOnly);
QTextStream ts(&f);

int i=0;
int res=0;
for( int j=0; j<10; ++j )
{
    int powI = std::pow(2.0,i );
    int powJ = std::pow(5.0,j );
    while ( powI <= powJ  ) 
    {
        res = powI * powJ;
        if ( res<0 ) 
            break; //integer range overflow

        ts<<i<<"\t"<<j<<"\t"<<res<<"\n";
        ++i;
        powI = std::pow(2.0,i );

    }
}

A saída:

i   j   2^i * 5^j
0   0   1
1   1   10
2   1   20
3   2   200
4   2   400
5   3   4000
6   3   8000
7   4   80000
8   4   160000
9   4   320000
10  5   3200000
11  5   6400000
12  6   64000000
13  6   128000000
14  7   1280000000
Valentin Heinitz
fonte
Esta solução perde algumas combinações. Por exemplo, ele não examinar o caso em que i = 1, j = 2 qualquer caso em que i = 1 e j> 1, para essa matéria ..
Federico
@Federico: Você está certo! Não admira por isso que eu falhei google-entrevistas duas vezes com 6 anos de intervalo, mas quase as mesmas perguntas :-)
Valentin Heinitz
0

Aqui está a minha solução

#include <stdio.h>
#include <math.h>
#define N_VALUE 5
#define M_VALUE  5

int n_val_at_m_level[M_VALUE];

int print_lower_level_val(long double val_of_higher_level, int m_level)
{
int  n;
long double my_val;


for( n = n_val_at_m_level[m_level]; n <= N_VALUE; n++) {
    my_val =  powl(2,n) * powl(5,m_level);
    if(m_level != M_VALUE && my_val > val_of_higher_level) {
        n_val_at_m_level[m_level] = n;
        return 0;
    }
    if( m_level != 0) {
        print_lower_level_val(my_val, m_level - 1);
    }
    if(my_val < val_of_higher_level || m_level == M_VALUE) {
        printf("    %Lf n=%d m = %d\n", my_val, n, m_level);
    } else {
        n_val_at_m_level[m_level] = n;
        return 0;
    }
 }
 n_val_at_m_level[m_level] = n;
 return 0;
 }


 main()
 {
    print_lower_level_val(0, M_VALUE); /* to sort 2^n * 5^m */
 }

Resultado:

1.000000 n = 0 m = 0
2.000000 n = 1 m = 0
4.000000 n = 2 m = 0
5.000000 n = 0 m = 1
8.000000 n = 3 m = 0
10.000000 n = 1 m = 1
16.000000 n = 4 m = 0
20.000000 n = 2 m = 1
25.000000 n = 0 m = 2
32.000000 n = 5 m = 0
40.000000 n = 3 m = 1
50.000000 n = 1 m = 2
80.000000 n = 4 m = 1
100.000000 n = 2 m = 2
125.000000 n = 0 m = 3
160.000000 n = 5 m = 1
200.000000 n = 3 m = 2
250.000000 n = 1 m = 3
400.000000 n = 4 m = 2
500.000000 n = 2 m = 3
625.000000 n = 0 m = 4
800.000000 n = 5 m = 2
1000.000000 n = 3 m = 3
1250.000000 n = 1 m = 4
2000.000000 n = 4 m = 3
2500.000000 n = 2 m = 4
3125.000000 n = 0 m = 5
4000.000000 n = 5 m = 3
5000.000000 n = 3 m = 4
6250.000000 n = 1 m = 5
10000.000000 n = 4 m = 4
12500.000000 n = 2 m = 5
20000.000000 n = 5 m = 4
25000.000000 n = 3 m = 5
50000.000000 n = 4 m = 5
100000.000000 n = 5 m = 5
dhanasubbu
fonte
0

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.

def func(i, j):
 print i, j, (2**i)*(5**j)

imax=i=2
j=0
print "i", "j", "(2**i)*(5**j)"

for k in range(20):
    func(i,j)
    j=j+1; i=i-2
    if(i<0):
        i = imax = imax+1
        j=0

Isso produz resultados como:

i j (2**i)*(5**j)
2 0 4
0 1 5
3 0 8
1 1 10
4 0 16
2 1 20
0 2 25
5 0 32
3 1 40
1 2 50
6 0 64
4 1 80
2 2 100
0 3 125
7 0 128
5 1 160
3 2 200
1 3 250
8 0 256
6 1 320
d1val
fonte
pode funcionar até 20 ou 200, mas em algum momento começará a pular alguns números e / ou produzi-los na ordem errada.
Will Ness
0

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:

  • Se houver um par de 2s ( i > 1) na expressão, devemos substituí-los por um 5 para obter o próximo número maior. Assim, i -= 2e j += 1.
  • Caso contrário, se houver um 5 ( j > 0), precisamos substituí-lo por três 2s. Então j -= 1e i += 3.
  • Caso contrário, precisamos apenas fornecer outros 2 para aumentar o valor em um mínimo. i += 1.

Aqui está o programa em Ruby:

i = j = 0                                                                       
20.times do                                                                     
  puts 2**i * 5**j

  if i > 1                                                                      
    j += 1                                                                      
    i -= 2                                                                      
  elsif j > 0                                                                   
    j -= 1                                                                      
    i += 3                                                                      
  else                                                                          
    i += 1                                                                      
  end                                                                                                                                                               
end
slowpoison
fonte
Isso não funciona, pois 'i' nunca é maior que 4, portanto, nenhum múltiplo de 32 (2 ^ 5) será exibido.
Threenplusone 31/01
0

Se tivermos permissão para usar a coleção java, podemos ter esse número em O (n ^ 2)

public static void main(String[] args) throws Exception {
    int powerLimit = 7;  
     int first = 2;
     int second = 5;
    SortedSet<Integer> set = new TreeSet<Integer>();

    for (int i = 0; i < powerLimit; i++) {
        for (int j = 0; j < powerLimit; j++) {
            Integer x = (int) (Math.pow(first, i) * Math.pow(second, j));
            set.add(x);
        }
    }

    set=set.headSet((int)Math.pow(first, powerLimit));

    for (int p : set)
        System.out.println(p);
}

Aqui o powerLimit deve ser inicializado com muito cuidado !! Dependendo de quantos números você deseja.

Kavi Temre
fonte
isso produz resultados incorretos: 2 ^ 8 = 256 está faltando antes de 2 ^ 6 * 5 = 320. a área de enumeração é triangular, não retangular.
Will Ness
@WillNess How ?? Quando estou a criação powerLimit = 9, este trecho de retornos seguintes números 1 2 4 5 8 10 16 25 32 40 20 50 64 80 100 125 128 160 200 250 256 320 400 500
kavi temre
não, produz 100 números. como você sabe onde parar? você deve explicar isso. --- Mencionei o 7 como presente no seu snippet de código. Para que essa seja uma resposta válida, você deve explicar exatamente como definir o limite para uma determinada quantidade de números e quantos números serão superproduzidos .
Will Ness
0

Aqui está minha tentativa com Scala:

case class IndexValue(twosIndex: Int, fivesIndex: Int)
case class OutputValues(twos: Int, fives: Int, value: Int) {
  def test(): Boolean = {
    Math.pow(2,  twos) * Math.pow(5, fives) == value
  }
}

def run(last: IndexValue = IndexValue(0, 0), list: List[OutputValues] = List(OutputValues(0, 0, 1))): List[OutputValues] = {
  if (list.size > 20) {
    return list
  }

  val twosValue = list(last.twosIndex).value * 2
  val fivesValue = list(last.fivesIndex).value * 5

  if (twosValue == fivesValue) {
    val lastIndex = IndexValue(last.twosIndex + 1, last.fivesIndex + 1)
    val outputValues = OutputValues(value = twosValue, twos = list(last.twosIndex).twos + 1, fives = list(last.fivesIndex).fives + 1)
    run(lastIndex, list :+ outputValues)
  } else if (twosValue < fivesValue) {
    val lastIndex = IndexValue(last.twosIndex + 1, last.fivesIndex)
    val outputValues = OutputValues(value = twosValue, twos = list(last.twosIndex).twos + 1, fives = list(last.twosIndex).fives)
    run(lastIndex, list :+ outputValues)
  } else {
    val lastIndex = IndexValue(last.twosIndex, last.fivesIndex + 1)
    val outputValues = OutputValues(value = fivesValue, twos = list(last.fivesIndex).twos, fives = list(last.fivesIndex).fives + 1)
    run(lastIndex, list :+ outputValues)
  }
}

val initialIndex = IndexValue(0, 0)
run(initialIndex, List(OutputValues(0, 0, 1))) foreach println

Resultado:

OutputValues(0,0,1)
OutputValues(1,0,2)
OutputValues(2,0,4)
OutputValues(0,1,5)
OutputValues(3,0,8)
OutputValues(1,1,10)
OutputValues(4,0,16)
OutputValues(2,1,20)
OutputValues(0,2,25)
OutputValues(5,0,32)
OutputValues(3,1,40)
OutputValues(1,2,50)
OutputValues(6,0,64)
OutputValues(4,1,80)
OutputValues(2,2,100)
OutputValues(0,3,125)
OutputValues(7,0,128)
OutputValues(5,1,160)
OutputValues(3,2,200)
OutputValues(1,3,250)
OutputValues(8,0,256)
nishnet2002
fonte