How to calculate Big O of Dynamic programming (Memoization) algorithm - java

How would I go about calculating the big O of a DP algorithm. I've come to realize my methods for calculating algorithms doesn't always work. I would use simple tricks to extract what the Big O was. For example if I were evaluating the none memoized version of the algorithm below (removing the cache mechanism) I would look at the number of times the recursive method called itself in this case 3 times. I would then raise this value to n giving O(3^n). With DP that isn't right at all because the recursive stack doesn't go as deep. My intuition tells me that the Big O of the DP solution would be O(n^3). How would we verbally explain how we came up with this answer. More importantly what is a technique that can be used to find the Big O of similar problems. Since it is DP I'm sure the number of sub problems is important how do we calculate the number of sub problems.
public class StairCase {
public int getPossibleStepCombination(int n) {
Integer[] memo = new Integer[n+1];
return getNumOfStepCombos(n, memo);
}
private int getNumOfStepCombos(int n, Integer[] memo) {
if(n < 0) return 0;
if(n == 0) return 1;
if(memo[n] != null) return memo[n];
memo[n] = getNumOfStepCombos(n - 1, memo) + getNumOfStepCombos(n - 2, memo) + getNumOfStepCombos(n-3,memo);
return memo[n];
}
}

The first 3 lines do nothing but compare int values, access an array by index, and see if an Integer reference is null. Those things are all O(1), so the only question is how many times the method is called recursively.
This question is very complicated, so I usually cheat. I just use a counter to see what's going on. (I've made your methods static for this, but in general you should avoid static mutable state wherever possible).
static int counter = 0;
public static int getPossibleStepCombination(int n) {
Integer[] memo = new Integer[n+1];
return getNumOfStepCombos(n, memo);
}
private static int getNumOfStepCombos(int n, Integer[] memo) {
counter++;
if(n < 0) return 0;
if(n == 0) return 1;
if(memo[n] != null) return memo[n];
memo[n] = getNumOfStepCombos(n - 1, memo) + getNumOfStepCombos(n - 2, memo) + getNumOfStepCombos(n-3,memo);
return memo[n];
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
counter = 0;
getPossibleStepCombination(i);
System.out.print(i + " => " + counter + ", ");
}
}
This program prints
0 => 1, 1 => 4, 2 => 7, 3 => 10, 4 => 13, 5 => 16, 6 => 19, 7 => 22, 8 => 25, 9 => 28,
so it looks like the final counter values are given by 3n + 1.
In a more complicated example, I might not be able to spot the pattern, so I enter the first few numbers (e.g. 1, 4, 7, 10, 13, 16) into the Online Encyclopedia of Integer Sequences and I usually get taken to a page containing a simple formula for the pattern.
Once you've cheated in this way to find out the rule, you can set about understanding why the rule works.
Here's how I understand where 3n + 1 comes from. For each value of n you only have to do the line
memo[n] = getNumOfStepCombos(n - 1, memo) + getNumOfStepCombos(n - 2, memo) + getNumOfStepCombos(n-3,memo);
exactly once. This is because we are recording the results and only doing this line if the answer has not already been calculated.
Therefore, when we start with n == 5 we run that line exacly 5 times; once for n == 5, once with n == 4, once with n == 3, once with n == 2 and once with n == 1. So that's 3 * 5 == 15 times the method getNumOfStepCombos gets called from itself. The method also gets called once from outside itself (from getPossibleStepCombination), so the total number of calls is 3n + 1.
Therefore this is an O(n) algorithm.
If an algorithm has lines that are not O(1) this counter method cannot be used directly, but you can often adapt the approach.

Paul's answer is technically not wrong but is a bit misleading. We should be calculating big O notation by how the function responds to changes in input size. Paul's answer of O(n) makes the complexity appear to be linear time when it really is exponential to the number of bits required to represent the number n. So for example, n=10 has ~30 calculations and m=2 bits. n=100 has ~300 calculations and m=3 bits. n=1000 has ~3000 calculations and m=4 bits.
I believe that your function's complexity would be O(2^m) where m is number of bits needed to represent n. I referred to https://www.quora.com/Why-is-the-Knapsack-problem-NP-complete-even-when-it-has-complexity-O-nW for a lot of my answer.

