Tricky Intervista a Google


169

Un mio amico sta intervistando per un lavoro. Una delle domande del colloquio mi ha fatto pensare, volevo solo un feedback.

Esistono 2 numeri interi non negativi: i e j. Data la seguente equazione, trova una soluzione (ottimale) per iterare su i e j in modo tale che l'output sia ordinato.

2^i * 5^j

Quindi i primi round sarebbero così:

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

Prova come potrei, non riesco a vedere uno schema. I vostri pensieri?


63
L'algoritmo ottimale in termini di tempo del programmatore è generare con due loop nidificati, quindi ordinare. Perché fanno domande come questa?
Tom Zych,

21
Potresti essere in grado di determinare i punti di transizione osservando quale numero è maggiore. 2^2 < 5ma 2^3 > 5così a quel punto aumenti j. Penso che tu possa produrre l'output in O (n) anziché in O (nlgn). @ tom-zynch due loop nidificati è O (n ^ 2). Questa domanda è molto valida
Mikhail

1
C'è solo un output, quindi la soluzione ottimale è O (n). Leggi la mia soluzione di seguito
Mikhail

3
Una domanda simile è stata posta prima apparentemente: stackoverflow.com/questions/4600048/nth-ugly-number .

1
... e l'OP probabilmente dovrebbe già scegliere una risposta. Dopotutto, ne ha già molti di buoni.
abeln

Risposte:


123

Dijkstra deriva una soluzione eloquente in "Una disciplina di programmazione". Attribuisce il problema a Hamming. Ecco la mia implementazione della soluzione di 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 pertinente: en.wikipedia.org/wiki/Regular_number#Algorithms . Non penso che questa sia un'ottima domanda per l'intervista. Ecco un (documento scritto a mano) di Dijkstra in cui fornisce e dimostra un algoritmo per questo problema: cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF
Elian Ebbing

Quando l'obiettivo è "iterare su iej" è necessaria una capacità di archiviazione inferiore, è sufficiente un FIFO. Vedi la mia soluzione Python.
GaBorgulya,

7
Quando l'obiettivo è "iterare su iej", non è lo stesso problema.
mhum

Questa è un'implementazione davvero piacevole, usando un minimo di memoria. È memoria lineare anche se si desidera solo un numero.
Thomas Ahle,

1
@ThomasAhle Non so se hai visto questo, ma ha un codice alla fine in grado di calcolare n-esimo numero in isolamento. Ad esempio un miliardesimo numero .
Will Ness,

47

ecco un modo più raffinato di farlo (più raffinato della mia precedente risposta, cioè):

immagina che i numeri siano posti in una matrice:

     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

quello che devi fare è 'camminare' questa matrice, a partire da (0,0). Devi anche tenere traccia di quali sono le tue prossime mosse possibili. Quando inizi da (0,0)hai solo due opzioni: o (0,1)oppure (1,0): poiché il valore di (0,1)è più piccolo, scegli quello. quindi fai lo stesso per la tua prossima scelta (0,2)o (1,0). Finora, si ha il seguente elenco: 1, 2, 4. La tua prossima mossa è (1,0)poiché il valore è inferiore a (0,3). Tuttavia, ora hai tre opzioni per la tua prossima mossa: o (0,3), oppure (1,1), o (2,0).

Non hai bisogno della matrice per ottenere l'elenco, ma devi tenere traccia di tutte le tue scelte (cioè quando arrivi a 125+, avrai 4 scelte).


Ho votato a favore perché stavo pensando sulla stessa linea, ma nel caso generale, non sarebbe qualcosa come O (i ^ 2 * j)? Dovresti controllare più numeri per ogni numero che hai prodotto.
Tom Zych,

1
@Tom devi controllare più di un numero, ma non è poi così male: quando produci numeri tra 125 e 625, devi guardare 4 valori. tra 625 e 3025, vengono visualizzati 5 valori. Quindi, veramente, è ji controlli per ogni 1 uscita
vlad

+1: Combina con questa domanda: stackoverflow.com/questions/5000836/algoritmo di ricerca e sembra che abbiamo una soluzione O (n).

@Moron maledizione, non voglio pagare $ 25 per quell'algoritmo, ma sembra interessante.
vlad

