Question d'entrevue délicate avec Google


169

Un de mes amis est en entrevue pour un emploi. L'une des questions de l'entrevue m'a fait réfléchir, je voulais juste un commentaire.

Il existe 2 entiers non négatifs: i et j. Étant donné l'équation suivante, trouvez une solution (optimale) pour itérer sur i et j de manière à ce que la sortie soit triée.

2^i * 5^j

Ainsi, les premiers tours ressembleraient à ceci:

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

Essayez comme je pourrais, je ne vois pas de modèle. Tes pensées?


63
L'algorithme optimal en termes de temps programmeur est de générer avec deux boucles imbriquées, puis de trier. Pourquoi posent-ils des questions comme celle-ci?
Tom Zych

21
Vous pourrez peut-être déterminer les points de transition en regardant le nombre le plus élevé. 2^2 < 5mais 2^3 > 5donc à ce point vous augmentez j. Je pense que vous pouvez produire la sortie en O (n) plutôt qu'en O (nlgn). @ tom-zynch deux boucles imbriquées est O (n ^ 2). Cette question est très valable
Mikhail

1
Il n'y a qu'une seule sortie, donc la solution optimale est O (n). Lisez ma solution ci
Mikhail

3
Une question similaire a été abordée avant apparemment: stackoverflow.com/questions/4600048/nth-ugly-number .

1
... et le PO devrait probablement déjà choisir une réponse. Après tout, il en a déjà plein de bons.
abeln

Réponses:


123

Dijkstra tire une solution éloquente dans "Une discipline de la programmation". Il attribue le problème à Hamming. Voici ma mise en œuvre de la solution de 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
Lien pertinent: en.wikipedia.org/wiki/Regular_number#Algorithms . Au fait, je ne pense pas que ce soit une très bonne question d'entrevue. Voici un (article manuscrit) de Dijkstra où il fournit et prouve un algorithme pour ce problème: cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF
Elian Ebbing

Lorsque l'objectif est "d'itérer sur i et j", vous avez besoin de moins de capacité de stockage, un FIFO suffit. Voir ma solution Python.
GaBorgulya

7
Lorsque le but est "d'itérer sur i et j", ce n'est pas le même problème.
mhum

C'est une très belle implémentation, utilisant un minimum de mémoire. C'est une mémoire linéaire même si vous ne voulez qu'un seul nombre.
Thomas Ahle

1
@ThomasAhle Je ne sais pas si vous avez vu cela, mais il contient du code à la fin qui est capable de calculer le n-ième nombre de manière isolée. Comme par exemple un milliardième nombre .
Will Ness

47

voici une façon plus raffinée de le faire (plus raffinée que ma réponse précédente, c'est-à-dire):

imaginez que les nombres sont placés dans une 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

ce que vous devez faire est de «parcourir» cette matrice, en commençant par (0,0). Vous devez également garder une trace de vos prochains mouvements possibles. Lorsque vous commencez par (0,0)vous n'avez que deux options: soit (0,1)ou (1,0): puisque la valeur de (0,1)est plus petite, vous choisissez cela. puis faites de même pour votre prochain choix (0,2)ou (1,0). , Vous avez jusqu'à présent la liste suivante: 1, 2, 4. Votre prochain mouvement est (1,0)que la valeur y est inférieure à (0,3). Cependant, vous avez maintenant trois choix pour votre prochain mouvement: soit (0,3), ou (1,1), ou (2,0).

Vous n'avez pas besoin de la matrice pour obtenir la liste, mais vous devez garder une trace de tous vos choix (c'est-à-dire que lorsque vous atteignez 125+, vous aurez 4 choix).


J'ai voté parce que je pensais dans le même sens, mais dans le cas général, ne serait-ce pas quelque chose comme O (i ^ 2 * j)? Vous devrez vérifier plusieurs numéros pour chaque numéro que vous produisez.
Tom Zych

1
@Tom vous devez vérifier plus d'un nombre, mais ce n'est pas si mal: lorsque vous sortez des nombres entre 125 et 625, vous devez regarder 4 valeurs. entre 625 et 3025, vous regardez 5 valeurs. alors vraiment, il jvérifie pour chaque sortie 1
vlad

+1: Combinez avec cette question: stackoverflow.com/questions/5000836/search-algorithm et on dirait que nous avons une solution O (n).

@Moron zut, je ne veux pas payer 25 $ pour cet algorithme, mais ça a l'air intéressant.
vlad