Related

find the Nth number of this sequence. Two set bits

Look at the following sequence:
3, 5, 6, 9, 10, 12, 17, 18, 20....
All the numbers in the series has exactly 2 bits set in their binary representation. Your task is simple, you have to find the Nth number of this sequence.
1 <= T <= 105
1 <= N <= 1014
public class Solution {
public static void main(String[] args) {
/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */
Scanner sc = new Scanner (System.in);
int t = sc.nextInt();
while ( t > 0 ){
int n = sc.nextInt();
t--;
int x =1;
while ( n > 0 ){
int y = 0;
while ( y < x ){
n--;
if ( n == 0 ){
System.out.println((1<<x)|(1<<y));
}
y++;
}
x++;
}
}
}
}
This is giving me a timeout error can i have an optimized solution of the given range of inputs
Examine The On-Line Encyclopedia of Integer Sequences
This is an integer sequence, which means we should be checking The On-Line Encyclopedia of Integer Sequences®. It frequently includes fairly optimal algorithms or mathematical expressions to produce elements in a specific integer sequence, so look there when you want an optimized solution.
After searching for 3, 5, 6, 9, 10, 12, 17, 18, 20, we find that this is OEIS sequence A018900, "Sum of two distinct powers of 2.", which includes several code snippets we should examine to determine which is fastest.
Fastest algorithm on OEIS page
Examining those snippets, the most efficient appears to be Smalltalk code by Hieronymus Fischer (Version 1 in the PROG section):
distinctPowersOf: b
"Version 1: Answers the n-th number of the form b^i + b^j, i>j>=0, where n is the receiver.
b > 1 (b = 2, for this sequence).
Usage: n distinctPowersOf: 2
Answer: a(n)"
| n i j |
n := self.
i := (8*n - 1) sqrtTruncated + 1 // 2.
j := n - (i*(i - 1)/2) - 1.
^(b raisedToInteger: i) + (b raisedToInteger: j)
Above code published in OEIS sequence A018900 on 20 April 2014, authored by Hieronymus Fischer, licensed by The Online Encyclopedia of Integer Sequences under the CC BY-NC 3.0 copyright license.
Appropriate data type
Signed 64-bit longs run out of space to hold the result and can begin to set incorrect bits after n exceeds 1,953. Since n won't exceed 1,014 in practice, long results will be fine.
Signed 32-bit ints run out of space after n exceeds 465, so they aren't large enough.
Solution using optimized algorithm
Here, we translate the Smalltalk algorithm to Java. Since optimized efficiency is your goal, we'll speed things up very slightly by using << 3 to multiply a small int value by eight and >>> 1 to perform floored division by two on a positive int:
import java.util.Scanner;
public class Solution {
// Gives the exact floor of the square root of x.
// based on Java algorithm by Programmer Olathe
// from http://www.codecodex.com/wiki/Calculate_an_integer_square_root#Java
public static final int floorSqrt(final int x) {
return (int) Math.sqrt(x);
} // Finds the nᵗʰ integer with exactly two bits set.
// Cannot properly handle n > 1953.
// based on Smalltalk algorithm by Hieronymus Fischer
// from https://oeis.org/A018900
public static final long nthWithTwoBitsSet(final int n) {
// Find the indexes of the two bits.
final int i = (floorSqrt((n << 3) - 1) + 1) >>> 1;
final int j = n - ((i*(i - 1)) >>> 1) - 1; // Return a long with the two bits set.
return (1L << i) | (1L << j);
} public static final void main(final String[] args) {
final Scanner in = new Scanner(System.in);
for (int t = in.nextInt(); t > 0; t--) {
System.out.println(nthWithTwoBitsSet(in.nextInt()));
}
}
}
Solution with slightly improved efficiency
We can gain further efficiency at the cost of bad design by combining all three methods into one:
import java.util.Scanner;
public class Solution {
// Cannot properly handle n > 1953.
// based on Java floored-square-root algorithm by Programmer Olathe
// from http://www.codecodex.com/wiki/Calculate_an_integer_square_root#Java
// based on Smalltalk nᵗʰ-with-two-bits-set algorithm by Hieronymus Fischer
// from https://oeis.org/A018900
public static final void main(final String[] args) {
final Scanner in = new Scanner(System.in);
for (int t = in.nextInt(); t > 0; t--) {
final int n = in.nextInt(); // Find the indexes of the two bits.
final int i = (((int) Math.sqrt((n << 3) - 1)) + 1) >>> 1;
final int j = n - ((i*(i - 1)) >>> 1) - 1; // Print a long with the two bits set.
System.out.println((1L << i) | (1L << j));
}
}
}
For my explanation I number the bit position from the least significant bit from 0. So 3 has bits 0 and 1 set. 5 has bits 0 and 2 set, etc.
There are 0 numbers where the most significant set bit is bit 0 (because then there is no other bit to set). 1 number where it’s bit 1 (3). Two numbers where it’s bit 2 (101 = 5 and 110 = 6). And so forth. m numbers where the most significant set bit is bit m.
This in turn means that up to and including numbers where bit b is the more significant of the two set bits, there are b * (b + 1) / 2 numbers. Let’s for a moment assume this is equal to N. Then according to the formual for solving a quadratic equation b = (sqrt(8 * N + 1) - 1) / 2. If this isn’t a whole number, it’s because N didn’t exactly equal the formula I said. Round up to find b and then find which other bit must be set for everything to agree.
I am on purpose not giving you the full solution. You wanted to solve this problem, you do the work. I hope my input is useful.
The other — smaller but easier — optimization is: Find the largest N among the test cases. Calculate the numbers of the sequence up to this largest N and put them into an array (you may modify your code from the question to do this). Then print all the required results by looking them up in the array. Language nitpicking: One may may argue that this is not literally an optimization since this word comes from latin optimus meaning best and it doesn’t produce the fastest possible program.

