12

What is the most efficient algorithm to print all unique combinations of factors of a positive integer. For example if the given number is 24 then the output should be

24*1
12*2
8*3
6*4
6*2*2
4*3*2
3*2*2*2

Here notice that when 6*4 gets printed then 4*6 doesn't get printed. So basically it's a problem of taking unique subsets without considering the order (one way to look at the problem). But the objective is to have a function that runs the fastest, so storing the factors in a data structure to do further manipulation might consume more time. I have tried my algorithm and pasted my code below, but it doesn't seem to give me the desired result, I'm making some mistake in my recursive call. Can you help me figure out an efficient way to do this?

public static void printfact(int num){
        int temp=0;
        for(int i=num-1;i>=num/2;i--){
            if(num % i == 0){
                temp = num/i;
                System.out.println(temp + " * " + i);
                if(isprime(i)==false){
                    System.out.print(temp + " * ");
                    printfact(i);
                }
            }
        }
}
crazyim5
  • 706
  • 2
  • 9
  • 26
  • Thanks for the question edit suggestion Sanjeev – crazyim5 Feb 27 '13 at 21:21
  • i think you need: http://en.wikipedia.org/wiki/Combination#Number_of_combinations_with_repetition after you find all of the factors. – Ray Tayek Feb 27 '13 at 21:51
  • Neither 6 nor 4 are prime factors of 24 (or of any other number), since they are not, well... prime. – Philip Feb 28 '13 at 11:05
  • @Aubin this is the right title, it's not about printing prime factors but all combination of factors. Look at the example I have given. – crazyim5 Feb 28 '13 at 19:31

9 Answers9

4

Try this recursive approach that also takes in 2 more inputs namely a string to carry over the current value of i in for loop to perform subsequent reduction and also a temp int to know when not to print duplicate reversals i.e., 8*3 and 3*8.

public static void printFactors(int number, String parentFactors, int parentVal) {
    int newVal = parentVal;
    for (int i = number - 1; i >= 2; i--) {

        if (number % i == 0) {
            if (newVal > i) {
                newVal = i;
            }
            if (number / i <= parentVal && i <= parentVal
                    && number / i <= i) {
                System.out.println(parentFactors + i + "*" + number / i);
                newVal = number / i;
            }

            if (i <= parentVal) {
                printFactors(number / i, parentFactors + i + "*", newVal);
            }
        }

    }

}

And call this method using printFactors(12,'',12)
Let me know if you find flaws in this approach. Thanks!

crazyim5
  • 706
  • 2
  • 9
  • 26
  • This answer would neglect the 1 * n answer which the OP is after. Other than that, I think it does catch everything. – demongolem Nov 09 '15 at 23:53
2

1) If i < num and i > num/2, then num % i == num - i. (Easy to prove.) So your for loop will pointlessly check all the integers greater than num/2 and the if statement will only succeed once, with temp == 2. I don't think that's what you wanted.

2) In you fixed that, the recursion might need to produce a lot of answers. But you only print temp * once. So the output will look a bit wierd.

3) isprime is unnecessary. num is always a legitimate factor, whether or not it is prime, provided you follow the point below.

4) Finally, you need to figure out how to avoid printing out the same factorization multiple times. The easy solution is to only produce factorizations where the factors are monotonically non-increasing (as in your example). In order to do that, the recursion needs to produce factorizations with some maximum factor (which would be the previously discovered factor.) So the recursive function should have (at least) two arguments: the number to factor and the maximum allowed factor. (You also need to deal with the problem I noted as point 4.)

The following Python code does (I believe) solve the problem, but it still does quite a few unnecessary divides. In a deviation from python style, it prints each factorization instead of acting as a generator, because that will be easier to translate into Java.

# Uses the last value in accum as the maximum factor; a cleaner solution
# would have been to pass max_factor as an argument.
def factors(number, accum=[]):
  if number == 1:
    print '*'.join(map(str, accum))
  else:
    if accum:
      max_factor = min(accum[-1], number)
    else:
      max_factor = number
    for trial_factor in range(max_factor, 1, -1):
      remnant = number / trial_factor
      if remnant * trial_factor == number:
        factors(remnant, accum + [trial_factor,])

It is possible to optimize the for statement. For example, once you compute remnant, you know that the next remnant must be at least one greater, so you can skip a bunch of trial_factor values when remnant is small.