1
en fait, j ~ n^0.5pour la n-ième valeur d'une séquence, puisque les nvaleurs remplissent une zone sur le i x jplan. Donc, cet algo est le O(n^1.5)temps, avec l' O(n^0.5)espace. Mais il existe un algorithme de temps linéaire avec le même espace complxty de n^0.5, et l'algo mini-tas de la réponse ci-dessous est le O(n*log(n))temps avec le même n^0.5espace.
Will Ness le

25

Utilisez un tas min.

Mettez 1.

extrait-Min. Dites que vous obtenez x.

Poussez 2x et 5x dans le tas.

Répéter.

Au lieu de stocker x = 2 ^ i * 5 ^ j, vous pouvez stocker (i, j) et utiliser une fonction de comparaison personnalisée.


1
Un tas donnerait à lg n du temps sur ses opérations, ce qui pousse la complexité à n lg n.
corsiKa

@glow: Oui, je ne vois aucune solution O (n) publiée jusqu'à présent, cependant :-)

@abel: Ce commentaire est vieux :-) On dirait qu'il aura des problèmes pour passer de (1,1) à (4,0) aussi. Mais le considérer comme une matrice de jeune (voir la réponse de vlad) permet en fait un algorithme de temps O (n).

@Moron: Je ne pense pas qu'il y ait quelque chose de mal avec cette solution. Certainement rien de mal dans les 30 premiers éléments, que je viens de vérifier tout de suite (cela couvrirait le cas (1,1) -> (4,0)).
abeln

@abel: Oui, je n'ai pas vraiment essayé de l'exécuter :-) Il y a peut-être une preuve facile de son exactitude aussi. FWIW, il a déjà mon +1.

13

Une solution basée sur FIFO nécessite moins de capacité de stockage. Code 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))

production:

  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

C'est très facile à faire O(n)dans les langages fonctionnels. La liste ldes 2^i*5^jnombres peut être simplement définie comme 1et ensuite 2*let 5*lfusionnée. Voici à quoi cela ressemble dans 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 mergefonction vous donne une nouvelle valeur en temps constant. Il en va de même mapet par conséquent l.


Je pense que 'k' n'est pas défini
Ither

2
appelons simplement cette fonction "fusion" à la unionplace, car elle supprime les doublons. merge, dans le cadre de mergesort, doit conserver les doublons provenant de ses deux séquences d'entrée. Voir le Data.List.Orderedpaquet pour les trucs connexes.
Will Ness

1
+1 pour Data.List.Ordered.union. Cela en fait une ligne:xs = 1 : union (map (2*) xs) (map (5*) xs)
Phob

@GaBorgulya Oui, il comprend cinq fois la liste, [1, 2, 4, 5,...]donc il comprend 5*4.
Thomas Ahle

1
@Phob Oui, c'est la Data.List.Ordered.unionfonction. À ne pas confondre avec Data.List.union.
Thomas Ahle

5

Vous devez garder une trace de leurs exposants individuels et de leurs sommes

vous devez donc commencer par f(0,0) --> 1 incrémenter l'un d'entre eux:

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

nous savons donc que 2 est le suivant - nous savons également que nous pouvons incrémenter l'exposant de i jusqu'à ce que la somme dépasse 5.

Vous continuez à aller et venir comme ça jusqu'à ce que vous soyez à votre nombre de tours défini.


Oui, ça l'est. Vous effectuez une opération O (1) pour chaque tour. Parfois, vous faites le tour tôt, mais quand vous arrivez à ce tour, vous n'êtes pas obligé de le faire là-bas, donc ça marche tout seul.
corsiKa

19
Comment passez-vous de (1,1) à (4,0)? Veuillez expliquer exactement quel est votre algorithme.

Le problème est que vous n'avez pas seulement deux possibilités incrémentielles - par exemple, vous n'en avez pas fini f(*,2)simplement parce que vous l'avez trouvé f(a1,b+1)>f(a2,b). Une approche incrémentielle générera éventuellement un nombre illimité de paires voisines de la région que vous avez déjà sortie.
comingstorm

@ user515430 a fourni une implémentation qui était plus que ce que je pouvais faire pendant ma pause déjeuner, mais c'est ce que j'essayais de faire.
corsiKa

4

En utilisant la programmation dynamique, vous pouvez le faire en O (n). La vérité fondamentale est qu'aucune valeur de i et j ne peut nous donner 0, et pour obtenir 1 les deux valeurs doivent être 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
    }
}

Chaque fois que vous appelez cette fonction, vérifiez si i et j sont définis, s'ils ne sont pas nuls, puis remplissez TwoCountetFiveCount