1
in realtà, j ~ n^0.5per l'n-esimo valore in una sequenza, poiché i nvalori riempiono un'area sul i x jpiano. Quindi questo algo è O(n^1.5)tempo, con O(n^0.5)spazio. Ma esiste un algo temporale lineare con lo stesso spazio complxty di n^0.5, e il mini-heap algo dalla risposta sotto è il O(n*log(n))tempo con lo stesso n^0.5spazio.
Will Ness,

25

Usa un heap Min.

Metti 1.

estrarre-Min. Di 'che hai x.

Spingere 2x e 5x nell'heap.

Ripetere.

Invece di memorizzare x = 2 ^ i * 5 ^ j, è possibile memorizzare (i, j) e utilizzare una funzione di confronto personalizzata.


1
Un mucchio darebbe lg n tempo alle sue operazioni, il che spinge la complessità a n lg n.
corsiKa

@glow: Sì, non vedo nessuna soluzione O (n) pubblicata finora, però :-)

@abel: Questo commento è vecchio :-) Sembra che avrà anche problemi a passare da (1,1) a (4,0). Ma vederlo come una matrice di un giovane (vedi la risposta di Vlad) in realtà consente un algoritmo di tempo O (n).

@Moron: non credo che ci sia qualcosa di sbagliato in quella soluzione. Certamente nulla di sbagliato nei primi 30 elementi, che ho appena verificato in questo momento (che coprirebbe il caso (1,1) -> (4,0)).
abeln,

@abel: Sì, in realtà non ho provato a eseguirlo :-) Forse c'è anche una facile prova della sua correttezza. FWIW, ha già il mio +1.

13

Una soluzione basata su FIFO richiede una capacità di archiviazione inferiore. Codice 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))

produzione:

  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

Questo è molto facile da fare O(n)in linguaggi funzionali. L'elenco ldei 2^i*5^jnumeri può essere semplicemente definito come 1e quindi 2*le 5*lunito. Ecco come appare in 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)

La mergefunzione ti dà un nuovo valore in tempo costante. Così fa mape quindi anche così l.


Penso che 'k' non sia definito
Ither

2
chiamiamo invece questa funzione "unione" union, poiché rimuove i duplicati. merge, come parte di mergesort, deve preservare i duplicati provenienti da entrambe le sue sequenze di input. Vedi Data.List.Orderedpacchetto per materiale correlato.
Will Ness,

1
+1 per Data.List.Ordered.union. Questo fa una riga:xs = 1 : union (map (2*) xs) (map (5*) xs)
Phob,

@GaBorgulya Sì, include cinque volte l'elenco, [1, 2, 4, 5,...]quindi include 5*4.
Thomas Ahle,

1
@Phob Sì, questa è la Data.List.Ordered.unionfunzione. Da non confondere Data.List.union.
Thomas Ahle,

5

Devi tenere traccia dei singoli esponenti di loro e quali sarebbero le loro somme

quindi inizi con f(0,0) --> 1 ora devi incrementarne uno:

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

quindi sappiamo che 2 è il prossimo - sappiamo anche che possiamo aumentare l'esponente di i fino a quando la somma supera 5.

Continui ad andare avanti e indietro in questo modo fino a quando non sei al tuo numero di turni.


Sì. Esegui un'operazione O (1) per ogni round. A volte fai il round presto, ma quando arrivi a quel round non devi farlo lì, quindi si risolve da solo.
corsiKa

19
Come si passa da (1,1) a (4,0)? Si prega di elaborare esattamente qual è il tuo algoritmo.

Il problema è che non hai solo due possibilità incrementali - ad esempio, non hai finito f(*,2)solo perché l'hai trovato f(a1,b+1)>f(a2,b). Un approccio incrementale alla fine genererà un numero illimitato di coppie vicine alla regione che hai già prodotto.
comingst

@ user515430 ha fornito un'implementazione che era più di quanto potessi fare durante la pausa pranzo, ma è quello che stavo cercando di ottenere.
corsiKa

4

Usando la programmazione dinamica puoi farlo in O (n). La verità fondamentale è che nessun valore di iej può darci 0, e per ottenere 1 entrambi i valori devono essere 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
    }
}

Ogni volta che si chiama questa funzione, verificare se i e j sono impostati, se non sono nulli, quindi popolare TwoCounteFiveCount


