[LintCode] Factorization
A non-negative numbers can be regarded as product of its factors.
Write a function that takes an integer n and return all possible combinations of its factors.
Notice
- Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
- The solution set must not contain duplicate combination.
Given n = 8
return [[2,2,2],[2,4]]
// 8 = 2 x 2 x 2 = 2 x 4.
Given n = 1
return []
Given n = 12
return [[2,6],[2,2,3],[3,4]]
1 public class Solution { 2 public List<List<Integer>> getFactors(int n) { 3 List<List<Integer>> results = new ArrayList<List<Integer>>(); 4 getFactorsDfs(results, new ArrayList<Integer>(), n, 2); 5 return results; 6 } 7 private void getFactorsDfs(List<List<Integer>> results, List<Integer> list, int n, int startFactor) { 8 if(n == 1) { 9 if(list.size() > 1) { 10 results.add(new ArrayList<Integer>(list)); 11 } 12 return; 13 } 14 for(int i = startFactor; i <= n; i++) { 15 if(n % i == 0) { 16 list.add(i); 17 getFactorsDfs(results, list, n / i, i); 18 list.remove(list.size() - 1); 19 } 20 } 21 } 22 }
Solution 2. Efficient than solution 1.
Solution 1 works correctly but does a lot of unnecessary work. If we uses the fact that any factorization that starts with a factor that is bigger than
Math.sqrt(n) is going to be a duplicated answer, we can get a more efficient solution.
Take n == 12 as an example, sqrt(12) is >= 3 but < 4. we have the following factorizations that start with a factor >= 4:
[4, 3]; [6, 2]; they are duplicates of [3, 4] and [2, 6] respectively.
This is true because for n = a * b, a <= sqrt(n), b>= sqrt(n), if we start to check n's factor from small to big, then we will check case of a * b prior to case of b * a.
As a result, we can cut the stop condition from i <= n to i <= Math.sqrt(n) without losing any valid factorization.
However, this optimization comes with a cost. When the stop condition is i <= Math.sqrt(n), we eliminated all [n, 1] factorizations, not only for the original input n,
but also for n's factors. For example, if n == 12, then [12, 1] will not be considered. When we recursively calls getFactorsDfs on 12's factor 6, we also eliminated the
factorization of [6,1] for 6 since i is in range [2, Math.sqrt(6)]; This causes the factorization of [2, 6] is missed. The reason is that we should only enfore the rule
of "actors should be greater than 1 and less than n itself" on the original input n, not on any of its factors.
To fix this, the highlighted code is added after [startFactor, sqrt(n)] are all checked. It checks if we can use the current n at the current recursive call level as a valid factor.
For the case of the original input n, the list only contains 1 element of n, so this list will not be added as a valid factorization.
For the case of n's factors f, the list already contains at least 2 elements(the divisor d that meets n % d == f and f itself), this list will be added as a valid factorization.
1 public class Solution { 2 public List<List<Integer>> getFactors(int n) { 3 List<List<Integer>> results = new ArrayList<List<Integer>>(); 4 getFactorsDfs(results, new ArrayList<Integer>(), n, 2); 5 return results; 6 } 7 private void getFactorsDfs(List<List<Integer>> results, List<Integer> list, int n, int startFactor) { 8 if(n == 1) { 9 if(list.size() > 1) { 10 results.add(new ArrayList<Integer>(list)); 11 } 12 return; 13 } 14 for(int i = startFactor; i <= Math.sqrt(n); i++) { 15 if(n % i == 0) { 16 list.add(i); 17 getFactorsDfs(results, list, n / i, i); 18 list.remove(list.size() - 1); 19 } 20 } 21 if(n >= startFactor) { 22 list.add(n); 23 getFactorsDfs(results, list, 1, n); 24 list.remove(list.size() - 1); 25 } 26 } 27 }