Réponse C ++. Désolé pour le mauvais style de codage, mais je suis pressé :(

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

Évidemment, vous pouvez utiliser des structures de données autres que des tableaux pour augmenter dynamiquement votre stockage, etc. Ceci est juste un croquis pour prouver que cela fonctionne.


4
Cela semble être une réponse intéressante, mais je ne vois pas comment cela fonctionne vraiment. Pouvez-vous ajouter plus de détails?
David Brunelle

Après l'avoir étudié moi-même, je ne vois vraiment pas comment cela fonctionne. En supposant une division entière, cela donnera exactement le même résultat pour 3 que pour 2. De plus, si les conditions if sont des tests pour non nul, cela ne fonctionnera jamais, car il n'y a pas d'entrées non nulles.
David Thornley

Publié une version C ++ pour tous les non-disants. @David Vos commentaires sont corrects, mais mon code d'origine était un pseudo-code et je pensais en termes de script, donc division non entière et distinction entre entrée nulle et entrée de valeur 0
Mikhail

ce code énumère tous les nombres naturels, donc, par commentaire de @ThomasAhle à la réponse par "Lost in Alabama" ci-dessous, il faut O(exp(sqrt(n))), pour produire les nnombres de la séquence. Un algorithme linéaire existe, par exemple tel que donné par ThomasAhle.
Will Ness

1
Vous avez raison. Dans ma compréhension, cela O(n)signifie nêtre la dernière valeur, pas le nombre d'articles imprimés, ce qui n'est pas correct. Je ne sais pas comment fonctionnent les langages fonctionnels ou comment la fusion fonctionne en temps constant, mais sa réponse a obtenu mon vote positif
Mikhail

2

Pourquoi ne pas essayer de regarder cela de l'autre côté. Utilisez un compteur pour tester les réponses possibles par rapport à la formule originale. Désolé pour le pseudo code.

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

Cela fonctionne dans environ O(4^sqrt(n))parce que le nthnuméro de la séquence est d'environ cette taille.
Thomas Ahle

2

Il s'agit de l'entrée pertinente chez OEIS.

Il semble possible d'obtenir la séquence ordonnée en générant les premiers termes, disons

1 2 4 5

puis, à partir du deuxième terme, multiplier par 4 et 5 pour obtenir les deux suivants

1 2 4 5 8 10

1 2 4 5 8 10 16 20

1 2 4 5 8 10 16 20 25

etc...

Intuitivement, cela semble correct, mais il manque bien sûr une preuve.


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

1
@NateKerkhofs, 512 est généré mais il est hors service car 512 est inférieur au 625 déjà généré; l'algorithme aurait besoin d'une logique supplémentaire pour mettre la sortie dans l'ordre - Ainsi, l'algorithme n'est pas aussi simple que proposé et pas du tout le même algorithme.
GordonBGood

1

Vous savez que log_2 (5) = 2,32. De cela, nous notons que 2 ^ 2 <5 et 2 ^ 3> 5.

Maintenant, regardez une matrice de réponses possibles:

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

Maintenant, pour cet exemple, choisissez les nombres dans l'ordre. Là, la commande serait:

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

Notez que chaque ligne commence 2 colonnes derrière la ligne qui la commence. Par exemple, i = 0 j = 1 vient directement après i = 2 j = 0.

Un algorithme que nous pouvons déduire de ce modèle est donc (supposons 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);
    }
}   

REMARQUE: le code ici limite les valeurs des exposants de i et j à moins de 10. Vous pouvez facilement étendre cet algorithme pour qu'il tienne dans toutes les autres limites arbitraires.

REMARQUE: le temps d'exécution de cet algorithme est O (n) pour les n premières réponses.

REMARQUE: la complexité spatiale pour cet algorithme est O (1)


Vous avez écrit "chaque ligne commence 2 colonnes derrière la ligne qui la commence". Cependant 2 ^ 9 = 512 et 5 ^ 4 = 625, donc ce n'est pas vrai pour la ligne 4.
GaBorgulya

@ user678105 Vous avez raison. Ce code ne fonctionne pas. Désolé tout. Ce code ne fonctionne pas à cause de l'arrondissement du journal et de mon hypothèse que cela n'avait pas d'importance.
KLee1

