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?


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

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


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.


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

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.


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.


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.


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

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.


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)


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

0

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


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 1000000dá 1145ms.

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

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

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)

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

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

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

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

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.


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