I have an array of random length full of random integers. For the sake of simplicity, they are ordered from the highest to the lowest.
I also have a target random integer.
I need to get all the combinations which sum is greater or equal to the target without using unnecesary numbers. Given this example:
nums = [10, 6, 5, 3, 2, 1, 1]
target = 8
I want this output:
10 (10 is higher or equal to 8, so there's no need to sum it)
6+5
6+3
6+2
6+1+1
5+3
5+2+1
5+2+1 (Note that this result is different from the previous since there are 2 1s)
I've tried a lot of recursive strategies but I can't find a correct answer. No specific language is required, pseudocode is welcome.
EDIT: Posting code
private static boolean filtrar(final CopyOnWriteArrayList<Padre> padres) {
if (sum(padres) < TARGET) {
return false;
}
int i;
for (i = padres.size(); i >= 0; i--) {
if (!filtrar(copiaPadresSinElemento(padres, i-1))) {
break;
}
}
// Solución óptima, no se puede quitar nada.
if (i == padres.size()) {
print(padres);
}
return true;
}
private static int sum(final CopyOnWriteArrayList<Padre> padres) {
int i = 0;
for (final Padre padre : padres) {
i += padre.plazas;
}
return i;
}
private static void print(final CopyOnWriteArrayList<Padre> padres) {
final StringBuilder sb = new StringBuilder();
for (final Padre padre : padres) {
sb.append(padre);
sb.append(", ");
}
final String str = sb.toString();
System.out.println(str);
}
private static CopyOnWriteArrayList<Padre> copiaPadresSinElemento(
final CopyOnWriteArrayList<Padre> padres, final int i) {
final CopyOnWriteArrayList<Padre> copia = new CopyOnWriteArrayList<Padre>();
for (int e = 0; e < padres.size(); e++) {
if (e != i) {
copia.add(padres.get(e));
}
}
return copia;
}
Padre is a simple object which contains a name for each number. Padre.plazas is the number.
The array right now is [6,4,4,4,3,3,3], and the target is 8.
I think something like this would work:
function filter(array, target): Boolean{ //assumes array is sorted in descending order
if(sum(array) < target) return false;
for(i = array.length-1; i>=0; --i){
if(! filter(array.createCopyRemovingElementAt(i), target)) break;
}
if(i==array.length-1) print(array); // solution is "optimal": could not remove a single number
return true;
}
An inefficient solution : Complexity = O((2^n)*n)
for i = 0 to 2^size-of-array
do
sum = 0
for j = 0 to n-1
do
if(jth bit in i is 1)
then
sum+=array[j]
fi
done
if(sum>=target)
print the number i (uniquely identifies the set of numbers)
done
Related
I'm trying to learn a bit Java with tutorials and currently I'm struggling with piece of code where I should find on which index is difference between arrays (if there is difference at all)
My code
Scanner scanner = new Scanner(System.in);
int[] arrOne = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int[] arrTwo = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int sumArrOne = 0;
int index = 0;
boolean diff = false;
for (int k : arrOne) {
if (Arrays.equals(arrOne, arrTwo)) {
sumArrOne += k;
} else {
for (int i : arrTwo) {
if (k != i) {
index = i;
diff = true;
break;
}
}
}
}
if (diff) {
System.out.println("Found difference at " + index + " index.");
} else {
System.out.println("Sum: " + sumArrOne);
}
So, if arrays are identical I'm sum array elements in arrOne. If they are not identical -> must show at which index they are not.
With this code when I input
1 2 3 4 5
1 2 4 3 5
I should get that difference is at index 2 instead I've got index 1.
I'm not quite sure why and would be glad if someone point me out where is my mistake.
I updated your code. Looks like you're misunderstanding the concept of indexes yet.
Use one common index to check with in both arrays, in my example it's simply called i:
import java.util.Arrays;
import java.util.Scanner;
public class BadArray {
static private final int INVALID_INDEX = Integer.MIN_VALUE;
public static void main(final String[] args) {
try (final Scanner scanner = new Scanner(System.in);) {
final int[] arrOne = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
final int[] arrTwo = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int sumArrOne = 0;
int diffIndex = INVALID_INDEX;
final int minLen = Math.min(arrOne.length, arrTwo.length);
for (int i = 0; i < minLen; i++) {
sumArrOne += arrOne[i];
if (arrOne[i] != arrTwo[i]) {
diffIndex = i;
break;
}
}
if (diffIndex != INVALID_INDEX) {
System.out.println("Found difference at " + diffIndex + " index.");
} else if (arrOne.length != arrTwo.length) {
System.out.println("Arrays are equal but have different length!");
} else {
System.out.println("Sum: " + sumArrOne);
}
}
}
}
I also put the scanner into a try-resource-catch to handle resource releasing properly.
Note you could also do the array lengths comparison right at the start if different array lengths play a more crucial role.
You are trying to find out which index has the first difference so you should iterate via the index rather than using a for-each loop (aka enhanced for loop). The following method should work for this.
/**
* Returns the index of the first element of the two arrays that are not the same.
* Returns -1 if both arrays have the same values in the same order.
* #param left an int[]
* #param right an int[]
* #return index of difference or -1 if none
*/
public int findIndexOfDifference(int[] left, int[] right) {
// short-circuit if we're comparing an array against itself
if (left == right) return -1;
for (int index = 0 ; index < left.length && index < right.length ; ++index) {
if (left[index] != right[index]) {
return index;
}
}
return -1;
}
In your code you compare, where the indexes are different, not the values at the indexes. Also your code has several other issues. I'll try to go through them step by step:
// compare the whole array only once, not inside a loop:
diff = !Arrays.equals(arrOne, arrTwo));
if (!diff) {
// do the summing without a loop
sumArrOne = Arrays.stream(arrOne).sum();
} else {
// find the difference
// it could be the length
index = Math.min(arrOne.length, arrTwo.length);
// or in some different values
for (int i = 0; i < index; i++) { // do a loop with counter
if (arrOne[i] != arrTwo[i]) {
index = i;
break;
}
}
}
It doesn't matter that I set index here above the loop as it's value will be overwritten anyways inside the loop, if relevant.
I am trying to come up with an algorithm in Java which when given a string of digits can identify a combination of integers which meets the following criteria
N = N1 + N2
N >= N1 >= N2
where:
N is the Nth element in the string or element at Nth position;
N1 is the (N-1) element in the string & N2 is the (N-2) element in the string.
Example 1: 224610
Elements in this string are 2, 2, 4, 6, 10.
First Set: 2+2=4 (N2=2; N1=2 & N= 4);
Second Set: 2+4=6 (N2=2; N1=4 & N=6);
Third Set: 4+6=10 (N2=4; N1=6 & N= 10)
Example 2: 11112233558
Elements in this string are 1, 11, 12, 23, 35, 58
Example 3: 1101102203
Elements in this string are 1, 101, 102, 203.
I have already written a function which can take an ArrayList of integers and tell you whether the array complies with the requirements.
public static boolean complies(ArrayList<Integer> al)
{
boolean result = true;
int alsize = al.size();
for (int n = alsize-1; n > 1; n--)
{
int N1 = al.get(n-1);
int N2 = al.get(n-2);
int N = al.get(n);
if (N != ( N1 + N2))
result = false;
if ((N < N1) || (N1 < N2))
result = false;
}
return(result);
}
The part I am struggling with his finding an elegant way to identify all possible integer combinations which I can run through the above function.
I thought about the question you asked, which was essentially how to find all the combinations of a set of digits in the order the digits were given, and realized that this could be solved recursively. All you have to do is add the first digit to all the combinations that can be made with the rest of the digits as well as put that digit in front of the first term of all the combinations. The base case is that there is only one digit, which means there is of course only one combination.
Let me give an example. With the digits 123 first we find all the combinations for 23 and since 23 has more than one digit we find all the combos for 3, which is just 3. Then add 2 to that combination which makes 2, 3 and put 2 in front of the first term of that combination, which makes 23. Now add 1 to all the combos which makes 1, 2, 3 and 1, 23 and put 1 in front of the first term which makes 12, 3 and 123.
So I made this method to find all the combinations. It returns a two dimensional arrayList with each individual arrayList being a unique combination. The preconditions are that you have to give it a string of numbers only and no empty strings. If I made a mistake or it doesn't work for your application say something. I am pretty sure you could just go through the two dimensional arrayList and check if each arrayList works in your boolean method. You can test it here.
public ArrayList<ArrayList<Integer>> findCombos(String input)
{
ArrayList<ArrayList<Integer>> answer = new ArrayList<ArrayList<Integer>>();
if(input.length()==1)
{
ArrayList<Integer> combo = new ArrayList<Integer>();
answer.add(combo);
combo.add(Integer.parseInt(input)); //this method converts from a string to an int
return answer;
}
else
{
answer = findCombos(input.substring(1));
int size = answer.size(); //you need to save this because when you add things to an arrayList the size changes
for(int i=0;i<size;i++) //this copies the arrayList back to itself
{
ArrayList<Integer> copy = new ArrayList<Integer>(answer.get(i));
answer.add(copy);
}
int digit = (char)(input.charAt(0)-'0');//this saves the current digit
for(int i=0;i<size;i++)//this adds the digit in front of all the previous combos
answer.get(i).add(0, digit);
for(int i=size;i<answer.size();i++)//this puts the digit in front of the first term of the previous combos
{
String copy = "" + answer.get(i).get(0);//I just did this to find the length of the first term easily
int append = (int)(digit*Math.pow(10, copy.length()));
answer.get(i).set(0, append+answer.get(i).get(0));
}
return answer;
}
}
The Code below works against all 3 given input, and is solid enough.
public class IntFinder
{
/**
* #param args the command line arguments
*/
private static String given = "444444889321420";
static N1N2 temp = new N1N2(given);
public static void main(String[] args) {
// TODO code application logic here
N1N2 n1 = new N1N2(given);
N1N2 n2 = new N1N2(given);
/*IntFinder.setGiven("224610");
n1 = new N(IntFinder.getGiven());
n2 = new N(IntFinder.getGiven());
n1.setValue(0, 0); // 2
n2.setValue(1, 1); // 2*/
/*IntFinder.setGiven("11112233558");
n1 = new N(IntFinder.getGiven());
n2 = new N(IntFinder.getGiven());
n1.setValue(0, 0); // 1
n2.setValue(1, 2); // 11*/
IntFinder.setGiven("1101102203");
n1 = new N1N2(IntFinder.getGiven());
n2 = new N1N2(IntFinder.getGiven());
n1.setValue(0, 0); // 1
n2.setValue(1, 3); // 101
System.out.println("string: " + n1.getValue());
System.out.println("string: " + n2.getValue());
System.out.println("result: " + ((IntFinder.findTillEndByN1N2(n1, n2) > -1) ? "found" : "NOT found"));
}
public static String setGiven(String givenString)
{
return IntFinder.given = givenString;
}
public static String getGiven()
{
return IntFinder.given;
}
public static int findTillEndByN1N2(N1N2 n1, N1N2 n2)
{
int retVal = -1, lenChange = n1.getLength() + n2.getLength() + n1.getStartIndex();
retVal = findNagainstN1N2(n1, n2, lenChange);
if (IntFinder.getGiven().length() == (n2.getEndIndex() + 1)) // base case 1 (last digit reached)
{
return 1;
}
else if (IntFinder.getGiven().length() < (n2.getEndIndex() + 1))
{
System.out.println("fatal err:");
System.exit(0);
}
if (retVal > -1) // recurse till end
{
if (!temp.getUsed())
{
temp = IntFinder.shallowCopy(n1);
temp.setUsed(true);
}
n1 = IntFinder.shallowCopy(n2);
n2.setValue(n2.getEndIndex() + 1 , retVal);
System.out.println("string: "+n2.getValue());
retVal = findTillEndByN1N2(n1, n2);
}
else
return retVal;
return retVal;
}
public static Integer findNagainstN1N2(N1N2 n1, N1N2 n2, Integer startIndex)
{
String remainingGiven = IntFinder.getGiven().substring(startIndex);
Integer i, n1n2Total = 0, retVal = -1;
n1n2Total = n1.getValue() + n2.getValue();
for (i = 0; i < remainingGiven.length(); i++)
{
try
{
int found = Integer.parseInt(remainingGiven.substring(0, (i+1)));
if (found == n1n2Total)
{
retVal = startIndex + i;
break;
}
else if (found > n1n2Total)
{
retVal = -1;
break;
}
}
catch (NumberFormatException e)
{
;
}
}
return retVal;
}
public static N1N2 shallowCopy(N1N2 from) {
N1N2 newN = new N1N2(IntFinder.getGiven());
newN.setValue(from.getStartIndex(), from.getEndIndex());
return newN;
}
}
>>N1N2.class
public class N1N2 {
private String givenString;
private int startIndex = 0;
private int endIndex = -1;
private int value = 0;
private int length = endIndex + 1;
private Boolean used = false;
public N1N2(String given) {
startIndex = 0;
endIndex = given.length() - 1;
givenString = given;
}
public int getValue() {
return value;
}
public int getLength() {
return length;
}
public Boolean getUsed()
{
return used;
}
public void setUsed(Boolean used)
{
this.used = used;
}
// public void outValues()
// {
// System.out.println("given:" + givenString + ", startIndex:"+ startIndex + ", endIndex: " + endIndex + ", length:" + length + ", value:" + value + "\n");
// }
public void setValue(int startIndex, int endIndex) {
this.value = Integer.parseInt(givenString.substring(startIndex, endIndex + 1));
this.startIndex = startIndex;
this.endIndex = endIndex;
this.length = (this.value + "").length();
// this.outValues();
}
public int getEndIndex() {
return this.endIndex;
}
public int getStartIndex() {
return this.startIndex;
}
}
Just set n1 and n2 properly before calling IntFinder.findTillEndByN1N2(n1, n2).
Observe the examples I used.
So to complete the program, create your own algorithm using two loops filling
n1.setValues and n2.setValues
Ex.
given = 1232447
//first loop
n1.setValues(0,0) // value = 1
n2.setValues(1,1) // value = 2
IntFinder.findTillEndByN1N2(n1, n2) // returns -1 // not found...
//next loop - increment n2 length
n1.setValues(0,0) // value = 1
n2.setValues(1,2) // value = 23
IntFinder.findTillEndByN1N2(n1, n2) // returns 1 // now found till end.
//ofcourse when n2 reached the last digit, increment n1 length by 1 and set n2 length back to 1.
//for given=444444889321420 // 44 444 488 932 1420
//all findTillEnd with n1 length 1 should fail so inc n1 length on outer loop
//n1.setValue(0, 1) // 2 digit number ( 44)
//n2.setValue(0, 0) // 4
//upon continuing loop, the desired result will be met.
In line with the above solution from Pham I could modify the code to exclude the edge cases and create a solution for the above question.This method will always return true if it complies with the pattern else false and yes we have to find first two elements to make it work.Checked for all test cases :
public static boolean complies(String input){
// List<Long> firstTwo = new ArrayList<Long>();
for(int i = 1; i < input.length(); i++){
for(int j = 0; j < i; j++){
long first = Long.parseLong(input.substring(0, j + 1));//substring form 0 to j
long second = Long.parseLong(input.substring(j + 1, i + 1));//substring from j + 1 to i
if(second < first)
continue;
String last = "" + first + second;
System.out.println("first :"+first+" second :"+second);
if(last.length() == input.length()){
return false;
}
// firstTwo.add(first);
// firstTwo.add(second);
while(last.length() < input.length()){
long nxt = first + second;
last += nxt;
first = second;
second = nxt;
}
if(last.equals(input)){//Matched!
System.out.println("matched");
// dont go further return true
return true;
}
// firstTwo.clear();
}
}
return false;
}
I'm having a difficult time with my program! For this method I have to check to see if all the numbers are distinct and I can't figure out for the life of me what I am doing wrong. I don't know if using an array is the best way to go. I must call the getDigit method.
for (int i = 0; i <= numDigits(number); i++) {
int digit = getDigit(number,i);
if (digit == getDigit(number,i)) {
return false;
}
}
return true;
You can first get each digit from the number and add them to a HashSet, then compare the size of HashSet with the number of digits present in the number
You can try this code:
public static void main(String[] args) {
int val = 123554;
Set<Integer> set = new HashSet<Integer>(); // HashSet contains only unique elements
int count = 0; // keeps track of number of digits encountered in the number
// code to get each digit from the number
while (val > 0) {
int tempVal = val % 10;
set.add(tempVal); // add each digit to the hash set
// you can have a boolean check like if(!set.add(tempVal)) return false; because add() returns false if the element is already present in the set.
val = val / 10;
count++;
}
if (count == set.size()) {
System.out.println("duplicate digit not present");
} else {
System.out.println("duplicate digit present");
}
}
Splitting Int into single digits:
Use something similar to this:
Code to print the numbers in the correct order:
int number; // = and int
LinkedList<Integer> stack = new LinkedList<Integer>();
while (number > 0) {
stack.push( number % 10 );
number = number / 10;
}
while (!stack.isEmpty()) {
print(stack.pop());
}
Source
Checking for Duplicates:
Again, something similar to this:
public static boolean duplicates (int [] x, int numElementsInX ) {
Set<Integer> set = new HashSet<Integer>();
for ( int i = 0; i < numElementsInX; ++i ) {
if ( set.contains( x[i])) {
return true;
}
else {
set.add(x[i]);
}
}
return false;
}
Source
Alternative
If you can split the array, an alternative could be to use:
int[] numbers = { 1, 5, 23, 2, 1, 6, 3, 1, 8, 12, 3 };
Arrays.sort(numbers);
for(int i = 1; i < numbers.length; i++) {
if(numbers[i] == numbers[i - 1]) {
System.out.println("Duplicate: " + numbers[i]);
}
}
i suppose that you want to compare for example the number 12345 with 23145, and prompt out a false, and if they are the same (digit by digit, prompt a true) , am i right?.
If you want to do this, you should make 2 arrays and you have to make sure to compare each position of both so you can compare digit by digit.
Hope it helps you
public boolean unique(int theNumber) {
String number = new Integer(theNumber).toString();
Set<Character> set = new LinkedHashSet<Character>();
for(char c:number.toCharArray()) {
set.add(Character.valueOf(c));
}
return number.length() == set.size();
}
I have to get the highest sum of some numbers which do not exceeded a limit.
For Example with 5, 7, 14 and a limit of 13, I must choose 5 and 7.
This example is with only 3 numbers but I have to be able to do this with a lot more numbers.
Is there a library or a method to do this?
I'm assuming the allowed inputs are positive integers. This will return [7, 5] for the example in your question.
public class Knapsack {
private class State {
State previousState = null;
int value = 0;
}
public List<Integer> solve(List<Integer> list, int limit) {
// validate input
if (limit < 0) {
throw new IllegalArgumentException();
}
if (list == null) {
throw new IllegalArgumentException();
}
for (Integer i: list) {
if (i == null || i.intValue() <= 0) {
throw new IllegalArgumentException();
}
}
// if the limit is 12, then 0 through 12 inclusive are valid amounts
State[] states = new State[limit + 1];
// the state at position x represents a way of achieving a sum of x
// if a state is null it means we can't get that sum, for example in your
// question there's no way to get a sum of 11 with any combination of inputs
// base state -- we can always get a sum of zero if we just take nothing
states[0] = new State();
// build up more states
for (Integer i: list) {
// iterate through the states backwards
// if we iterate forwards we'll encounter any changes we make to the list
// during the iteration, which has the effect of taking the same number
// multiple times
for (int j = limit - i.intValue(); j >= 0; --j) {
if (states[j] != null) {
State newState = new State();
newState.previousState = states[j];
newState.value = i.intValue();
states[i.intValue() + j] = newState;
}
}
}
// find the best state
State s = null;
for (int i = limit; i >= 0; --i) {
if (states[i] != null) {
// if all you care about is the best achievable sum, you can just
// return i here
s = states[i];
break;
}
}
// build the list of numbers
List<Integer> ret = new ArrayList<Integer>();
while (s.previousState != null) {
// this will add them backwards, change to add to the beginning of the list
// to preserve the same order as the input
ret.add(Integer.valueOf(s.value));
s = s.previousState;
}
return ret;
}
public static void main(String[] arg) {
List<Integer> list = new ArrayList<Integer>();
for (int i: new int[] { 5, 7, 9 }) {
list.add(Integer.valueOf(i));
}
int limit = 13;
Knapsack k = new Knapsack();
System.out.println(k.solve(list, limit));
}
}
I'm working on a puzzle that involves analyzing all size k subsets and figuring out which one is optimal. I wrote a solution that works when the number of subsets is small, but it runs out of memory for larger problems. Now I'm trying to translate an iterative function written in python to java so that I can analyze each subset as it's created and get only the value that represents how optimized it is and not the entire set so that I won't run out of memory. Here is what I have so far and it doesn't seem to finish even for very small problems:
public static LinkedList<LinkedList<Integer>> getSets(int k, LinkedList<Integer> set)
{
int N = set.size();
int maxsets = nCr(N, k);
LinkedList<LinkedList<Integer>> toRet = new LinkedList<LinkedList<Integer>>();
int remains, thresh;
LinkedList<Integer> newset;
for (int i=0; i<maxsets; i++)
{
remains = k;
newset = new LinkedList<Integer>();
for (int val=1; val<=N; val++)
{
if (remains==0)
break;
thresh = nCr(N-val, remains-1);
if (i < thresh)
{
newset.add(set.get(val-1));
remains --;
}
else
{
i -= thresh;
}
}
toRet.add(newset);
}
return toRet;
}
Can anybody help me debug this function or suggest another algorithm for iteratively generating size k subsets?
EDIT: I finally got this function working, I had to create a new variable that was the same as i to do the i and thresh comparison because python handles for loop indexes differently.
First, if you intend to do random access on a list, you should pick a list implementation that supports that efficiently. From the javadoc on LinkedList:
All of the operations perform as could be expected for a doubly-linked
list. Operations that index into the list will traverse the list from
the beginning or the end, whichever is closer to the specified index.
An ArrayList is both more space efficient and much faster for random access. Actually, since you know the length beforehand, you can even use a plain array.
To algorithms: Let's start simple: How would you generate all subsets of size 1? Probably like this:
for (int i = 0; i < set.length; i++) {
int[] subset = {i};
process(subset);
}
Where process is a method that does something with the set, such as checking whether it is "better" than all subsets processed so far.
Now, how would you extend that to work for subsets of size 2? What is the relationship between subsets of size 2 and subsets of size 1? Well, any subset of size 2 can be turned into a subset of size 1 by removing its largest element. Put differently, each subset of size 2 can be generated by taking a subset of size 1 and adding a new element larger than all other elements in the set. In code:
processSubset(int[] set) {
int subset = new int[2];
for (int i = 0; i < set.length; i++) {
subset[0] = set[i];
processLargerSets(set, subset, i);
}
}
void processLargerSets(int[] set, int[] subset, int i) {
for (int j = i + 1; j < set.length; j++) {
subset[1] = set[j];
process(subset);
}
}
For subsets of arbitrary size k, observe that any subset of size k can be turned into a subset of size k-1 by chopping of the largest element. That is, all subsets of size k can be generated by generating all subsets of size k - 1, and for each of these, and each value larger than the largest in the subset, add that value to the set. In code:
static void processSubsets(int[] set, int k) {
int[] subset = new int[k];
processLargerSubsets(set, subset, 0, 0);
}
static void processLargerSubsets(int[] set, int[] subset, int subsetSize, int nextIndex) {
if (subsetSize == subset.length) {
process(subset);
} else {
for (int j = nextIndex; j < set.length; j++) {
subset[subsetSize] = set[j];
processLargerSubsets(set, subset, subsetSize + 1, j + 1);
}
}
}
Test code:
static void process(int[] subset) {
System.out.println(Arrays.toString(subset));
}
public static void main(String[] args) throws Exception {
int[] set = {1,2,3,4,5};
processSubsets(set, 3);
}
But before you invoke this on huge sets remember that the number of subsets can grow rather quickly.
You can use
org.apache.commons.math3.util.Combinations.
Example:
import java.util.Arrays;
import java.util.Iterator;
import org.apache.commons.math3.util.Combinations;
public class tmp {
public static void main(String[] args) {
for (Iterator<int[]> iter = new Combinations(5, 3).iterator(); iter.hasNext();) {
System.out.println(Arrays.toString(iter.next()));
}
}
}
Output:
[0, 1, 2]
[0, 1, 3]
[0, 2, 3]
[1, 2, 3]
[0, 1, 4]
[0, 2, 4]
[1, 2, 4]
[0, 3, 4]
[1, 3, 4]
[2, 3, 4]
Here is a combination iterator I wrote recetnly
package psychicpoker;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
public class CombinationIterator<T> implements Iterator<Collection<T>> {
private int[] indices;
private List<T> elements;
private boolean hasNext = true;
public CombinationIterator(List<T> elements, int k) throws IllegalArgumentException {
checkArgument(k<=elements.size(), "Impossible to select %d elements from hand of size %d", k, elements.size());
this.indices = new int[k];
for(int i=0; i<k; i++)
indices[i] = k-1-i;
this.elements = elements;
}
public boolean hasNext() {
return hasNext;
}
private int inc(int[] indices, int maxIndex, int depth) throws IllegalStateException {
if(depth == indices.length) {
throw new IllegalStateException("The End");
}
if(indices[depth] < maxIndex) {
indices[depth] = indices[depth]+1;
} else {
indices[depth] = inc(indices, maxIndex-1, depth+1)+1;
}
return indices[depth];
}
private boolean inc() {
try {
inc(indices, elements.size() - 1, 0);
return true;
} catch (IllegalStateException e) {
return false;
}
}
public Collection<T> next() {
Collection<T> result = new ArrayList<T>(indices.length);
for(int i=indices.length-1; i>=0; i--) {
result.add(elements.get(indices[i]));
}
hasNext = inc();
return result;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
I've had the same problem today, of generating all k-sized subsets of a n-sized set.
I had a recursive algorithm, written in Haskell, but the problem required that I wrote a new version in Java.
In Java, I thought I'd probably have to use memoization to optimize recursion. Turns out, I found a way to do it iteratively. I was inspired by this image, from Wikipedia, on the article about Combinations.
Method to calculate all k-sized subsets:
public static int[][] combinations(int k, int[] set) {
// binomial(N, K)
int c = (int) binomial(set.length, k);
// where all sets are stored
int[][] res = new int[c][Math.max(0, k)];
// the k indexes (from set) where the red squares are
// see image above
int[] ind = k < 0 ? null : new int[k];
// initialize red squares
for (int i = 0; i < k; ++i) { ind[i] = i; }
// for every combination
for (int i = 0; i < c; ++i) {
// get its elements (red square indexes)
for (int j = 0; j < k; ++j) {
res[i][j] = set[ind[j]];
}
// update red squares, starting by the last
int x = ind.length - 1;
boolean loop;
do {
loop = false;
// move to next
ind[x] = ind[x] + 1;
// if crossing boundaries, move previous
if (ind[x] > set.length - (k - x)) {
--x;
loop = x >= 0;
} else {
// update every following square
for (int x1 = x + 1; x1 < ind.length; ++x1) {
ind[x1] = ind[x1 - 1] + 1;
}
}
} while (loop);
}
return res;
}
Method for the binomial:
(Adapted from Python example, from Wikipedia)
private static long binomial(int n, int k) {
if (k < 0 || k > n) return 0;
if (k > n - k) { // take advantage of symmetry
k = n - k;
}
long c = 1;
for (int i = 1; i < k+1; ++i) {
c = c * (n - (k - i));
c = c / i;
}
return c;
}
Of course, combinations will always have the problem of space, as they likely explode.
In the context of my own problem, the maximum possible is about 2,000,000 subsets. My machine calculated this in 1032 milliseconds.
Inspired by afsantos's answer :-)... I decided to write a C# .NET implementation to generate all subset combinations of a certain size from a full set. It doesn't need to calc the total number of possible subsets; it detects when it's reached the end. Here it is:
public static List<object[]> generateAllSubsetCombinations(object[] fullSet, ulong subsetSize) {
if (fullSet == null) {
throw new ArgumentException("Value cannot be null.", "fullSet");
}
else if (subsetSize < 1) {
throw new ArgumentException("Subset size must be 1 or greater.", "subsetSize");
}
else if ((ulong)fullSet.LongLength < subsetSize) {
throw new ArgumentException("Subset size cannot be greater than the total number of entries in the full set.", "subsetSize");
}
// All possible subsets will be stored here
List<object[]> allSubsets = new List<object[]>();
// Initialize current pick; will always be the leftmost consecutive x where x is subset size
ulong[] currentPick = new ulong[subsetSize];
for (ulong i = 0; i < subsetSize; i++) {
currentPick[i] = i;
}
while (true) {
// Add this subset's values to list of all subsets based on current pick
object[] subset = new object[subsetSize];
for (ulong i = 0; i < subsetSize; i++) {
subset[i] = fullSet[currentPick[i]];
}
allSubsets.Add(subset);
if (currentPick[0] + subsetSize >= (ulong)fullSet.LongLength) {
// Last pick must have been the final 3; end of subset generation
break;
}
// Update current pick for next subset
ulong shiftAfter = (ulong)currentPick.LongLength - 1;
bool loop;
do {
loop = false;
// Move current picker right
currentPick[shiftAfter]++;
// If we've gotten to the end of the full set, move left one picker
if (currentPick[shiftAfter] > (ulong)fullSet.LongLength - (subsetSize - shiftAfter)) {
if (shiftAfter > 0) {
shiftAfter--;
loop = true;
}
}
else {
// Update pickers to be consecutive
for (ulong i = shiftAfter+1; i < (ulong)currentPick.LongLength; i++) {
currentPick[i] = currentPick[i-1] + 1;
}
}
} while (loop);
}
return allSubsets;
}
This solution worked for me:
private static void findSubsets(int array[])
{
int numOfSubsets = 1 << array.length;
for(int i = 0; i < numOfSubsets; i++)
{
int pos = array.length - 1;
int bitmask = i;
System.out.print("{");
while(bitmask > 0)
{
if((bitmask & 1) == 1)
System.out.print(array[pos]+",");
bitmask >>= 1;
pos--;
}
System.out.print("}");
}
}
Swift implementation:
Below are two variants on the answer provided by afsantos.
The first implementation of the combinations function mirrors the functionality of the original Java implementation.
The second implementation is a general case for finding all combinations of k values from the set [0, setSize). If this is really all you need, this implementation will be a bit more efficient.
In addition, they include a few minor optimizations and a smidgin logic simplification.
/// Calculate the binomial for a set with a subset size
func binomial(setSize: Int, subsetSize: Int) -> Int
{
if (subsetSize <= 0 || subsetSize > setSize) { return 0 }
// Take advantage of symmetry
var subsetSizeDelta = subsetSize
if (subsetSizeDelta > setSize - subsetSizeDelta)
{
subsetSizeDelta = setSize - subsetSizeDelta
}
// Early-out
if subsetSizeDelta == 0 { return 1 }
var c = 1
for i in 1...subsetSizeDelta
{
c = c * (setSize - (subsetSizeDelta - i))
c = c / i
}
return c
}
/// Calculates all possible combinations of subsets of `subsetSize` values within `set`
func combinations(subsetSize: Int, set: [Int]) -> [[Int]]?
{
// Validate inputs
if subsetSize <= 0 || subsetSize > set.count { return nil }
// Use a binomial to calculate total possible combinations
let comboCount = binomial(setSize: set.count, subsetSize: subsetSize)
if comboCount == 0 { return nil }
// Our set of combinations
var combos = [[Int]]()
combos.reserveCapacity(comboCount)
// Initialize the combination to the first group of set indices
var subsetIndices = [Int](0..<subsetSize)
// For every combination
for _ in 0..<comboCount
{
// Add the new combination
var comboArr = [Int]()
comboArr.reserveCapacity(subsetSize)
for j in subsetIndices { comboArr.append(set[j]) }
combos.append(comboArr)
// Update combination, starting with the last
var x = subsetSize - 1
while true
{
// Move to next
subsetIndices[x] = subsetIndices[x] + 1
// If crossing boundaries, move previous
if (subsetIndices[x] > set.count - (subsetSize - x))
{
x -= 1
if x >= 0 { continue }
}
else
{
for x1 in x+1..<subsetSize
{
subsetIndices[x1] = subsetIndices[x1 - 1] + 1
}
}
break
}
}
return combos
}
/// Calculates all possible combinations of subsets of `subsetSize` values within a set
/// of zero-based values for the set [0, `setSize`)
func combinations(subsetSize: Int, setSize: Int) -> [[Int]]?
{
// Validate inputs
if subsetSize <= 0 || subsetSize > setSize { return nil }
// Use a binomial to calculate total possible combinations
let comboCount = binomial(setSize: setSize, subsetSize: subsetSize)
if comboCount == 0 { return nil }
// Our set of combinations
var combos = [[Int]]()
combos.reserveCapacity(comboCount)
// Initialize the combination to the first group of elements
var subsetValues = [Int](0..<subsetSize)
// For every combination
for _ in 0..<comboCount
{
// Add the new combination
combos.append([Int](subsetValues))
// Update combination, starting with the last
var x = subsetSize - 1
while true
{
// Move to next
subsetValues[x] = subsetValues[x] + 1
// If crossing boundaries, move previous
if (subsetValues[x] > setSize - (subsetSize - x))
{
x -= 1
if x >= 0 { continue }
}
else
{
for x1 in x+1..<subsetSize
{
subsetValues[x1] = subsetValues[x1 - 1] + 1
}
}
break
}
}
return combos
}