1
Voici comment résoudre ce problème. Sur le plan (x, y) plein de points à coefficients entiers, tracez une ligne de (0,1) à (log2 (5), 0). (0,0) est dans le coin supérieur gauche. L'axe X va vers la droite, l'axe Y descend. Tracez maintenant une ligne à partir du point d'origine (0,0) qui est perpendiculaire à la 1ère ligne. Faites maintenant glisser la première ligne le long de la seconde, de plus en plus loin de l'origine, et collectez les points de coordonnées entières au fur et à mesure qu'ils sont traversés. Pour une séquence générée par {2,3,5}, ce sera un plan se déplaçant à travers, dans l'espace (i, j, k). Si vous pouvez traduire cette idée en code, faites-moi signe. :)
Will Ness

1

Ma mise en œuvre est basée sur les idées suivantes:

  • Utilisez deux files d'attente Q2 et Q5, toutes deux initialisées avec 1. Nous garderons les deux files d'attente dans l'ordre trié.
  • À chaque étape, retirez de la file d'attente le plus petit élément numérique MIN de Q2 ou Q5 et imprimez-le. Si Q2 et Q5 ont le même élément - supprimez les deux. Imprimez ce numéro. Il s'agit essentiellement de fusionner deux tableaux triés - à chaque étape, choisissez le plus petit élément et avancez.
  • Mettre en file d'attente MIN * 2 à Q2 et MIN * 5 à Q5. Cette modification ne rompt pas l'invariant de Q2 / Q5 en cours de tri, car MIN est supérieur au numéro MIN précédent.

Exemple:

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

Code en 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

calculer les résultats et les mettre dans une liste triée, avec les valeurs pour ietj


Cela vous donnera probablement des trous à la fin de votre séquence. Par exemple, vous aurez 2^n*5^nmais pas 2^(n+1)*5^(n-1)ce qui est plus petit.
Thomas Ahle

@Thomas Je ne suis pas sûr de suivre votre logique ici. Si vous en calculez un, pourquoi ne calculeriez-vous pas également l'autre?
vlad

2
@vlad Vous devez avoir une limite sur vos iet vos j, n'est-ce pas? Sinon, vous n'obtiendrez jamais l'état de tri, et par conséquent, vous ne retournerez jamais une seule valeur. Mais quelle que soit la limite que nvous choisissez, votre liste sera imparfaite.
Thomas Ahle

@Thomas votre argument n'a toujours pas de sens. Le PO n'a jamais précisé la fin de sa liste de résultats. S'il le fait, vous pouvez trouver le max iet j.
vlad

1
@vlad En lisant votre réponse, vous calculez d'abord les "résultats" / les 2^i*5^jvaleurs, puis les triez. Si vous n'avez pas un nombre limité de "résultats", comment allez-vous arriver à l'étape de tri?
Thomas Ahle

0

L'algorithme implémenté par user515430 par Edsger Dijkstra (http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF) est probablement aussi rapide que possible. J'appelle chaque numéro qui est une forme de 2^i * 5^j"numéro spécial". Maintenant, la réponse de Vlads serait O(i*j)mais avec un double algorithme, un pour générer les numéros spéciaux O(i*j)et un pour les trier (selon l'article lié égalementO(i*j) .

Mais vérifions l'algorithme de Dijkstra (voir ci-dessous). Dans ce cas, nla quantité de nombres spéciaux que nous générons est donc égale à i*j. Nous faisons une boucle une fois, 1 -> net dans chaque boucle nous effectuons une action constante. Donc, cet algorithme l'est également O(i*j). Et avec une constante assez rapide aussi.

Mon implémentation en C ++ avec GMP (wrapper C ++), et la dépendance sur boost::lexical_cast, bien que cela puisse être facilement supprimé (je suis paresseux, et qui n'utilise pas Boost?). Compilé avec g++ -O3 test.cpp -lgmpxx -o test. Sur Q6600, Ubuntu 10.10 time ./test 1000000donne 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

Si vous dessinez une matrice avec i comme ligne et j comme colonne, vous pouvez voir le modèle. Commencez par i = 0 puis parcourez simplement la matrice en remontant de 2 lignes et à droite de 1 colonne jusqu'à atteindre le haut de la matrice (j> = 0). Alors allez i + 1, etc ...

Donc pour i = 7 vous voyagez comme ceci:

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

Et pour i = 8:

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

Le voici en Java allant jusqu'à i = 9. Il imprime la position de la matrice (i, j) et la valeur.

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

Mon intuition :

Si je prends la valeur initiale comme 1 où i = 0, j = 0, alors je peux créer les nombres suivants comme (2 ^ 1) (5 ^ 0), (2 ^ 2) (5 ^ 0), (2 ^ 0) * (5 ^ 1), ... soit 2,4,5 ..

Disons qu'à tout moment, mon nombre est x. alors je peux créer les numéros suivants de la manière suivante:

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

Explication :

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.

Essai

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]
  • Commençons par x = 1.

    Les trois nombres suivants sont 1 * 2, 1 * 4, 1 * 5 [2,4,5]; Arr [1,2,4,5]

  • Maintenant x = 2

    Les trois nombres suivants sont [4,8,10] {Puisque 4 est déjà arrivé, nous l'ignorerons} [8,10]; Arr [1,2,4,5,8,10]

  • Maintenant x = 4

    Les trois numéros suivants [8,16,20] {8 sont déjà apparus ignorer} [16,20] Arr [1,2,4,5,8,10,16,20]

  • x = 5

    Trois nombres suivants [10,20,25] {10,20} déjà donc [25] est ajouté Arr [1,2,4,5,8,10,16,20,25]

Condition de résiliation

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

Une analyse

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

0

J'étais juste curieux de savoir à quoi s'attendre la semaine prochaine et j'ai trouvé cette question.

Je pense que l'idée est de 2 ^ i n'augmente pas dans ce grand pas comme 5 ^ j. Augmentez donc i tant que le prochain pas j ne serait pas plus grand.

L'exemple en C ++ (Qt est facultatif):

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

    }
}