Complexity of coin algorithm

Can anyone tell me the complexity (Big O notation preferred) of this code? It finds the least number of "coins" needed to make a target sum.
To do this it calculates the least number of coins for each number up to the target starting from 1. Each number is worked out based on the possible pairs of numbers that could sum to it, and the pair with the smallest cost is used. An example hopefully makes this clearer
If the "coins" are {1, 3, 4} and the target is 13 then it iterates from 1 to 13, where the cost of 2 the minimum from (0+2, 1+1), the c(5) is the smallest cost of (c(0)+c(5), c(1)+c(4), c(2)+c(3)), etc up to c(13)
This is a version of the knapsack problem and I'm wondering how to define its complexity?
Code:
import java.util.*;
public class coinSumMinimalistic {
public static final int TARGET = 12003;
public static int[] validCoins = {1, 3, 5, 6, 7, 10, 12};
public static void main(String[] args) {
Arrays.sort(validCoins);
sack();
}
public static void sack() {
Map<Integer, Integer> coins = new TreeMap<Integer, Integer>();
coins.put(0, 0);
int a = 0;
for(int i = 1; i <= TARGET; i++) {
if(a < validCoins.length && i == validCoins[a]) {
coins.put(i, 1);
a++;
} else coins.put(i, -1);
}
for(int x = 2; x <= TARGET; x++) {
if(x % 5000 == 0) System.out.println("AT: " + x);
ArrayList<Integer> list = new ArrayList<Integer>();
for(int i = 0; i <= x / 2; i++) {
int j = x - i;
list.add(i);
list.add(j);
}
coins.put(x, min(list, coins));
}
System.out.println("It takes " + coins.get(TARGET) + " coins to reach the target of " + TARGET);
}
public static int min(ArrayList<Integer> combos, Map<Integer, Integer> coins) {
int min = Integer.MAX_VALUE;
int total = 0;
for(int i = 0; i < combos.size() - 1; i += 2) {
int x = coins.get(combos.get(i));
int y = coins.get(combos.get(i + 1));
if(x < 0 || y < 0) continue;
else {
total = x + y;
if(total > 0 && total < min) {
min = total;
}
}
}
int t = (min == Integer.MAX_VALUE || min < 0) ? -1:min;
return t;
}
}
EDIT: Upon research I think that the complexity is O(k*n^2) where n is the target, and k is the number of coins supplied, is this correct?
I thinky the code you provided is kind of chaotic. So this post is more about the conceptual algorithm instead of the real algorithm. This can differ a bit since for instance insertion in an ArrayList<T> is not O(1), but I'm confident that you can use good datastructures (for instance LinkedList<T>s) for this to let all operations run in constant time.
What your algorithm basically does is the following:
It starts with a map that maps all the given coins to one: it requires one coin to achieve the value on the coin.
For each iteration, it mixes all already achieved values with all already achieved values. The result is thus the sum of the coins and it takes at the sum of the number of coins unless it was already present in the collection.
This step you forgot: kick out values strictly larger than the requested value: since all coins are strictly positive, you will never be able to construct a value with such composition less than the requested value.
You keep doing this until you have constructed the requested coin value.
If at iteration i all new values added to the set are strictly larger than the requested value, you can stop: the requested value can't be constructed.
The parameters are:
n: the number of coins.
r: the requested value.
A first observation is that each step of (2.) requires O(s^2) time with s the number of elements in the set at the start of the iteration: this is because you match every value with every value.
A second observation is that you can never have more elements in the set than the requested value. This means that s is bounded by O(r) (we assume all coins are integers, thus the set can contain at most all integer values from 0 to r-1). Step (2.) has thus a maximum time complexity of O(r^2).
And furthermore the set evolves progressively: at each iteration, you will always construct a new value that is at least one larger than the maximum thus far. As a consequence, the algorithm will perform maximum O(r) iterations.
This implies that the algorithm has a time-complexity of O(r^3): r times O(r^2).
Why is the behavior exponential and thus at least NP-hard?
A first argument is that it comes down on how you represent input: in many cases, numbers are represented using a system with a radix greater than or equal to 2. This means that with k characters, you can represent a value that scales with O(g^k) with g the radix. Thus exponential. In other words, if you use a 32-bit number, worst case, r=O(2^32). So if you take this as input, there is an exponential part. If you would encode the target using unary notation, the algorithm is in P. But of course that's a bit like the padding-argument: given you provide enough useless input data (exponential or even super-exponential), all algorithms are in P, but you don't buy much with this.
A second argument is that if you leave the the requested value out of the input, you can only state that you start with n coins. You know that the number of iterations is fixed: you see the target value as an unknown constant. Each iteration, the total number of values in the Map<Integer,Integer> potentially squares. This thus means that the computational effort is:
n+n^2+n^4+n^6+...n^(log r)
^ ^ ^
| \-- first iteration \-- end of algorithm
\-- insertion
It is clear that this behavior is exponential in n.

