Related
I want to make a program that will generate a random number. I know I need to use Math.random() for this but I want to set a ratio for a number that can repeat. for example, I want the compiler to give a number from 1-10 but I want 5 repeat 3 times more than another number. how do I do that? please help thank you.
for exactly that case:
private static final int[] CHOICES = new int[] { 1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9, 10 };
public static int strangeRandom() {
return CHOICES[ThreadLocalRandom.current().nextInt(CHOICES.length)];
}
selects randomly from a given set of choices & you control the choices so that 5 is more likely.
First, you should use the Random class, not Math.random(). For one, it has the nice helper method nextInt(int n) that generates a random integer between 0 and n-1 (inclusive).
In your particular case, you want a number 1-10, so generate 0-9 and add 1, i.e. nextInt(10) + 1.
But you want the number 5 to occur 3 times more often. A quick dirty way is the generate two extra number (1-12) and map them to 5.
Random rnd = new Random();
int num = rnd.nextInt(12) + 1;
if (num == 11 || num == 12)
num = 5;
As I said, quick and dirty, but it does the trick for your particular case.
Now, for a more generic solution, you want to be able to specify weighted probabilities. Numbers 1-4, 6-10 has a weight of 1, and 5 has a weight of 3.
What you then do is sum the weight (12), and generate a random number, then find the number where the accumulated weight exceeds the random number.
Here is a method for that:
private static int random(Random rnd, int ... weights) {
if (weights.length < 2)
throw new IllegalArgumentException("Need at least two weights");
int total = 0;
for (int weight : weights) {
if (weight <= 0)
throw new IllegalArgumentException("Invalid weight: " + weight);
if ((total += weight) < 0)
throw new IllegalArgumentException("Weight overflow");
}
for (int i = 0, val = rnd.nextInt(total); ; i++, val -= weights[i])
if (val < weights[i])
return i;
}
You could then call it like this:
Random rnd = new Random();
int num = random(rnd, 1,1,1,1,3,1,1,1,1,1) + 1;
Notice the +1, since the method is like Random.nextInt(n) and returns a number 0 to n-1, where n is the number of weights given.
You could easily turn it into a nice class, where the weights are given on the constructor, and the class manages the Random object for you.
Beware that performance will degrade as number of weights go up. There is a way to use TreeMap or binarySearch to improve that, but you need the class implementation, so it can prepare the data.
This will have the 5 come up at least 3 times as often as the other 9 numbers:
private static int getRandom ()
{
Random r = new Random();
int next = r.nextInt(100000) + 1;
if (next <= 25000)
{
return 5;
}
else
{
return r.nextInt(10) + 1;
}
}
Note: You can also preclude the 5 in the else case to try and hit the "3 times" likelihood better. I didn't for simplicity.
Another Variation:
Here is a variation that uses modulus of the nextInt() instead of 25000/100000 split.
Also, I put code in the else case that tries to preclude the 5 (without the infinite loop).
private static int getRandom ()
{
Random r = new Random();
int next = r.nextInt();
if (next % 4 == 0)
{
return 5;
}
else
{
int p = r.nextInt(10) + 1;
int tries = 0;
// try to not get a 5 for a few times
while (p == 5 && tries++ < 4)
{
p = r.nextInt(10) + 1;
}
return p;
}
}
Test Code:
public static void main(String[] args)
{
Map<Integer, Integer> frequecnyMap = new HashMap<Integer, Integer>();
for (int i = 0; i < 12000; i++)
{
int r = getRandom();
Integer n = frequecnyMap.get(r);
if (n == null)
{
frequecnyMap.put(r, 1);
}
else
{
frequecnyMap.put(r, n + 1);
}
}
System.out.println(frequecnyMap);
}
Sample Output (2nd Variation):
{1=971, 2=975, 3=995, 4=1037, 5=3025, 6=1042, 7=995, 8=976, 9=969, 10=1015}
{1=1016, 2=1019, 3=994, 4=968, 5=3068, 6=1030, 7=996, 8=914,
9=990, 10=1005}
{1=939, 2=944, 3=979, 4=986, 5=3023, 6=1040, 7=1007, 8=1046, 9=997, 10=1039}
If you want to have full control of your random generated numbers, you should do something like this:
public class MyRandom {
private int[] probability;
private long[] ntimes;
private long times;
public MyRandom(int[] probability) {
this.probability = new int[10];
System.arraycopy(probability, 0, this.probability, 0, probability.length);
ntimes=new long[10];
for(int i=0; i < ntimes.length; i++)
ntimes[i]=0;
times=0;
}
public void showProbability() {
for (long i : probability) {
System.out.print(i+" ");
}
System.out.println();
}
public int random() {
int t = 10;
int r = (int)Math.floor(Math.random()*10+1);
double p = 0;
if (times == 0)
p = 0;
else
p = ntimes[r-1]*100/times;
System.out.println("P: "+p +" : "+probability[r-1]);
while (p > probability[r-1] && t > 0) {
r = (int)Math.floor(Math.random()*10+1);
p = ntimes[r-1]*100/times;
t--;
}
ntimes[r-1]++;
times++;
return r;
}
public long getOcurrences(int i) {
return ntimes[i-1];
}
//This is an example of how to use it.
public static void main(String[] args) {
int[] p = {5, 5, 5, 5, 30, 5, 5, 5, 10, 15};
MyRandom mr = new MyRandom(p);
for (int i = 0; i < 2000; i++) {
int r = mr.random();
System.out.println("Id: "+i+" Number: "+r+" Ocurrences: "+mr.getOcurrences(r));
}
}
}
I am trying to use recursion to find the minimum amount of coins to make a given amount. I have code that is able to list the minimum amount of coins required, but I can't seem to find a way to print off which coins were used to come up with the solution. I've searched and found similar examples, but I can't seem to properly apply it to this.
Here is what I have thus far:
import java.util.*;
public class Coins{
public static int findMinCoins(int[] currency, int amount) {
int i, j, min, tempSolution;
min = amount;
for (i = 0; i < currency.length; i++) {
if (currency[i] == amount) {
return 1;
}
}
for (j = 1; j <= (amount / 2); j++) {
tempSolution = findMinCoins(currency, j) + findMinCoins(currency, amount - j);
if (tempSolution < min) {
min = tempSolution;
}
}
return min;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] USA =
{1, 5, 10, 25, 50};
System.out.println("Please enter an integer amount.");
int amount = in.nextInt();
int minCoins = findMinCoins(USA, amount);
System.out.println("The minimum number of coins to make " + amount + " in United States currency is " + minCoins + ".");
System.out.println("The coins used were:");
/*Print coins used to find minCoins.*/
in.close();
}
}
An example of the code running thus far:
Please enter an integer amount.
17
The minimum number of coins to make 17 in United States currency is 4.
The coins used were:
If someone could give me some insight on how to do this, it would be much appreciated.
I think this should totally work with what you want to achieve. Just call the public static int findMinCoins(arg1, arg2) and it will output you the minimum number of coins and all the particular coins(It will show times the number of its occurrence) used using recursive algorithms.
public static int findMinCoins(int[] currency, int amount) {
int min = findMinCoins(currency, amount, 0);
System.out.print("The coins used were: ");
return min;
}
private static int findMinCoins(int[] currency, int amount, int min){
int number, value1, value2;
int min1 = min;
for(int i=currency.length-1; i>=0; i--) {
if (amount>=currency[i]){
amount = amount - currency[i];
System.out.print(currency[i] + " ");
min1 = findMinCoins(currency, amount, min1);
return ++min1;
}
}
return min1;
}
Here is a recursive code (working, but need a fix ...).
The idea is to pass and array with all coins {1,5,10,25,50} and recursively call from left to right (until end of array)
NOTES :
There is a little bug in the output
The number is passed as array of 1 element instead of a primitive int. (this is to have a reference type to keep its value through all recursive calls :
public class coins {
public static void main(String[] args) {
// arrays of coin types
int[] coinTypes = { 0, 1, 5, 10, 25, 50 };
// arrays are references, so changing them
// inside the recursive function will 'really' change
int[] price = {11}; // sample input
tryBigger(price, coinTypes, 0);
}
// tries to see if higher coin is possible by passing array
// of coin types and recursively call until the end of array
public static void tryBigger(int[] price, int[] theCoins, int index) {
if (index < theCoins.length-1){
// until last element
if (price[0] > theCoins[index]){
tryBigger(price, theCoins, ++index);
}
}
// find amount of this coin
int thisCoin = price[0]/theCoins[index];
// remove the amount already got before
price[0] = price[0] - thisCoin*theCoins[index];
System.out.println(thisCoin + " coins of " + theCoins[index]);
return;
}
}
In the code you offer, either the number 1 or min are returned by the recursive function. Keeping with this method, one way you could obtain the list of coins is by altering the return variable to include both coins and count.
Here's an example of the general idea; since I don't know about programming in Java, I'll leave the implementation to you.
if (currency[i] == amount){
return [1,i];
...
temp1 = findMinCoins(currency,j);
temp2 = findMinCoins(currency,amount - j);
if(temp1[0] + temp2[0] < min[0]){
min = [temp1[0] + temp2[0]] concatenated with temp1[1..] and temp2[1..]
Here you go, you have a test and the function is below. Notice that edge cases are not handled, such as empty currency or negative amount.
It is assumed that the currency array is sorted. If not, sort it with Arrays.sort(currency).
public class FindMinimumCoinsTest {
#Test
public void test() throws Exception {
int[] USA = { 1, 5, 10, 25, 50 };
assertEquals(2, findMinCoins(USA, 11));
assertEquals(4, findMinCoins(USA, 8));
assertEquals(4, findMinCoins(USA, 111));
assertEquals(3, findMinCoins(USA, 27));
}
public static int findMinCoins(int[] currency, int amount) {
int coins = 0;
int sum = 0;
int value, n;
for (int i = currency.length - 1; i >= 0; i--) {
value = currency[i];
n = (amount - sum) / value;
if (n > 0) {
coins += n;
sum += n * value;
}
}
return coins;
}
}
Also, no need for recursion using this method ;)
I was working on something similar and this is what I came up with. You can hold the used coins in a separate array and have a helper function print the last used coins recursively. If you want to return a list or a string you can just have the helper create and return one.
/**
* FIND MINIMAL NUMBER OF COINS TO MAKE CHANGE, WITH CHANGE VALUES: 1, 2, 5, 10, 20, 50, 100, 200
*
* #param change The amount of change we need to give
* #return Minimal amount of coins used
*/
public static int minimalNumberOfCoinsToMakeChange(int change) {
int[] denominations = {1, 2, 5, 10, 20, 50, 100, 200};
int[] dp = new int[change + 1];
int[] origins = new int[change+1];
dp[0] = 0;
for (int i = 1; i <= change; i++) {
dp[i] = Integer.MAX_VALUE;
for (int coin : denominations) {
if (i - coin < 0) {
continue;
}
dp[i] = Math.min(dp[i], 1 + dp[i - coin]);
origins[i] = coin;
}
}
if (dp[change] == Integer.MAX_VALUE) {
return -1;
}
printPath(origins);
return dp[change];
}
/**
* HELPER FUNCTION - GET PATH
*
* #param origins Array with origins from main function
*/
private static void printPath(int[] origins) {
int last = origins[origins.length-1];
System.out.println(last);
origins = Arrays.copyOfRange(origins,0,origins.length-last);
if (origins.length-1 > 0){
printPath(origins);
}
}
I hardcoded the array of denominations in this example, but you should be able to remove it and pass another as argument. It is not the most efficient way, but might be helpful to people who are only just getting into dynamic programming, like I am. Cheers!
I have a homework to write a method that returns a random number between
1 and 54, excluding the numbers passed in the argument. The method header is
specified as follows:
public static int getRandom(int... numbers)
I can't use anything more advanced than Single-Dimensional Arrays.
my code is:
public class PE13RandomNumberChooserVer2 {
public static void main(String[] args) {
int[] excludeNumbers = {1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18};
int randomNumber = getRandom(excludeNumbers);
System.out.println();
System.out.println("Random number chosen: " + randomNumber);
}
public static int getRandom(int... excludeNumbers) {
int random = 1 + (int)(Math.random() * (54 - 1) + 1);
System.out.println("Numbers to exclude: ");
for (int i = 0; i < excludeNumbers.length; i++) {
System.out.print(excludeNumbers[i] + " ");
while (excludeNumbers[i] == random) {
random = 1 + (int)(Math.random() * 54);
System.out.println("\n(for test only) next random number: " + random);
}
}
return random;
}
}
a sample run showed that my logic is wrong:
(for test only) initial random number: 8
Numbers to exclude:
1 2 3 4 5 6 7 8
(for test only) next random number: 12
11 12
(for test only) next random number: 3
13 14 15 16 17 18
Random number chosen: 3
it checks only if random is equal to the current item in the array, it doesn't consider the case in which it can be equal to the previous item in the list that is already checked.
The end result for the random generated number should be a value different from the numbers in the array.
Any suggestions how to fix it are greatly appreciated.
The following will do it:
private final Random rand = new Random();
public int getRandom(int min, int max, int... excludeNumbers) {
int random = rand.nextInt(max - min + 1 - excludeNumbers.length) + min;
for (int exc : excludeNumbers) {
if (random >= exc) {
random++;
}
}
return random;
}
Observe how it only generates a single random number and doesn't require a rejection loop.
Note that both min and max are inclusive. Also note that excludeNumbers must appear in ascending order.
This
int random = 1 + (int)(Math.random() * (54 - 1) + 1);
and this
random = 1 + (int)(Math.random() * 54);
are strange and should coincide.
Latter one is correct.
Next your loops are wrong. Loops are for regenerating number in case it coincides with prohibited one. So you should place for inside while and place println outside all loops. for should serve to check all prohibited numbers for one generated and while should server as retriement loop.
Also you can use Random class.
THE CODE
public static void main(String[] args) {
int[] excludeNumbers = {1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18};
int randomNumber;
System.out.println("Numbers to exclude: ");
for (int i = 0; i < excludeNumbers.length; i++) {
System.out.print(excludeNumbers[i] + " ");
}
// 100 tests
for(int i=0; i<100; ++i ) {
randomNumber = getRandom(excludeNumbers);
System.out.println();
System.out.println("Random number chosen: " + randomNumber);
}
}
public static int getRandom(int... excludeNumbers) {
int random;
// regeneration loop
regeneration: while(true) {
// generating a number
random = 1 + (int)(Math.random() * 54);
// checking of it's correctness
for (int i = 0; i < excludeNumbers.length; i++) {
// checking if number conincides for prohibited
if( excludeNumbers[i] == random ) {
// if number concided, then going to regeneration
continue regeneration;
}
// here number has not coincided
// but with just one of prohibites
// no decision here
}
// here all prohibited numbers checked and
// no coincidences found
// so generated number is good
System.out.println("\n(for test only) next random number: " + random);
break regeneration;
}
return random;
}
Try this, which simply keeps trying until you get an acceptable number.
List<Integer> nums = Arrays.asList(excludedNumbers);
while (true) {
Random random = 1 + (int)(Math.random() * 54);
if (!nums.contains(random))
return random;
}
The method would be cleaner if you passed exclude numbers as a list.
The techniques that keep retrying until hitting an allowed number are crude and turn into a crying misery as the number of allowed numbers approaches 1. A much more elegant way is this:
create a boolean[54];
set each excluded element to true;
choose a random number r from a range as large as the number of allowed choices (54 - number of exclusions);
return the rth false element in the boolean array.
Note: this algorithm is the best fit when you can cache the boolean array; for your exact case (the function receives a new array every time) NPE's solution is superior.
Look into java.util.random, it has a method that provides a random integer between 0 and your specified number. I can't give you an example because I'm typing this from my phone at the moment, but if you were to get it between 1 and 54 I would get a random number between 0 and 53 and add 1 to the result.
I am also teaching myself Java right now. I spend several days on this problem already while there is no answer available yet online. The solution above is too advanced for me so far. Here is my solution finally, which employ basic array knowledge only
public class Q6_13 {
public static void main(String[] args) {
int[] excludeNumbers = {1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18};
System.out.println (getRandom(excludeNumbers));
}
public static int getRandom(int...numbers) {
int n = (int)(Math.random() * 54) + 1; ;
boolean newRandom = false;
boolean getNew = false;
while (getNew == false) {
for (int i = 0; (i < numbers.length) && newRandom == false; i++) {
if (n == numbers[i]) {
newRandom = true;
}
}
if (newRandom) {
n = (int)(Math.random() * 54) + 1;
getNew = false;
newRandom = false;
}
else
getNew = true;
}
return n;
}
}
public static int getRandom(int... numbers) {
final Random random = new Random();
final int[] arr1 = new int[54];
int count = 54;
for(int i = 0; i < arr1.length; ++i) {
arr1[i] = i + 1; // good luck doing this with foreach
}
for(int i = 0; i < numbers.length; ++i) {
final int n = numbers[i];
if(arr1[n] != 0) {
--count;
}
arr1[n] = 0;
}
final int[] arr2 = new int[count];
for(int i = 0, j = 0; i < arr2.length; ++i, ++j) {
if(arr1[j] == 0) {
++j;
}
else {
arr2[i] = arr1[j];
}
}
return arr2[random.nextInt(count)];
}
public class Compute {
public static void main(String[] args) {
int[] values = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41};
int x = 1+ (int) (Math.random()*54);
System.out.println("Value: "+getRandom(x,values));
}
public static int getRandom(int a, int... numbers){
System.out.println("x1: "+a);
for(int j=0;j<numbers.length;){
//System.out.println("Numbers: "+numbers[j]);
if(numbers[j]==a){
a = 1 + (int) (Math.random()*54);
System.out.println("x2: "+a);
j=0;
continue;
}
j++;
}
return a;
}
}
I need to generate an array int[] randomNumbers of random numbers with no duplicates. To do this, I make an array with all values that can go into randomNumbers, then use a random number generator to pick one out of the list, check if it's already in randomNumbers, and if it isn't, put it in randomNumbers.
(I want numbers between 1 and max, not 0 and max-1)
To be able to use Arrays.sort(int[]), the list needs to be sorted. So I use a third array, with the same values as randomNumbers called sortedNumbers, and sort it on every iteration:
public int[] uniqueRandom(int max, int numRequired) {
if (max < numRequired) {
numRequired = max;
}
int[] randomNumbers = new int[numRequired];
int[] sortedNumbers = new int[numRequired];
int[] sequentialNumbers = new int[max];
for (int i = 1; i < max; i++) {
sequentialNumbers[i] = i;
System.out.println(sequentialNumbers[i]);
}
int p = 0;
while (p < numRequired) {
int j = r.nextInt(max) + 1;
System.out.println("J:" + j);
if (Arrays.binarySearch(sortedNumbers, j) >= 0) {
System.out.println("Number Found:" + Arrays.binarySearch(randomNumbers, j));
} else {
randomNumbers[p] = j;
sortedNumbers[p] = j;
Arrays.sort(sortedNumbers);
for (int i = 0; i < randomNumbers.length; i++) {
System.out.println("rNum[" + i + "]:" + randomNumbers[i]);
}
System.out.println("\n");
for (int i = 0; i < randomNumbers.length; i++) {
System.out.println("sNum[" + i + "]:" + sortedNumbers[i]);
}
p++;
}
}
return randomNumbers;
}
My issue is that I'm getting an output where sortedNumbers is overwriting values. For uniqueRandom(5, 5) the output is:
J:2
rNum[0]:2
rNum[1]:0
rNum[2]:0
rNum[3]:0
rNum[4]:0
sNum[0]:0
sNum[1]:0
sNum[2]:0
sNum[3]:0
sNum[4]:2
J:2 // 2 already in the list, try again
J:2
J:4
rNum[0]:2
rNum[1]:4
rNum[2]:0
rNum[3]:0
rNum[4]:0
sNum[0]:0
sNum[1]:0
sNum[2]:0
sNum[3]:2
sNum[4]:4
J:5
rNum[0]:2
rNum[1]:4
rNum[2]:5
rNum[3]:0
rNum[4]:0
sNum[0]:0
sNum[1]:0
sNum[2]:2
sNum[3]:4
sNum[4]:5
J:2
J:3
rNum[0]:2
rNum[1]:4
rNum[2]:5
rNum[3]:3
rNum[4]:0
sNum[0]:0 // Should be:
sNum[1]:0 // 2
sNum[2]:2 // 3
sNum[3]:3 // 4
sNum[4]:5 // 5
J:4
rNum[0]:2
rNum[1]:4
rNum[2]:5
rNum[3]:3
rNum[4]:4
sNum[0]:0
sNum[1]:0
sNum[2]:2
sNum[3]:3
sNum[4]:4
So you can see the issue. I'm using java 1.7, and have no idea why it's doing this!
To solve your problem I would use a Set, that assure us to have unique results.
Below snipest will generate array with required number of unique integers.
Set<Integer> uniqueNumbers = new HashSet<Integer>();
Random r = new Random();
while(uniqueNumbers.size() < numRequired) {
uniqueNumbers.add(r.nextInt(maxRandom) + 1);
}
return uniqueNumbers.toArray(new Integer[0]);
You are putting the new number into both arrays using the same index. Your rNum array is filling from top down, but the sorted array is not: Each time you sort it, the new value moves down in the array and the zeros are always at the top. I think you could fix it by always putting the new number in the first position of the sorted array:
sortedNumbers[0] = j;
When you input J=5
the sortedNUm[] is
sNum[0]:0
sNum[1]:0
sNum[2]:2
sNum[3]:4
sNum[4]:5
next when you input J=3 (your p=3)
after
sortedNumbers[p] = j;
your sNUM[3] which is 4 is replaced by 3
hence after sorting it becomes
sNum[0]:0 // Should be:
sNum[1]:0 // 2
sNum[2]:2 // 3
sNum[3]:3 // 4
sNum[4]:5 // 5
notice 4 is not present
I suggest you initialize the array to -1 or 0 and add the variables at the start of array
like
sortedNumbers[0]=j;
and after Arrays.sort(); the first position will always be empty to add more numbers
While it doesn't answer the question, here is an alternative which is O(n) and work well provided max is not large.
public static void main(String[] args) {
System.out.println(Arrays.toString(uniqueRandom(20, 10)));
}
public static int[] uniqueRandom(int max, int numRequired) {
int[] possible = new int[max];
int[] ret = new int[numRequired];
for (int i = 0; i < max; i++)
possible[i] = i + 1;
Random r = new Random();
int numLeft = max;
for (int i = 0; i < numRequired; i++) {
int idx = r.nextInt(numLeft);
ret[i] = possible[idx];
if (idx < --numLeft)
possible[idx] = possible[numLeft];
}
return ret;
}
prints
[4, 10, 12, 19, 8, 3, 15, 1, 14, 7]
What I am trying to say is that perhaps you could make it simpler.
There are a few issues with your code:
since you only increment p when the new number j doesn't already exist in the arrays, that, combined with the fact that you sort the sortedArray first leads to the value actually being placed sometimes over an existing value (which shifted position due to the sort)
I don't understand what's the use of the sequentialNumbers array...
Here's an example which should work:
private static Random r = new Random();
public static void main(String[] args) {
System.out.println(Arrays.toString(uniqueRandom(10, 10)));
}
public static int[] uniqueRandom(int max, int numRequired) {
if (max < numRequired) {
numRequired = max;
}
int[] randomNumbers = new int[numRequired];
int[] sortedNumbers = new int[numRequired];
Arrays.sort(sortedNumbers);
int p = 0;
while (p < numRequired) {
int j = r.nextInt(max) + 1;
if(Arrays.binarySearch(sortedNumbers, j)<0) {
randomNumbers[p] = j;
System.arraycopy(randomNumbers, 0, sortedNumbers, 0, randomNumbers.length);
Arrays.sort(sortedNumbers);
p++;
}
}
return randomNumbers;
}
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
}