Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I need help with solving the second level of Google's Foobar challenge.
Commander Lambda uses an automated algorithm to assign minions randomly to tasks, in order to keep her minions on their toes. But you've noticed a flaw in the algorithm - it eventually loops back on itself, so that instead of assigning new minions as it iterates, it gets stuck in a cycle of values so that the same minions end up doing the same tasks over and over again. You think proving this to Commander Lambda will help you make a case for your next promotion.
You have worked out that the algorithm has the following process:
1) Start with a random minion ID n, which is a nonnegative integer of length k in base b
2) Define x and y as integers of length k. x has the digits of n in descending order, and y has the digits of n in ascending order
3) Define z = x - y. Add leading zeros to z to maintain length k if necessary
4) Assign n = z to get the next minion ID, and go back to step 2
For example, given minion ID n = 1211, k = 4, b = 10, then x = 2111, y = 1112 and z = 2111 - 1112 = 0999. Then the next minion ID will be n = 0999 and the algorithm iterates again: x = 9990, y = 0999 and z = 9990 - 0999 = 8991, and so on.
Depending on the values of n, k (derived from n), and b, at some point the algorithm reaches a cycle, such as by reaching a constant value. For example, starting with n = 210022, k = 6, b = 3, the algorithm will reach the cycle of values [210111, 122221, 102212] and it will stay in this cycle no matter how many times it continues iterating. Starting with n = 1211, the routine will reach the integer 6174, and since 7641 - 1467 is 6174, it will stay as that value no matter how many times it iterates.
Given a minion ID as a string n representing a nonnegative integer of length k in base b, where 2 <= k <= 9 and 2 <= b <= 10, write a function solution(n, b) which returns the length of the ending cycle of the algorithm above starting with n. For instance, in the example above, solution(210022, 3) would return 3, since iterating on 102212 would return to 210111 when done in base 3. If the algorithm reaches a constant, such as 0, then the length is 1.
Test Cases: Solution.solution("1211", 10) returns 1
Solution.solution("210022", 3) returns 3
Here is my code:
import java.util.ArrayList;
import java.util.Arrays;
public class Solution {
public static int solution(String n, int b) {
int k = n.length();
String m = n;
ArrayList<String> minionID = new ArrayList<>();
while (!minionID.contains(m)) {
minionID.add(m);
char[] s = m.toCharArray();
Arrays.sort(s);
int y = Integer.parseInt(toString(s));
int x = Integer.parseInt(reverseString(s));
if (b == 10) {
int intM = x - y;
m = Integer.toString(intM);
} else {
int intM10 = ((int) Integer.parseInt(toBase10(x,b))) - ((int) Integer.parseInt(toBase10(y, b)));
m = toBaseN(intM10, b);
}
m = addLeadingZeros(k, m);
}
System.out.println(minionID);
return minionID.size() - minionID.indexOf(m);
}
private static String toBaseN (int intBase10, int b) {
int residual = intBase10;
ArrayList<String> digitsBaseN = new ArrayList<>();
while (residual >= b) {
int r = residual % b;
digitsBaseN.add(Integer.toString(residual));
residual = (residual - r) / b;
}
digitsBaseN.add(Integer.toString(residual));
StringBuilder reverseDigits = new StringBuilder();
for (int i = digitsBaseN.size() -1; i >= 0; i--) {
reverseDigits.append(digitsBaseN.get(i));
}
return reverseDigits.toString();
}
private static String toBase10 (int intBaseN, int b) {
int[] xArr = new int[Integer.toString(intBaseN).length()];
int count = 0;
for (int i = xArr.length - 1; i >= 0; i--) {
xArr[count] = Integer.toString(intBaseN).charAt(i) - '0';
count++;
}
int yBase10 = 0;
for(int i = 0; i < xArr.length; i++) {
yBase10 += xArr[i] * (Math.pow(b, i));
}
return Integer.toString(yBase10);
}
public static String toString(char[] arr) {
StringBuilder newString = new StringBuilder();
for (char c : arr) {
newString.append(c);
}
if (newString.toString().contains("-")) {
newString.deleteCharAt(0);
}
return newString.toString();
}
public static String reverseString(char[] arr) {
StringBuilder newString = new StringBuilder();
for (int i = arr.length - 1; i >= 0; i--) {
newString.append(arr[i]);
}
if (newString.toString().contains("-")) {
newString.deleteCharAt(newString.length()-1);
}
return newString.toString();
}
public static String addLeadingZeros(int k, String z) {
if (k > z.length()) {
String zeros = "";
for (int i = 0; i < (k - z.length()); i++) {
zeros += "0";
}
zeros += z;
return zeros;
}
return z;
}
It only works for three out of the ten test cases
def answer(n, b):
k = len(n)
m = n
mini_id = []
while m not in mini_id:
mini_id.append(m)
s = sorted(m)
x_descend = ''.join(s[::-1])
y_ascend = ''.join(s)
if b == 10:
int_m = int(x_descend) - int(y_ascend)
m = str(int_m)
else:
int_m_10 = int(to_base_10(x_descend, b)) - int(to_base_10(y_ascend, b))
m = to_base_n(str(int_m_10), b)
m = (k - len(m)) * '0' + m
return len(mini_id) - mini_id.index(m)
Related
I have to create a program which adds two integers and prints the sum vertically.
For example, I have.
a=323, b=322.
The output should be:
6
4
5
I've created the code for when the integers are up to two digits, but I want it to work for at least three digits.
Below is the best I could think of.
It may be completely wrong, but the only problem I'm facing is the declaration of array.
It says that the array might not be initialized.
If I set it to null then also it won't assign values to it later.
I know maybe I'm making a big mistake here, but I'll really appreciate if anyone could help me out.
Please keep in mind that I must not use any other functions for this code.
Hope I'm clear.
public class Vert
{
public static void main(String args[])
{
int n,i=0,j,a=323,b=322;
int s[];
n=a+b;
while(n>9)
{
s[i]=n%10;
i++;
s[i]=n/10;
if(s[i]>9)
{
n=s[i];
}
}
j=i;
for(j=i;j>=0;j--)
{
System.out.println(+s[j]);
}
}
}
String conversion seems like cheating, so here's a Stack.
int a = 323, b = 322;
java.util.Stack<Integer> stack = new java.util.Stack<>();
int n = a + b;
while (n > 0) {
stack.push(n % 10);
n = n / 10;
}
while (!stack.isEmpty())
System.out.println(stack.pop());
If an array is required, you need two passes over the sum
int a = 323, b = 322;
// Get the size of the array
int n = a + b;
int size = 0;
while (n > 0) {
size++;
n = n / 10;
}
// Build the output
int s[] = new int[size];
n = a + b;
for (int i = size - 1; n > 0; i--) {
s[i] = n % 10;
n = n / 10;
}
// Print
for (int x : s) {
System.out.println(x);
}
To initialize an array, you need to specify the size of your array as next:
int s[] = new int[mySize];
If you don't know the size of your array, you should consider using a List of Integer instead as next:
List<Integer> s = new ArrayList<Integer>();
Here is how it could be done:
// Convert the sum into a String
String result = String.valueOf(a + b);
for (int i=0; i <result.length();i++) {
// Print one character corresponding to a digit here per line
System.out.println(result.charAt(i));
}
I'd do it like this:
int a = 322;
int b = 322;
int sum = a + b;
String s = Integer.toString(sum);
for(int i = 0; i < s.length(); i++) {
System.out.println(s.charAt(i));
}
But your problem looks like an array is required.
The steps are same as in my solution:
Use int values
Sum the int values (operation)
Convert the int value in an array/string
Output the array/string
I am trying to figure out "what 5-digit number when multiplied by 4 gives you its reverse?" using this code but I get error: Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 5
at java.lang.String.charAt(String.java:658)
at Digits.main(Digits.java:12)
public class Digits{
public static void main(String[] args) {
int n = 0;
int b = 0;
String number = Integer.toString(n);
String backwards = Integer.toString(b);
for (int x = 9999; x < 100000 ; x++ ) {
n = x;
b = x *4;
if (number.charAt(0) == backwards.charAt(5 )&& number.charAt(1) == backwards.charAt(4)
&& number.charAt(2) == backwards.charAt(3) && number.charAt(3) == backwards.charAt(2)
&& number.charAt(4) == backwards.charAt(1) && number.charAt(5) == backwards.charAt(0)) {
System.out.println(n);
break;
}
}
Any help would be grealy appreciated
Correct. Because the first five characters are at indices 0, 1, 2, 3 and 4. I would use a StringBuilder (because of StringBuilder.reverse()). And, I would suggest you restrict variable visibility. Then remember to modify number and backwards when you change n and/or b. Something like
for (int x = 9999; x < 100000; x++) {
int n = x;
int b = x * 4;
String number = Integer.toString(n);
String backwards = Integer.toString(b);
StringBuilder sb = new StringBuilder(number);
sb.reverse();
if (sb.toString().equals(backwards)) {
System.out.printf("%s * 4 = %s", number, backwards);
}
}
And I get
21978 * 4 = 87912
backwards and number are String, which internally uses an array. And an array are indexed from 0 to size-1 . Hence such statements will throw ArrayIndexOutOfBoundsException:
backwards.charAt(5 )
number.charAt(5)
At the time you create your strings, both of your ints are 0, so both of your strings are "0" for the duration of your program. What you really want is the strings to change every time your number changes. So your code should look more like this:
public class Digits{
public static void main(String[] args) {
int n = 0;
int b = 0;
String number;
String backwards;
for (int x = 10000; x < 100000 ; x++ ) {
n = x;
b = x *4;
number = Integer.toString(n);
backwards = Integer.toString(b)
. . .
}
In addition, arrays in Java are zero-indexed, so for instance for the string "10000", your program will throw the index out of bounds exception on backwards.charAt(5) because the string is indexed from character 0 to character 4.
Hi I have the following method. What it does is it finds all the possible paths from the top left to bottom right of a N x M matrix. I was wondering what is the best way to optimize it for speed as it is a little slow right now. The resulted paths are then stored in a set.
EDIT I forgot to clarify you can only move down or right to an adjacent spot, no diagonals from your current position
For example
ABC
DEF
GHI
A path from the top left to bottom right would be ADEFI
static public void printPaths (String tempString, int i, int j, int m, int n, char [][] arr, HashSet<String> palindrome) {
String newString = tempString + arr[i][j];
if (i == m -1 && j == n-1) {
palindrome.add(newString);
return;
}
//right
if (j+1 < n) {
printPaths (newString, i, j+1, m, n, arr, palindrome);
}
//down
if (i+1 < m) {
printPaths (newString, i+1, j, m, n, arr, palindrome);
}
}
EDIT Here is the entirety of the code
public class palpath {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("palpath.in"));
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("palpath.out")));
StringTokenizer st = new StringTokenizer(br.readLine());
int d = Integer.parseInt(st.nextToken());
char[][] grid = new char [d][d];
String index = null;
for(int i = 0; i < d; i++)
{
String temp = br.readLine();
index = index + temp;
for(int j = 0; j < d; j++)
{
grid[i][j] = temp.charAt(j);
}
}
br.close();
int counter = 0;
HashSet<String> set = new HashSet<String>();
printPaths ("", 0, 0, grid.length, grid[0].length, grid, set);
Iterator<String> it = set.iterator();
while(it.hasNext()){
String temp = it.next();
StringBuilder sb = new StringBuilder(temp).reverse();
if(temp.equals(sb.toString())) {
counter++;
}
}
pw.println(counter);
pw.close();
}
static public void printPaths (String tempString, int i, int j, int m, int n, char [][] arr, HashSet<String> palindrome) {
String newString = tempString + arr[i][j];
if (i == m -1 && j == n-1) {
palindrome.add(newString);
return;
}
//right
if (j+1 < n) {
printPaths (newString, i, j+1, m, n, arr, palindrome);
}
//down
if (i+1 < m) {
printPaths (newString, i+1, j, m, n, arr, palindrome);
}
}
Given a graph of length M x N, all paths from (0,0) to (M-1, N-1) that only involve rightward and downward moves are guaranteed to contain exactly M-1 moves rightward and N-1 moves downward.
This presents us with an interesting property: we can represent a path from (0,0) to (M-1, N-1) as a binary string (0 indicating a rightward move and 1 indicating a downward move).
So, the question becomes: how fast can we print out a list of permutations of that bit string?
Pretty fast.
public static void printPaths(char[][] arr) {
/* Get Smallest Bitstring (e.g. 0000...111) */
long current = 0;
for (int i = 0; i < arr.length - 1; i++) {
current <<= 1;
current |= 1;
}
/* Get Largest Bitstring (e.g. 111...0000) */
long last = current;
for (int i = 0; i < arr[0].length - 1; i++) {
last <<= 1;
}
while (current <= last) {
/* Print Path */
int x = 0, y = 0;
long tmp = current;
StringBuilder sb = new StringBuilder(arr.length + arr[0].length);
while (x < arr.length && y < arr[0].length) {
sb.append(arr[x][y]);
if ((tmp & 1) == 1) {
x++;
} else {
y++;
}
tmp >>= 1;
}
System.out.println(sb.toString());
/* Get Next Permutation */
tmp = (current | (current - 1)) + 1;
current = tmp | ((((tmp & -tmp) / (current & -current)) >> 1) - 1);
}
}
You spend a lot of time on string memory management.
Are strings in Java mutable? If you can change chars inside string, then set length of string as n+m, and use this the only string, setting (i+j)th char at every iteration. If they are not mutable, use array of char or something similar, and transform it to string at the end
For a given size N×M of the array all your paths have N+M+1 items (N+M steps), so the first step of optimization is getting rid of recursion, allocating an array and running the recursion with while on explicit stack.
Each partial path can be extended with one or two steps: right or down. So you can easily make an explicit stack with positions visited and a step taken on each position. Put the position (0,0) to the stack with phase (step taken) 'none', then:
while stack not empty {
if stack is full /* reached lower-right corner, path complete */ {
print the path;
pop;
}
else if stack.top.phase == none {
stack.top.phase = right;
try push right-neighbor with phase none;
}
else if stack.top.phase == right {
stack.top.phase = down;
try push down-neighbor with phase none;
}
else /* stack.top.phase == down */ {
pop;
}
}
If you make a few observations about your requirements you can optimise this drastically.
There will be exactly (r-1)+(c-1) steps (where r = rows and c = columns).
There will be exactly (c-1) steps to the right and (r-1) steps down.
You therefore can use numbers where a zero bit could (arbitrarily) indicate a down step while a 1 bit steps across. We can then merely iterate over all numbers of (r-1)+(c-1) bits containing just (c-1) bits set. There's a good algorithm for that at the Stanford BitTwiddling site Compute the lexicographically next bit permutation.
First a BitPatternIterator I have used before. You could pull out the code in hasNext if you wish.
/**
* Iterates all bit patterns containing the specified number of bits.
*
* See "Compute the lexicographically next bit permutation" http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
*
* #author OldCurmudgeon
*/
public static class BitPattern implements Iterable<BigInteger> {
// Useful stuff.
private static final BigInteger ONE = BigInteger.ONE;
private static final BigInteger TWO = ONE.add(ONE);
// How many bits to work with.
private final int bits;
// Value to stop at. 2^max_bits.
private final BigInteger stop;
// All patterns of that many bits up to the specified number of bits.
public BitPattern(int bits, int max) {
this.bits = bits;
this.stop = TWO.pow(max);
}
#Override
public Iterator<BigInteger> iterator() {
return new BitPatternIterator();
}
/*
* From the link:
*
* Suppose we have a pattern of N bits set to 1 in an integer and
* we want the next permutation of N 1 bits in a lexicographical sense.
*
* For example, if N is 3 and the bit pattern is 00010011, the next patterns would be
* 00010101, 00010110, 00011001,
* 00011010, 00011100, 00100011,
* and so forth.
*
* The following is a fast way to compute the next permutation.
*/
private class BitPatternIterator implements Iterator<BigInteger> {
// Next to deliver - initially 2^n - 1 - i.e. first n bits set to 1.
BigInteger next = TWO.pow(bits).subtract(ONE);
// The last one we delivered.
BigInteger last;
#Override
public boolean hasNext() {
if (next == null) {
// Next one!
// t gets v's least significant 0 bits set to 1
// unsigned int t = v | (v - 1);
BigInteger t = last.or(last.subtract(BigInteger.ONE));
// Silly optimisation.
BigInteger notT = t.not();
// Next set to 1 the most significant bit to change,
// set to 0 the least significant ones, and add the necessary 1 bits.
// w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));
// The __builtin_ctz(v) GNU C compiler intrinsic for x86 CPUs returns the number of trailing zeros.
next = t.add(ONE).or(notT.and(notT.negate()).subtract(ONE).shiftRight(last.getLowestSetBit() + 1));
if (next.compareTo(stop) >= 0) {
// Dont go there.
next = null;
}
}
return next != null;
}
#Override
public BigInteger next() {
last = hasNext() ? next : null;
next = null;
return last;
}
#Override
public void remove() {
throw new UnsupportedOperationException("Not supported.");
}
#Override
public String toString() {
return next != null ? next.toString(2) : last != null ? last.toString(2) : "";
}
}
}
Using that to iterate your solution:
public void allRoutes(char[][] grid) {
int rows = grid.length;
int cols = grid[0].length;
BitPattern p = new BitPattern(rows - 1, cols + rows - 2);
for (BigInteger b : p) {
//System.out.println(b.toString(2));
/**
* Walk all bits, taking a step right/down depending on it's set/clear.
*/
int x = 0;
int y = 0;
StringBuilder s = new StringBuilder(rows + cols);
for (int i = 0; i < rows + cols - 2; i++) {
s.append(grid[y][x]);
if (b.testBit(i)) {
y += 1;
} else {
x += 1;
}
}
s.append(grid[y][x]);
// That's a solution.
System.out.println("\t" + s);
}
}
public void test() {
char[][] grid = {{'A', 'B', 'C'}, {'D', 'E', 'F'}, {'G', 'H', 'I'}};
allRoutes(grid);
char[][] grid2 = {{'A', 'B', 'C'}, {'D', 'E', 'F'}, {'G', 'H', 'I'}, {'J', 'K', 'L'}};
allRoutes(grid2);
}
printing
ADGHI
ADEHI
ABEHI
ADEFI
ABEFI
ABCFI
ADGJKL
ADGHKL
ADEHKL
ABEHKL
ADGHIL
ADEHIL
ABEHIL
ADEFIL
ABEFIL
ABCFIL
which - to my mind - looks right.
Here's the problem.
http://usaco.org/index.php?page=viewproblem2&cpid=487
Here's the solution.
http://usaco.org/current/data/sol_marathon_bronze.html
I already solved that problem but my code is too slow to pass all the testcases until 100, 000 points, so I read the official solution, but I could not understand the solution, the math part that says:
int largestSkip = 0;
for(int i = 1; i < n-1; i++) {
int noSkipDistance = Math.abs(x[i+1] - x[i]) + Math.abs(x[i] - x[i-1]) + Math.abs(y[i+1] - y[i]) + Math.abs(y[i] - y[i-1]);
int skipDistance = Math.abs(x[i+1] - x[i-1]) + Math.abs(y[i+1] - y[i-1]);
largestSkip = Math.max(largestSkip, noSkipDistance - skipDistance);
}
How do they get those formulas?, if I understood well the variable skipDistance goes from 0 - skip 1- 2, 1- skip 2 -3, and so on, the other variables noskipDistance goes from 0 -2, 1 -3 (no skipping) and so on, are there other similar problems? or please if anyone could help me to understand I will be really grateful
The best solution that comes to my mind is, but It is too slow
import java.io.*;
import java.util.*;
public class marathon {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("marathon.in"));
PrintWriter pw = new PrintWriter(new File("marathon.out"));
int n = Integer.parseInt(br.readLine());
int[][] arr = new int[n][2];
boolean[] flag = new boolean[n];
for(int i = 0; i < n; i++){
String s = br.readLine();
int n1 = Integer.parseInt(s.substring(0 , s.indexOf(" ")));
int n2 = Integer.parseInt(s.substring(s.indexOf(" ") +1, s.length()));
arr[i][0] = n1;
arr[i][1] = n2;
}
Arrays.fill(flag, true);
long min = Long.MAX_VALUE;
for(int i = 1; i < (n -1); i++){
flag[i] = false;
min = Math.min(min, solve(arr, flag, n));
flag[i] = true;
}
pw.println(min);
pw.close();
}
public static long solve(int[][]arr, boolean[] flag, int n){
long min = Long.MAX_VALUE;
long sum = 0;
List<Integer> listX = new ArrayList<>();
List<Integer> listY = new ArrayList<>();
listX.add(arr[0][0]);
listY.add(arr[0][1]);
for(int i = 1; i < n -1; i++){
if(flag[i]){
listX.add(arr[i][0]);
listY.add(arr[i][1]);
}
}
listX.add(arr[n-1][0]);
listY.add(arr[n-1][1]);
for(int i = 1; i < listX.size(); i++){
sum = Math.abs(listX.get(i -1) - listX.get(i)) + Math.abs(listY.get(i -1) - listY.get(i)) + sum;
}
return sum;
}
}
What happening in this solution is what is written in their editorial. At each point it is calculating the effective length it would go if it chose that point as skipping point.
For eq:
If there are four points (a, b, c, d) and you are at point 'a'. You can either go to 'b' or skip 'b'.
If you go to 'b', distance traveled is: Manhattan distance a -> b + b -> c; (Lets call it X)
If you skip 'b', distance traveled is: Manhattan distance a ->c; (Call it Y)
Gain in distance by choosing this choice = X - Y.
You objective is to find the maximum gain and choose the point which gives it.
In the end, ans is: sum of all Manhattan distances - the maximum gain.
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
}