Recursive method to count the number of combinations

this is a java code that recursively counts the number of payments in specific coins (ex. 1, 2, 5, 20, 50 etc.). I have tried to figure out how it works but can't seem to get it. Could somebody please be so kind and explain the math and logic behind the code and how this recursion works? I would really appreciate it.
// Returns the count of ways we can sum S[0...m-1] coins to get sum n
int count( int S[], int m, int n ){
// If n is 0 then there is 1 solution (do not include any coin)
if (n == 0)
return 1;
// If n is less than 0 then no solution exists
if (n < 0)
return 0;
// If there are no coins and n is greater than 0, then no solution exist
if (m <=0 && n >= 1)
return 0;
// count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1]
return count( S, m - 1, n ) + count( S, m, n-S[m-1] );
}
The method works like this:
First statements are the stopping conditions for the current recursion (without these for all cases then you end up with an infinite loop which ultimately end in a StackOverFlow)
The final line is where the calculation occurs. Each of the statements is reducing the problem into smaller chunks by:
count( S, m - 1, n ) reduces the number of coins (m-1) which excludes the last coin in the next recursive call to
count( S, m, n-S[m-1]) uses the last coin in the array and reduces the sum that is need to reach by the value of that coin
Consider this small example:
S[] = {1,2) // We have a 1 and 2 cent coin
m = S.length // Consider all possibilities ( = 2)
n = 3 // How many ways can we make 3c
// Obviously with: 1x1c + 1x2c
// and: 3x1c
The recursion as a tree; left branch = count( S, m - 1, n ), right branch = count( S, m, n-S[m-1]):
m=2;n=3
/ \
m=1;n=3 m=2;n=1
/ \ / \
m=0;n=3 m=1;n=2 m=1;n=1 m=2;n=-1
/ \ / \
m=0;n=2 m=1;n=1 m=0;n=1 m=1;n=0
/ \
m=0;n=1 m=1;n=0
This recursion can be thought of as a Pre-order Traversal of this tree.
If you then consider the conditions of the method for where a solution is found or not. So at the leaf nodes where n = 0.
Each which comes about like this:
First solution
m=1;n=3 - Exclude the last coin (2c)
m=1;n=2 - Use this coin (1c) and reduce by 1
m=1;n=1 - Use this coin (1c) and reduce by 1
m=1;n=0 - Use this coin (1c) and reduce by 1
n = 0 - a solution (3x1c)
Second Solution
m=2;n=1 - Use this coin(2c) and reduce by 2
m=1;n=1 - Exclude the last coin (2c)
m=1;n=0 - Use this coin (1c) and reduce by 1
n = 0 - a solution (1x2c + 1x2c)
At each node a value is returned - 0 (no solution) or 1 (a solution) - to add to the total count of solutions found. Once the recursion ends this final value is returned and is the number of solutions.
Some additional notes:
This piece of code will only consider the first m coins in the array S so to consider all the possible ways the initial call to the method needs to have m == S.length
Assumes that each coin can be used multiple times
Code modification with print statements to see the recursion:
public static void main(String[] args){
int[] coins = new int[]{1,2};
System.out.println("Final Count = " + count(coins, coins.length, 3, ""));
}
public static int calls = 0;
public static int count( int S[], int m, int n , String from){
calls++;
System.out.print("Call#" + calls + ": " + from + "; m = " + m + "; n = " + n);
// If n is 0 then there is 1 solution (do not include any coin)
if (n == 0)
{
System.out.println(" - Solution Found");
return 1;
}
// If n is less than 0 then no solution exists
if (n < 0)
{
System.out.println(" - No Solution Found n < 0");
return 0;
}
// If there are no coins and n is greater than 0, then no solution exist
if (m <=0 && n >= 1)
{
System.out.println(" - No Solution Found (other Case)");
return 0;
}
System.out.println();
// count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1]
return count( S, m - 1, n , from + "E" ) + count( S, m, n-S[m-1], from + "I" );
}
From the code, I'm assuming S is an array with at least m elements, with each element representing an available coin denomination, and n is the intended sum.
The comments really say it all, except that the last comment is backwards. count( S, m - 1, n ) is the number of solutions excluding the last coin in the current range. count( S, m, n-S[m-1] ) is the number of solutions using that coin.
The exclude case simply drops the last coin in the current range by reducing m by one.
The include case uses it by reducing n by that coin's value. Since the include case does not also reduce m, presumably any coin denomination can be used multiple times. It does not matter if the coin is too big - that is taken care of by returning 0 if n < 0.
If anything about the base cases is not clear from the comments, please ask a specific question.

