A friend of mine is interviewing for a job. One of the interview questions got me thinking, just wanted some feedback.
There are 2 non-negative integers: i and j. Gi
Using dynamic programming you can do this in O(n). Ground truth is that no values of i and j can give us 0, and to get 1 both values must be 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
}
}
Whenever you call this function check if i and j are set, if they are not null, then populate TwoCount
and FiveCount
C++ answer. Sorry for bad coding style, but i'm in a hurry :(
#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;
}
}
}
Obviously you can use data structures other than array to dynamically increase your storage etc. This is just a sketch to prove that it works.
My Intuition :
If I take initial value as 1 where i=0, j=0, then I can create next numbers as (2^1)(5^0), (2^2)(5^0), (2^0)*(5^1), ... i.e 2,4,5 ..
Let say at any point my number is x. then I can create next numbers in the following ways :
Explanation :
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]
Lets start with x = 1.
Next three numbers are 1*2, 1*4, 1*5 [2,4,5]; Arr[1,2,4,5]
Now x = 2
Next three numbers are [4,8,10] {Since 4 already occurred we will ignore it} [8,10]; Arr[1,2,4,5,8,10]
Now x =4
Next three numbers [8,16,20] {8 already occurred ignore it} [16,20] Arr[1,2,4,5,8,10,16,20]
x = 5
Next three numbers [10,20,25] {10,20} already so [25] is added Arr[1,2,4,5,8,10,16,20,25]
Termination Condition
Terminating condition when Arr last number becomes greater
than (5^m1 * 2^m2), where m1,m2 are given by user.
Analysis
Time Complexity : O(K) : where k is numbers possible between i,j=0 to
i=m1,j=m2.
Space Complexity : O(K)
Just was curious what to expect next week and have found this question.
I think, the idea is 2^i increases not in that big steps as 5^j. So increase i as long as next j-step wouldn't be bigger.
The example in C++ (Qt is optional):
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 );
}
}
The output:
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
This is very easy to do O(n)
in functional languages. The list l
of 2^i*5^j
numbers can be simply defined as 1
and then 2*l
and 5*l
merged. Here is how it looks 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)
The merge
function gives you a new value in constant time. So does map
and hence so does l
.
You know that log_2(5)=2.32. From this we note that 2^2 < 5 and 2^3 > 5.
Now look a matrix of possible answers:
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 ...
Now, for this example, choose the numbers in order. There ordering would be:
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...
Note that every row starts 2 columns behind the row starting it. For instance, i=0 j=1 comes directly after i=2 j=0.
An algorithm we can derive from this pattern is therefore (assume 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);
}
}
NOTE: The code here caps the values of the exponents of i and j to be less than 10. You could easily extend this algorithm to fit into any other arbitrary bounds.
NOTE: The running time for this algorithm is O(n) for the first n answers.
NOTE: The space complexity for this algorithm is O(1)
If you go by what's really happening when we increment i or j in the expression 2^i * 5^j
, you are either multiplying by another 2 or another 5. If we restate the problem as - given a particular value of i and j, how would you find the next greater value, the solution becomes apparent.
Here are the rules we can quite intuitively enumerate:
i > 1
) in the expression, we should replace them with a 5 to get the next biggest number. Thus, i -= 2
and j += 1
.j > 0
), we need to replace it with three 2s. So j -= 1
and i += 3
.i += 1
.Here's the program 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