rici
  • 234,347
  • 28
  • 237
  • 341
  • Exactly, I'm not sure if I can pass in the function with a secondary String argument that carries over the previous factors to be printed before recursively reducing the larger element (for example, if number is 40 then 10*4 and then reduce both 10 and 4 into 5*2*2*2 etc..). But how will we call the function for the first time. I'm having trouble visualizing it though. – crazyim5 Feb 27 '13 at 21:37
  • No, you don't need to reduce both 10 and 4. The first factor `5` should show up later. So you should produce `10*4` and `10*2*2` and then `8*5` and then `5*4*2` and `5*2*2*2`. (That's after the other factorizations, `40` and `20*2`, of course.) So the question is really how to efficiently decide which are the plausible first factors in the recursion, without doing a lot of unnecessary modulus operations. – rici Feb 27 '13 at 21:51
  • Yes you are right but how to recursively device that. Can you provide a sample code? – crazyim5 Feb 27 '13 at 21:57
  • @crazyim5: added some python code, which you can consider pseudo-code. – rici Feb 27 '13 at 22:46
  • Thanks Rici, I modified a bit more and answered by my own. Let me know what you think. – crazyim5 Feb 28 '13 at 01:26
2

This code find all the factors of a number, sort them (locally and globally):

public class PrimeFactors {

   final SortedSet< List< Integer >> _solutions = new TreeSet<>(
      new Comparator<List<Integer>>(){
         @Override
         public int compare( List<Integer> left, List<Integer> right ) {
            int count = Math.min( left.size(), right.size());
            for( int i = 0; i < count; ++i ) {
               if( left.get(i) < right.get(i)) {
                  return -1;
               }
               if( left.get(i) > right.get(i)) {
                  return +1;
               }
             }
            return left.size() - right.size();
         }});

   public SortedSet< List< Integer >> getPrimes( int num ) {
      _solutions.clear();
      getPrimes( num, new LinkedList< Integer >());
      return _solutions;
   }

   private void getPrimes( int num, List< Integer > solution ) {
      for( int i = num - 1; i > 1; --i ) {
         if(( num % i ) == 0 ) {
            int temp = num / i;
            List< Integer > s = new LinkedList<>();
            s.addAll( solution );
            s.add( temp );
            s.add( i );
            Collections.sort( s );
            if( _solutions.add( s )) { // if not already found
               s = new LinkedList<>();
               s.addAll( solution );
               s.add( temp );
               getPrimes( i, s );
             }
         }
      }
   }
   public static void main( String[] args ) {
      SortedSet< List< Integer >> solutions =
         new PrimeFactors().getPrimes( 24 );
      System.out.println( "Primes of 24 are:" );
      for( List< Integer > l : solutions ) {
         System.out.println( l );
      }
   }
}

Outputs:

Primes of 24 are:
[2, 2, 2, 3]
[2, 2, 6]
[2, 3, 4]
[2, 12]
[3, 8]
[4, 6]
Aubin
  • 14,617
  • 9
  • 61
  • 84
  • Thanks for the solution. Yes, this is pretty much what I did in the first time but we need to eliminate duplicate combinations. – crazyim5 Feb 27 '13 at 21:37
  • It's a lot more efficient to not generate duplicates than to eliminate them after the fact. As I said, the key is to always generate monotonically non-increasing sequences; it's not hard. – rici Feb 27 '13 at 21:52
  • I had improved the algorithm, eliminating the already walked path when traversing the decomposition. – Aubin Feb 28 '13 at 06:56
2

I have a solution without recursion or sorting or stacks in C/C++.

#include <vector>
#include <iostream>

// For each n, for each i = n-1 to 2, try prod = prod*i, if prod < N.

int
g(int N, int n, int k)
{
        int i = k;
        int prod = n;
        std::vector<int> myvec;

        myvec.push_back(n);
        while (i > 1) {
                if (prod * i == N) {
                        prod = prod*i;
                        myvec.push_back(i);
                        for (auto it = myvec.begin();
                                it != myvec.end(); it++) {
                                std::cout << *it << ",";
                        }
                        std::cout << std::endl;
                        return i;
                } else if (prod * i < N) {
                        prod = prod*i;
                        myvec.push_back(i);
                } else { i--;}
        }

        return k;
}

void
f(int N)
{
        for (int i = 2; i <= N/2; i++) {
                int x = g(N, i, i-1);
                // Extract all possible factors from this point
                while (x > 0) {
                        x = g(N, i, x-1);
                }
        }
}

