7

Using guava 12 Collections2.permutations(), I'm wondering if it's possible to limit the size of the permutations ?

More precisely, I would like to get a list of k-sized permutations within a list of n elements, instead of getting a list of all n-sized permutations.

Currently, if I pass a list of 4 fruits, permutations() will currently return a list of 24 4-sized permutations although I'm only interested in retrieving, say, the 4 unique size 3 permutations.

Say I have a list of 4 fruits :

["Banana", "Apple", "Orange", "Peach"]

If I'm only interested in size 3 permutations, I'd like the following returned :

["Banana", "Apple", "Orange"]
["Banana", "Apple", "Peach"]
["Banana", "Orange", "Peach"]
["Apple", "Orange", "Peach"]

Could anyone please provide any hints to a solution ? Thanks !

jbmusso
  • 3,436
  • 1
  • 25
  • 36

3 Answers3

9

This code works out the variations, then runs the permutations on each unique set of 3.

i.e. for "A", "B", "C", "D" the possibilities are [[A, B, C], [A, B, D], [A, C, D], [B, C, D]]. We then calculate the permutations on each threesome (or n-some) and append the possibilities to a list.

PermutationsOfN.processSubsets( List set, int k ) returns: [[A, B, C], [A, B, D], [A, C, D], [B, C, D]]

Taking it a bit further PermutationsOfN.permutations( List list, int size ) returns:
[[A, B, C], [A, C, B], [C, A, B], [C, B, A], [B, C, A], [B, A, C], [A, B, D], [A, D, B], [D, A, B], [D, B, A], [B, D, A], [B, A, D], [A, C, D], [A, D, C], [D, A, C], [D, C, A], [C, D, A], [C, A, D], [B, C, D], [B, D, C], [D, B, C], [D, C, B], [C, D, B], [C, B, D]]

import java.util.Collection;
import java.util.List;

import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

public class PermutationsOfN<T> {
  public static void main( String[] args ) {
    List<String> f = Lists.newArrayList( "A", "B", "C", "D" );
    PermutationsOfN<String> g = new PermutationsOfN<String>();
    System.out.println( String.format( "n=1 subsets %s", g.processSubsets( f, 1 ) ) );
    System.out.println( String.format( "n=1 permutations %s", g.permutations( f, 1 ) ) );
    System.out.println( String.format( "n=2 subsets %s", g.processSubsets( f, 2 ) ) );
    System.out.println( String.format( "n=2 permutations %s", g.permutations( f, 2 ) ) );
    System.out.println( String.format( "n=3 subsets %s", g.processSubsets( f, 3 ) ) );
    System.out.println( String.format( "n=3 permutations %s", g.permutations( f, 3 ) ) );
    System.out.println( String.format( "n=4 subsets %s", g.processSubsets( f, 4 ) ) );
    System.out.println( String.format( "n=4 permutations %s", g.permutations( f, 4 ) ) );
    System.out.println( String.format( "n=5 subsets %s", g.processSubsets( f, 5 ) ) );
    System.out.println( String.format( "n=5 permutations %s", g.permutations( f, 5 ) ) );
  }

  public List<List<T>> processSubsets( List<T> set, int k ) {
    if ( k > set.size() ) {
      k = set.size();
    }
    List<List<T>> result = Lists.newArrayList();
    List<T> subset = Lists.newArrayListWithCapacity( k );
    for ( int i = 0; i < k; i++ ) {
      subset.add( null );
    }
    return processLargerSubsets( result, set, subset, 0, 0 );
  }

  private List<List<T>> processLargerSubsets( List<List<T>> result, List<T> set, List<T> subset, int subsetSize, int nextIndex ) {
    if ( subsetSize == subset.size() ) {
      result.add( ImmutableList.copyOf( subset ) );
    } else {
      for ( int j = nextIndex; j < set.size(); j++ ) {
        subset.set( subsetSize, set.get( j ) );
        processLargerSubsets( result, set, subset, subsetSize + 1, j + 1 );
      }
    }
    return result;
  }

  public Collection<List<T>> permutations( List<T> list, int size ) {
    Collection<List<T>> all = Lists.newArrayList();
    if ( list.size() < size ) {
      size = list.size();
    }
    if ( list.size() == size ) {
      all.addAll( Collections2.permutations( list ) );
    } else {
      for ( List<T> p : processSubsets( list, size ) ) {
        all.addAll( Collections2.permutations( p ) );
      }
    }
    return all;
  }
}

A special mention goes to meriton whose answer here helped me work it out.

Community
  • 1
  • 1
mrswadge
  • 1,659
  • 1
  • 20
  • 43
  • Hi mrswadge, thanks for the detailed input ! Though your Java skills are way greater than mine and I'm unable to modify your code so I only get the following list : [[A, B, C], [A, B, D], [A, C, D], [B, C, D]] ;). – jbmusso Jun 20 '12 at 19:16
  • Ah, sorry - I see what you mean! I just re-read your question. Change the method processSubsets( list, size ) to public. Call that instead of the method permutations( List list, int size ). Job done :) – mrswadge Jun 21 '12 at 07:46
  • Wonderful, I think I fully understand your solution now. Thank you! – jbmusso Jun 21 '12 at 16:17
7

There's no built-in Guava feature that does this, though you could submit a feature request.

If I were writing an implementation, I think the simplest way would be to iterate through combinations (with Gosper's hack), and then permutations of those with Collections2.permutations.

Alternately, it looks like some minor modifications to the normal permutation generation algorithm suffice, based on this Python code.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • Thank you for the input, I'll indeed submit a feature request for I believe such feature would be a valuable addition to Guava. – jbmusso Jun 21 '12 at 16:19
1

A direct answer to your question would be this:

public static <X> List<List<X>> test(List<X> input, final int n, final int m) {
    int x = 0;

    List<List<X>> newPerm = Lists.newArrayList();
    Collection<List<X>> perm = Collections2.permutations(input);
    for (Iterator<List<X>> it = perm.iterator(); it.hasNext(); x++) {
        if (x >= n) {
            break;
        }

        List<X> list = it.next();
        newPerm.add(Lists.partition(list, m).get(0));
    }

    return newPerm;
}

and then:

    List<String> list = Lists.newArrayList("Banana", "Apple", "Orange", "Peach");
    List<List<String>> answer = test(list, 4, 3);
    for (List<String> list1 : answer) {
        System.out.println(list1);
    }

But it takes only the first ones and not a specifically defined set, your probably better off following Louis's advice

epoch
  • 16,396
  • 4
  • 43
  • 71
  • 2
    So you are suggesting to generate all permutations and then throw away the unneeded ones? The complexity of that is appalling! Imagine a set with 1000 elements and a limit of 2. Run this Java code to see what you get: `System.out.println(Collections2.permutations(Ranges.closed(1, 1000).asSet(DiscreteDomains.integers())).size())` – Sean Patrick Floyd Jun 20 '12 at 14:42
  • @SeanPatrickFloyd, yeah I understand that, but as I said it is a direct solution to his problem, not necessarily a good/concrete one – epoch Jun 21 '12 at 05:48
  • a solution like this would probably make a good unit test for a more efficient version. – Ryan Leach Oct 04 '18 at 03:09