Fibonacci recursion ex

Again, I'm still working with recursion, I've got a question regarding one of the base cases.
UPD: a and b represent the 1st numbers in the sequence and n is the desired position for the to-be calculated sum.
My code is as follows:
public static int fib(int a, int b, int n) {
if (n <=1) {
return a;
} else if (n == 2) {
return b;
} else {
return (fib(a, b, n - 1) + fib(a, b, n - 2));
}
}
In Line 2, before i started tracing out the program by hand, i kept it as "n<=0" . However, I got a diff answer as i traced and run the program. The problem was at some point n will = to 1. So I changed the first base case to n<=1 and got same answers.
Now the question is, suppose I called the method as follows: fib(2,3,6)
the answer should be = 21 ( with line 2 = " n<=1")
but when line 2 was "n<=0" the answer was 27.
I would like to know what happens to the program when n eventually = 1 given "n<=0" in Line 2
The call when n is 1 will generate two extra recursive calls with n as 0 and n as -1. These two recursive calls will add a twice to the correct answer.
To get the nth Fibonacci, simply pass n to the function.
Assuming the sequence is 0, 1, 1, 2, 3 ...
Your algorithm will be
if n = 1 return 0
else if n = 2 return 1
else return fib(n - 2) + fib(n - 1)
I actually answered my own question.
You see at some point n = 3,
So the value to be returned will eventually lead to n =1 as follows:
return (Fib(2,3,2)+ Fib(2,3,1))
Now that n = 1 the base case in Line 2 will be executed properly.
whereas if the base case in Line 2 was " n<=0" then for the case of n =3
return (Fib(2,3,2) + Fib(2,3,1))
then Fib(2,3,1) will cause the method to be called again and will result to n =0 and that will lead to having n = -1 & n =-2 causing the answers to differ.
//UpDATED: having n =0 & n=-1 will cause the answers to differ
You have two base cases where both will hit eventually and instead of returning n which would be correct it returns one of the passed and unchanged variables a or b.
Your code is a messy combination of the two different ways of computing the fibonacci series.
You have the recursive one that is very inefficient:
public static int fib(int n) {
if (n <=1)
return n;
return fib(n-1) + fib(n-2);
}
But, as you see it will compute everything severeal times. If you see the sequence you can iterate from the beginning by having two numbers, a and b, as arguments:
a 0 1 1 2 3 5 ...
b 1 1 2 3 5 8 ...
To compute the next a, you use b. To compute the new a you add a and b. Thus a more efficient algorithm is:
public static int fib(int a, int b, int n) {
if (n <= 0)
return a;
return fib(b, a+b, n-1);
}
Java does not tail call optimize so recursion won't work as well as in other languages that does optimize tail calls, thus an non recursive version of this would be better, like:
public static int fib(int n) {
for(int a=0, b=1;; n--) {
if (n <= 0)
return a;
int tmpa = a;
a = b;
b += tmpa;
}
}