int
main()
{
        f(24);

        return 0;
}

And output is like this:

$ ./a.out
    3,2,2,2,
    4,3,2,
    6,4,
    6,2,2,
    8,3,
    12,2,
ᴀʀᴍᴀɴ
  • 4,443
  • 8
  • 37
  • 57
smh
  • 81
  • 1
  • 1
1

Here is my solution based on @rici's ideas.

def factors(number, max_factor=sys.maxint):
    result = []

    factor = min(number / 2, max_factor)
    while factor >= 2:
        if number % factor == 0:
            divisor = number / factor

            if divisor <= factor and divisor <= max_factor:
                result.append([factor, divisor])

            result.extend([factor] + item for item in factors(divisor, factor))

        factor -= 1

    return result

print factors(12) # -> [[6, 2], [4, 3], [3, 2, 2]]
print factors(24) # -> [[12, 2], [8, 3], [6, 4], [6, 2, 2], [4, 3, 2], [3, 2, 2, 2]]
print factors(157) # -> []
Valentin Shergin
  • 7,166
  • 2
  • 50
  • 53
0
vector<unsigned int> GetAllFactors(unsigned int number)
{
    vector<unsigned int> factors;

    for (int i = 2; i <= number; i++)
    {
        if (number % i == 0)
        {
            factors.push_back(i);
        }
    }

    return factors;
}

void DoCombinationWithRepetitionFactors(vector<unsigned int> allFactors, unsigned currentProduct, unsigned targetProduct, vector<unsigned int> currentFactorSet, unsigned currentFactorIndex)
{
    if (currentProduct == targetProduct)
    {
        for (auto a : currentFactorSet)
        {
            cout << a << " , ";
        }

        cout << endl;

        return;
    }


    for (int i = currentFactorIndex; i < allFactors.size(); i++)
    {
        if (currentProduct * allFactors[i] <= targetProduct)
        {
            currentFactorSet.push_back(allFactors[i]);
            DoCombinationWithRepetitionFactors(allFactors, currentProduct * allFactors[i], targetProduct, currentFactorSet, i);
            currentFactorSet.pop_back();
        }
    }
}
0
bool isprime(int n){
for(int i=2; i<=sqrt(n); i++)
    if(n%i==0)
        return false;
return true;
}

void printprime(int n){

int i,j,y=n;

while(y%2==0){
    cout<<"2 * ";
    y/=2;
}

for(i=3; i<=sqrt(y); i+=2){
    while(y%i==0){
        cout<<i<<" * ";
        y/=i;
    }
}

if(y>2)
    cout<<y;
}

void allfacs(int n){

int i;
unordered_set<int> done;

for(i=2; i<sqrt(n); i++){
    if(n%i==0){
        cout<<i<<" * "<<n/i<<endl;

        if(!isprime(i) && done.find(i) == done.end()){
            done.insert(i);
            printprime(i);
            cout<<n/i<<endl;
        }
        if(!isprime(n/i) && done.find(n/i) == done.end()){
            done.insert(n/i);
            cout<<i<< " * ";
            printprime(n/i);
            cout<<endl;
        }
    }
}
}
0

I came up with this, seems easy to read and understand. Hope it helps!

def getFactors(num):

    results = []

    if num == 1 or 0:
        return [num]

    for i in range(num/2, 1, -1):

        if (num % i == 0):
            divisor = num / i

            if(divisor <= i):
                results.append([i, divisor])

            innerResults = getFactors(divisor)

            for innerResult in innerResults:
                if innerResult[0] <= i:
                    results.append([i] + innerResult)

    return results
Gitmo
  • 2,366
  • 5
  • 25
  • 31
0
#include<bits/stdc++.h>
using namespace std;
int n;
// prod = current product of factors in combination vector
// curr = current factor
void fun(int curr, int prod, vector<int> combination )
{
    if(prod==n)
    {
        for(int j=0; j<combination.size(); j++)
        {
            cout<<combination[j]<<" ";
        }
        cout<<endl; 
        return;
    }

    for(int i=curr; i<n; i++)
    {
        if(i*prod>n) break;
        if(n%i==0)
        {
            combination.push_back(i);
            fun(i, i*prod, combination);
            combination.resize(combination.size()-1);
        }
    }
}

int main()
{
    cin>>n;
    vector<int> combination;
    fun(2, 1, combination);
    cout<<1<<" "<<n;
    return 0;
}
arima_232
  • 1
  • 1