Risposta C ++. Ci scusiamo per il cattivo stile di programmazione, ma ho fretta :(

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

Ovviamente puoi usare strutture di dati diverse dall'array per aumentare dinamicamente la tua memoria ecc. Questo è solo uno schizzo per dimostrare che funziona.


4
Sembra una risposta interessante, ma non riesco a vedere come funziona davvero. Potresti aggiungere maggiori dettagli?
David Brunelle,

Dopo averlo studiato da solo, non vedo davvero come funziona. Supponendo la divisione intera, fornirà esattamente lo stesso risultato per 3 come per 2. Inoltre, se le condizioni if ​​sono test per valori diversi da zero, non funzionerà mai, poiché non vi sono voci diverse da zero.
David Thornley,

Ha pubblicato una versione C ++ per tutti coloro che dicono no. @David I tuoi commenti sono corretti, ma il mio codice originale era pseudo-codice e stavo pensando in termini di scripting, quindi divisione non intera e distinzione tra immissione nulla e immissione di valore 0
Mikhail

questo codice elenca tutti i numeri naturali, quindi, per commento di @ThomasAhle alla risposta di "Lost in Alabama" di seguito, ci vuole O(exp(sqrt(n))), per produrre nnumeri della sequenza. Esiste un algoritmo lineare , ad es. Dato da ThomasAhle.
Will Ness,

1
Hai ragione. A mio avviso O(n)significava nessere l'ultimo valore, non il numero di articoli stampati, il che non è corretto. Non so come funzionano i linguaggi funzionali o come funziona l'unione in tempo costante, ma la sua risposta ha ottenuto il mio voto
Mikhail

2

Perché non provare a guardare questo dall'altra direzione. Usa un contatore per testare le possibili risposte rispetto alla formula originale. Ci scusiamo per lo pseudo codice.

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

Ciò si verifica O(4^sqrt(n))perché il nthnumero della sequenza è approssimativamente di quella dimensione.
Thomas Ahle,

2

Questa è la voce pertinente di OEIS.

Sembra essere possibile ottenere la sequenza ordinata generando i primi termini, diciamo

1 2 4 5

e poi, a partire dal secondo termine, moltiplicando per 4 e 5 per ottenere i due successivi

1 2 4 5 8 10

1 2 4 5 8 10 16 20

1 2 4 5 8 10 16 20 25

e così via...

Intuitivamente, questo sembra corretto, ma ovviamente manca una prova.


2
Sbagliato :( [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 ] Comunque 500 <512 = 2 ^ 9 <625.
GaBorgulya

1
@NateKerkhofs, 512 viene generato ma non funziona poiché 512 è inferiore al 625 già generato; l'algoritmo avrebbe bisogno di ulteriore logica per mettere in ordine l'output - Quindi l'algoritmo non è così semplice come proposto e non è affatto lo stesso algoritmo.
GordonBood

1

Sai che log_2 (5) = 2.32. Da ciò notiamo che 2 ^ 2 <5 e 2 ^ 3> 5.

Ora guarda una matrice di possibili risposte:

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

Ora, per questo esempio, scegli i numeri in ordine. L'ordinamento sarebbe:

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

Si noti che ogni riga inizia 2 colonne dietro la riga che la avvia. Ad esempio, i = 0 j = 1 arriva direttamente dopo i = 2 j = 0.

Un algoritmo che possiamo derivare da questo modello è quindi (supponiamo che 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: il codice qui racchiude i valori degli esponenti di i e j come inferiori a 10. È possibile estendere facilmente questo algoritmo per adattarlo a qualsiasi altro limite arbitrario.

NOTA: il tempo di esecuzione per questo algoritmo è O (n) per le prime n risposte.

NOTA: la complessità dello spazio per questo algoritmo è O (1)


Hai scritto "ogni riga inizia 2 colonne dietro la riga iniziando". Tuttavia 2 ^ 9 = 512 e 5 ^ 4 = 625, quindi questo non è vero per la riga 4.
GaBorgulya

@ user678105 Hai ragione. Questo codice non funziona. Mi dispiace tutto. Questo codice non funziona a causa dell'arrotondamento del registro e della mia ipotesi che non avesse importanza.
KLee1

1
Ecco come risolvere questo problema. Sul piano (x, y) pieno di punti con coefficienti integrali, traccia una linea da (0,1) a (log2 (5), 0). (0,0) si trova nell'angolo in alto a sinistra. L'asse X va verso destra, l'asse Y scende. Ora traccia una linea dal punto di origine (0,0) che è perpendicolare alla prima linea. Ora fai scorrere la prima linea lungo la seconda, sempre più lontano dall'origine, e raccogli i punti delle coordinate intere mentre vengono incrociati. Per la sequenza generata da {2,3,5}, sarà un piano che si muove attraverso, nello spazio (i, j, k). Se riesci a tradurre questa idea in codice, fammi un messaggio. :)
Will Ness,

1

La mia implementazione si basa sulle seguenti idee:

  • Utilizzare due code Q2 e Q5, entrambe inizializzate con 1. Manterremo entrambe le code in ordine.
  • Ad ogni passo, dequeue l'elemento MIN più piccolo da Q2 o Q5 e stampalo. Se sia Q2 che Q5 hanno lo stesso elemento, rimuovili entrambi. Stampa questo numero. Questo è fondamentalmente l'unione di due array ordinati: ad ogni passaggio scegliere l'elemento più piccolo e avanzare.
  • Accodare MIN * 2 a Q2 e MIN * 5 a Q5. Questa modifica non interrompe l'invariante di Q2 / Q5 in ordine, poiché MIN è maggiore del precedente numero MIN.

Esempio:

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

Codice in 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

calcola i risultati e inseriscili in un elenco ordinato, insieme ai valori per iej


Questo probabilmente ti darà buchi nell'ultima parte della sequenza. Ad esempio, avrai 2^n*5^nma non 2^(n+1)*5^(n-1)è più piccolo.
Thomas Ahle,

@Thomas Non sono sicuro di seguire la tua logica qui. Se ne calcoli uno, perché non dovresti calcolare anche l'altro?
vlad

2
@vlad Devi avere un limite ai tuoi ie ai tuoi j, vero? Altrimenti non arriverai mai allo stato di ordinamento e quindi non restituirai mai un singolo valore. Ma per qualsiasi limite ntu scelga, la tua lista sarà difettosa.
Thomas Ahle,

@Thomas il tuo argomento non ha ancora senso. L'OP non ha mai specificato la fine del suo elenco di risultati. Se lo fa, puoi trovare il massimo ie j.
vlad

1
@vlad Mentre leggo la tua risposta, prima calcola i "risultati" / i 2^i*5^jvalori, e poi li ordina. Se non si dispone di un numero limitato di "risultati", come si arriva alla fase di selezione?
Thomas Ahle,

0

L'algoritmo implementato da user515430 da Edsger Dijkstra (http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF) è probabilmente il più veloce possibile. Chiamo ogni numero che è una forma di 2^i * 5^j"numero speciale". Ora la risposta di Vlads sarebbe O(i*j)ma con un doppio algoritmo, uno per generare i numeri speciali O(i*j)e uno per ordinarli (secondo l'articolo collegato ancheO(i*j) .

Ma controlliamo l'algoritmo di Dijkstra (vedi sotto). In questo caso nè la quantità di numeri speciali che stiamo generando, quindi uguale i*j. Stiamo 1 -> neseguendo il loop una volta e in ogni loop eseguiamo un'azione costante. Quindi anche questo algoritmo lo èO(i*j) . E anche con una costante veloce piuttosto ardente.

La mia implementazione in C ++ con GMP (wrapper C ++) e dipendenza boost::lexical_cast, sebbene possa essere facilmente rimossa (sono pigro e chi non usa Boost?). Compilato con g++ -O3 test.cpp -lgmpxx -o test. Su Q6600 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;
}

0

Se disegni una matrice con i come riga e j come colonna puoi vedere lo schema. Inizia con i = 0 e poi attraversa la matrice salendo 2 righe e 1 colonna a destra fino a raggiungere la parte superiore della matrice (j> = 0). Quindi vai a + 1, ecc ...

Quindi per i = 7 viaggi così:

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

E per i = 8:

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

Eccolo in Java che sale a i = 9. Stampa la posizione della matrice (i, j) e il valore.

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

La mia intuizione :

Se prendo il valore iniziale come 1 dove i = 0, j = 0, allora posso creare i numeri successivi come (2 ^ 1) (5 ^ 0), (2 ^ 2) (5 ^ 0), (2 ^ 0) * (5 ^ 1), ... ovvero 2,4,5 ..

Diciamo che in qualsiasi momento il mio numero è x. quindi posso creare i numeri successivi nei seguenti modi:

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

Spiegazione :

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.

Prova

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]
  • Iniziamo con x = 1.

    I successivi tre numeri sono 1 * 2, 1 * 4, 1 * 5 [2,4,5]; Arr [1,2,4,5]

  • Ora x = 2

    I prossimi tre numeri sono [4,8,10] {Poiché già si sono verificati 4, lo ignoreremo} [8,10]; Arr [1,2,4,5,8,10]

  • Ora x = 4

    Prossimi tre numeri [8,16,20] {8 già avvenuti ignorarlo} [16,20] Arr [1,2,4,5,8,10,16,20]

  • x = 5

    I prossimi tre numeri [10,20,25] {10,20} già così [25] viene aggiunto Arr [1,2,4,5,8,10,16,20,25]

