Un de mes amis interviewe pour un emploi. L'une des questions d'entrevue m'a fait réfléchir, je voulais juste des commentaires.
Il y a 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 telle sorte 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 motif. Tes pensées?
Dijkstra tire une solution éloquente dans "Une discipline de programmation". Il attribue le problème à Hamming. Voici mon implémentation 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;
}
voici une façon plus raffinée de le faire (plus raffinée que ma réponse précédente, c'est-à-dire):
imaginez 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 à (0,0)
vous n'avez que deux options: soit (0,1)
ou (1,0)
: puisque la valeur de (0,1)
est plus petit, vous choisissez cela. faites de même pour votre prochain choix (0,2)
ou (1,0)
. Jusqu'à présent, vous avez la liste suivante: 1, 2, 4
. Votre prochain coup est (1,0)
puisque la valeur y est inférieure à (0,3)
. Cependant, vous avez maintenant trois choix pour votre prochain coup: 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 atteindrez 125+, vous aurez 4 choix).
Utilisez un tas Min.
Mettez 1.
extrait-Min. Disons 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.
Une solution basée sur FIFO nécessite moins de capacité de stockage. Python code.
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))
sortie:
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
C'est très facile à faire O(n)
dans les langages fonctionnels. La liste l
de 2^i*5^j
les nombres peuvent être simplement définis comme 1
et alors 2*l
et 5*l
fusionné. Voici à quoi cela ressemble à 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 fonction merge
vous donne une nouvelle valeur en temps constant. map
aussi, et donc l
.
Vous devez garder une trace de leurs représentants individuels, et quelles seraient leurs sommes
vous commencez donc avec f(0,0) --> 1
maintenant vous devez incrémenter l'un d'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 à faire des allers-retours comme ceci jusqu'à ce que vous ayez atteint votre nombre de tours défini.
En utilisant la programmation dynamique, vous pouvez le faire dans 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, remplissez TwoCount
et FiveCount
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. Ce n'est qu'un croquis pour prouver que cela fonctionne.
Pourquoi ne pas essayer de regarder cela dans l'autre sens. Utilisez un compteur pour tester les réponses possibles par rapport à la formule d'origine. 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
}
}
This est l'entrée pertinente à 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 trimestre, en multipliant 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 bien sûr, il manque une preuve.
Vous savez que log_2 (5) = 2,32. On en déduit que 2 ^ 2 <5 et 2 ^ 3> 5.
Regardez maintenant 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 numéros dans l'ordre. Il y aurait commande:
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 démarre. Par exemple, i = 0 j = 1 vient directement après i = 2 j = 0.
Un algorithme que nous pouvons dériver 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 l'adapter à toute autre limite arbitraire.
REMARQUE: le temps d'exécution de cet algorithme est O(n) pour les n premières réponses.
REMARQUE: la complexité de l'espace pour cet algorithme est O (1)
Ma mise en œuvre est basée sur les idées suivantes:
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);
}
}
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 des manières suivantes:
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.
Test Run
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 se sont déjà produits, nous l'ignorerons} [8,10]; Arr [1,2,4,5,8,10]
Maintenant x = 4
Les trois numéros suivants [8,16,20] {8 déjà survenus, ignorez-le} [16,20] Arr [1,2,4,5,8,10,16,20]
x = 5
Les trois nombres suivants [10,20,25] {10,20} déjà ainsi [25] sont ajoutés 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.
Analyse
Time Complexity : O(K) : where k is numbers possible between i,j=0 to
i=m1,j=m2.
Space Complexity : O(K)
Je sais que je me trompe probablement mais il y a une heuristique très simple ici 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
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
Si vous dessinez une matrice avec i comme ligne et j comme colonne, vous pouvez voir le motif. Commencez avec i = 0, puis traversez simplement la matrice en remontant 2 lignes et 1 colonne de droite jusqu'à ce que vous atteigniez 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 affiche 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++;
}
}
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
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)
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
Un "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 nombres spéciaux O(i*j)
et un pour les trier (selon l'article lié également O(i*j)
.
Mais vérifions l'algorithme de Dijkstra (voir ci-dessous). Dans ce cas, n
est le nombre de nombres spéciaux que nous générons, donc égal à i*j
. Nous bouclons une fois, 1 -> n
Et dans chaque boucle nous effectuons une action constante. Donc, cet algorithme est également O(i*j)
. Et avec une constante assez rapide.
Mon implémentation en C++ avec GMP (wrapper C++), et la dépendance à 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 1000000
Donne 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;
}
calculer les résultats et les mettre dans une liste triée, avec les valeurs de i
et j
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 par un autre 2 ou un autre 5. Si nous reformulons le problème comme - étant donné une valeur particulière de i et j, comment trouveriez-vous la prochaine valeur supérieure, la solution devient apparente.
Voici les règles que nous pouvons énumérer assez intuitivement:
i > 1
) dans l'expression, nous devons les remplacer par un 5 pour obtenir le prochain plus grand nombre. Ainsi, i -= 2
et j += 1
.j > 0
), nous devons le remplacer par trois 2. Alors j -= 1
et i += 3
.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
Si nous sommes autorisés à utiliser Java Collection alors 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 !! Selon le nombre de numéros que vous souhaitez.
Était juste curieux de savoir à quoi s'attendre la semaine prochaine et ont trouvé cette question.
Je pense que l'idée est de 2 ^ i n'augmente pas à ce niveau comme 5 ^ j. Augmentez donc i tant que la prochaine étape j ne sera pas plus grande.
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