Euler Project Help (Problem 12) - Prime Factors and the like

I hate to have to ask, but I'm pretty stuck here.
I need to test a sequence of numbers to find the first which has over 500 factors:
http://projecteuler.net/index.php?section=problems&id=12
-At first I attempted to brute force the answer (finding a number with 480 after a LONG time)
-I am now looking at determining the prime factors of a number and then use them to find all other factors.
I am currently at the stage where I can get an array of prime factors for any number I input - i.e 300 has the prime factors 2 2 3 5 5
Using this array of prime factors I need to be able to calculate the remaining factors - This is the part I am stuck on. Basically, as I understand it, I need to calculate ALL possible combinations of the numbers in the array...
i.e
2 * 2
2 * 2 * 3
2 * 2 * 3 * 5
2 * 3
2 * 3 * 3
...and so forth - But where it gets interesting is with things like...
2 * 5
2 * 3 * 5
...i.e Numbers which are not adjacent to each other in the array
I can't think of a way to code this in a generic fashion for any length array...
I need help! P.S - I am working in Java
EDIT: My brute force code - As it has been suggested brute forcing the problem will work and so there may be an error in my code :(
package euler.problem12;
public class Solution {
public static void main(String[] args) {
int next = 1;
int triangle = 0;
int maxFactors = 0;
while(true) {
triangle = triangle + next;
int factors = 1;
int max = (int) triangle / 2;
for(int i = 1; i <= max; ++i) {
if(triangle % i == 0) {
factors ++;
}
}
if(factors > maxFactors) {
maxFactors = factors;
System.out.println(triangle + "\t" + factors);
}
next++;
}
}
}
OK, second attempt as I was making things far too difficult.
Answer is given here: Link
If you factor a number into its prime
power factors, then the total number
of factors is found by adding one to
all the exponents and multiplying
those results together. Example: 108 =
2^2 * 3^3, so the total number of
factors is (2+1) * (3+1) = 3 * 4 = 12.
Sure enough, the factors of 108 are 1,
2, 3, 4, 6, 9, 12, 18, 27, 36, 54, and
108. This happens because to be a factor, a number must have the same
primes, and raised to the same or lower powers.
So if you know the prime factors, you just need to count the repeated ones and use the above calculation to work out the number of factors.
As far as I can tell, question 12 doesn't mention anything about prime numbers? Is this the one you're looking at?
The sequence of triangle numbers is generated by adding the natural numbers...
If so, then perhaps not thinking about primes will help? ;)
Possibly 3 months too late, but here goes...
I see that answer two has privided the function to give you the answer you require, but in answer to your original question on how you generate all the factors assuming you need to for some reason, then here's how you do it:
Assuming that you have the factors in an array:
int[] primeFactors = new int[] {2, 2, 3, 5, 5};
What you need to do is recurse every in-order permutation for each possible depth, and then reduce the resulting result set to just the unique values.
I'll explain what I mean:
"In-order permutation": assuming you start at position 0 of the array, the next element must be 1, 2, 3 or 4, if you start from 1 then the next one must be 2, 3 or 4 and so on.
"Each possible depth": each single factor, then any two factors, then any three factors and so on until you get to all five factors.
"Reduce the set": If you take two elements, say 0&3, 0&4, 1&3 or 1&4 they all give you 2 * 5 = 10, they all provide the factor 10, so you need to winnow your set to just distinct values. (Phew, this is getting longer than I expected... :))
The way to do this is to use two methods, one to select the maximum depth of recursion, kick off the recustion and the winnow the final results, and the other to recurse the values:
public static void main(String[] args) {
int[] primeFactors = new int[] {2, 2, 3, 5, 5};
List<Integer> allFactors = getAllFactors(primeFactors);
for (int factor : allFactors) {
System.out.println("Factor: " + factor);
}
}
private static List<Integer> getAllFactors(int[] primeFactors) {
Set<Integer> distinctFactors = new HashSet<Integer>();
for (int maxDepth = 0; maxDepth <= primeFactors.length; maxDepth++) {
permutatPrimeFactors(0, maxDepth, 0, 1, primeFactors, distinctFactors);
}
List<Integer> result = new ArrayList<Integer>(distinctFactors);
Collections.sort(result);
return result;
}
private static void permutatPrimeFactors(int depth, int maxDepth, int minIndex, int valueSoFar, int[] primeFactors, Set<Integer> distinctFactors) {
if (depth == maxDepth) {
distinctFactors.add(valueSoFar);
return;
}
for (int index = minIndex; index < primeFactors.length; index++) {
permutatPrimeFactors(depth + 1, maxDepth, index + 1, valueSoFar * primeFactors[index], primeFactors, distinctFactors);
}
}
The getAllFactors uses a Set to make sure we only get distinct values, than adds them to a list and sorts that so that we can display the factors in order.
While permutatPrimeFactors, generates from zero terms (factor = 1) though to all terms (factor = 1 * 2 * 2 *3 * 5 * 5 = 300).
Hope that helps.

Categories