Le résultat:

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

Cette solution manque certaines combinaisons. Par exemple, il n'examine pas le cas où i = 1, j = 2 tout cas où i = 1 et j> 1 d'ailleurs ..
Federico

@Federico: Vous avez raison! Pas étonnant que j'aie échoué aux interviews google deux fois avec un intervalle de 6 ans mais à peu près les mêmes questions :-)
Valentin Heinitz

0

Voici ma solution

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

Résultat :

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

Je sais que je me trompe probablement, mais il y a ici une heuristique très simple car elle n'implique pas beaucoup de nombres comme 2,3,5. Nous savons que pour tout i, j 2 ^ i * 5 ^ j la séquence suivante serait 2 ^ (i-2) * 5 ^ (j + 1). Être un google q doit avoir une solution simple.

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

Cela produit une sortie comme:

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

cela peut fonctionner jusqu'à 20 ou 200, mais à un moment donné, il commencera à sauter certains nombres et / ou à les afficher dans le mauvais ordre.
Will Ness

0

Si vous passez par ce qui se passe réellement lorsque nous incrémentons i ou j dans l'expression 2^i * 5^j, vous multipliez soit par un autre 2, soit par un autre 5. Si nous reformulons le problème comme - étant donné une valeur particulière de i et j, comment trouveriez-vous la suivante plus grande valeur, la solution devient apparente.

Voici les règles que nous pouvons énumérer assez intuitivement:

  • S'il y a une paire de 2 ( i > 1) dans l'expression, nous devrions les remplacer par un 5 pour obtenir le prochain plus grand nombre. Ainsi, i -= 2et j += 1.
  • Sinon, s'il y a un 5 ( j > 0), nous devons le remplacer par trois 2. Alors j -= 1et i += 3.
  • Sinon, nous devons simplement fournir 2 autres pour augmenter la valeur d'un minimum. i += 1.

Voici le programme en 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

Cela ne fonctionne pas car «i» ne dépasse jamais 4, donc aucun multiple de 32 (2 ^ 5) n'apparaîtra jamais.
threenplusone

0

Si nous sommes autorisés à utiliser java Collection, nous pouvons avoir ces nombres dans 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);
}

Ici, powerLimit doit être initialisé très soigneusement !! En fonction du nombre de numéros que vous souhaitez.


cela produit des résultats erronés: 2 ^ 8 = 256 est manquant avant 2 ^ 6 * 5 = 320. la zone de dénombrement est triangulaire et non rectangulaire.
Will Ness

@WillNess Comment ?? Lorsque je règle powerLimit = 9, cet extrait de code renvoie les nombres suivants 1 2 4 5 8 10 16 20 25 32 40 50 64
80100125160200250256320400500

non, il produit 100 nombres. comment savez-vous où vous arrêter? vous devez expliquer cela. --- J'ai fait référence à 7 comme étant présent dans votre extrait de code. pour que cela soit une réponse valide, vous devez expliquer exactement comment fixer la limite pour un nombre donné de nombres, et combien de nombres il surproduira .
Will Ness du

0

Voici ma tentative avec 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

Production:

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)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.