Condizioni di terminazione

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

Analisi

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

0

Era solo curioso di sapere cosa aspettarsi la prossima settimana e ho trovato questa domanda.

Penso che l'idea sia 2 ^ I non aumenta in quei grandi passi di 5 ^ j. Quindi aumenta i fintanto che il prossimo j-step non sarebbe più grande.

L'esempio in C ++ (Qt è facoltativo):

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

    }
}

Il risultato:

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

Questa soluzione manca alcune combinazioni. Ad esempio, non esamina il caso in cui i = 1, j = 2 in ogni caso in cui i = 1 e j> 1 del resto.
Federico

@Federico: hai ragione! Non c'è da stupirsi perché ho fallito due volte le interviste su Google con un intervallo di 6 anni, ma quasi le stesse domande :-)
Valentin Heinitz,

0

Ecco la mia soluzione

#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 */
 }

Risultato:

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

So che probabilmente mi sbaglio, ma qui c'è un'euristica molto semplice poiché non coinvolge molti numeri come 2,3,5. Sappiamo che per ogni i, j 2 ^ i * 5 ^ j la sequenza successiva sarebbe 2 ^ (i-2) * 5 ^ (j + 1). Essere un google q deve avere una soluzione semplice.

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

Questo produce output come:

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

potrebbe funzionare fino a 20 o 200, ma a un certo punto inizierà a saltare alcuni numeri e / o ad emetterli nell'ordine sbagliato.
Will Ness,

0

Se segui ciò che sta realmente accadendo quando incrementiamo i o j nell'espressione 2^i * 5^j , stai moltiplicando per un altro 2 o un altro 5. Se riaffermiamo il problema come - dato un valore particolare di iej, come troveresti il ​​prossimo maggiore valore, la soluzione diventa evidente.

Ecco le regole che possiamo enumerare in modo abbastanza intuitivo:

  • Se i > 1nell'espressione è presente una coppia di 2 ( ), dovremmo sostituirli con un 5 per ottenere il numero più grande successivo. Quindi, i -= 2e j += 1.
  • Altrimenti, se c'è un 5 ( j > 0), dobbiamo sostituirlo con tre 2s. Quindi j -= 1e i += 3.
  • Altrimenti, dobbiamo solo fornire altri 2 per aumentare il valore di un minimo. i += 1.

Ecco il programma in 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

Questo non funziona poiché "i" non supera mai 4, quindi non verranno mai visualizzati multipli di 32 (2 ^ 5).
threenplusone,

0

Se ci è permesso usare java Collection allora possiamo avere questi numeri in 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);
}

Qui powerLimit deve essere inizializzato con molta attenzione !! A seconda di quanti numeri vuoi.


ciò produce risultati errati: 2 ^ 8 = 256 mancanti prima di 2 ^ 6 * 5 = 320. l'area di enumerazione è triangolare, non rettangolare.
Will Ness,

@WillNess How ?? Quando imposto powerLimit = 9, questo frammento restituisce i seguenti numeri 1 2 4 5 8 10 16 20 25 32 40 50 64 80 100 125 128 160 200 250 256 320 400 500
kavi temre

no, produce 100 numeri. come fai a sapere dove fermarsi? devi spiegarlo. --- Ho fatto riferimento a 7 come presente nello snippet di codice. per questo di essere una risposta valida, è necessario spiegare esattamente come impostare il limite per una data quantità di numeri, e quanti numeri si è sovrapproduzione .
Will Ness,

0

Ecco il mio tentativo con 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

Produzione:

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.