In this problem I'm trying to simply take a list of items and a range and to find combinations that would allow all items to be used.
Here's an example: imagine you have 4 items (apple, pear, peach, and orange) and want a minimum of 20% of the basket to contain each and a maximum of 60%. For example, you could have 25%, 25%, 25%, 25% of each item or 30%, 30%, 20%, 20% and so on, but 0%, 0%, 50%, 50% does not work because the specified min% is 20%.
The program works fine, but it uses items less than the entire list (instead of 4 items in every solution, some solutions contain 2 or 3 items, which is not what I want). If I send a list of 4 items, I want the combinations using all 4 items togethers and nothing less. I do not want this because I plan to use large lists and I want the size to be items used to only be all and nothing less than the min%. Here's an example using the above information(4 items, 20-60% range):
good:
apples = 22
pears = 24
peach = 25
orange = 29
total: 100%
bad:
apples = 0
pears = 0
peach = 40
orange = 60
total: 100%
// Although total is correct, the example fails because
// the minimum of 20% per item was not obeyed.
I'm really confused as to why this is happening, but if I had to bet, I'd think it's the way my recursion is taking the number of items in the list and subtracting one before sending it back. Its in the method recursion_part
:
private static void recursion_part(int k, int sum, int[] coeff) {
//k is number of items in the list(in this example its 4(0-3), sum is the remaining total percent to break down, coeff is the template to store values
//this recursively takes the sum and tries to find lower values of it until it equals zero using the bounds given
for (int c = low_bound[k]; c <= high_bound[k]; c++) {
coeff[k] = c;
int[] newcoeff = Arrays.copyOf(coeff, coeff.length);
if (c - sum == 0) {
results.add(newcoeff);
printresults(newcoeff);
break;
} else if (k > 0) {
recursion_part(k - 1, sum - c, newcoeff);
}
}
}
I want to work on larger lists and I think it'll be a problem if it calculates a lot of results that I don't care for. How can I redesign this to only process all items in the list and stay within the range limits?
I thought of putting a method that checks how many zeros there are in the list and then breaks if it goes below the size of the list, but the fact that I'm getting blank results means its processing items less than my list and I'm thinking its better to design the program so it doesn't waste resources.
Here's the entire code (it works as described but is giving zero results as mentioned):
import java.util.ArrayList;
import java.util.Arrays;
public class recursion_percent_returner {
static final int target_percent = 100;
static final String[] names = new String[] {"apples", "pears", "peach", "orange" };
static int[] low_bound = new int[names.length];
static int[] high_bound = new int[names.length];
static ArrayList results = new ArrayList(); //queue to store results
static int[] default_coeff = new int[names.length];
public static void main(String[] args) {
System.out.println("starting..");
System.out.println("list size " + names.length);
Arrays.fill(low_bound, 20); //fills the min list with default value
Arrays.fill(high_bound, 60); //fills the max list with default value
recursion_part(names.length-1,target_percent,default_coeff);
System.out.println("total size of results are " + results.size());
}
private static void recursion_part(int k, int sum, int[] coeff) {
//k is number of items in the list(in this example its 4(0-3), sum is the remaining total percent to break down, coeff is the template to store values
//this recursively takes the sum and tries to find lower values of it until it equals zero using the bounds given
for (int c = low_bound[k]; c <= high_bound[k]; c++) {
coeff[k] = c;
int[] newcoeff = Arrays.copyOf(coeff, coeff.length);
if (c - sum == 0) {
results.add(newcoeff);
printresults(newcoeff);
break;
} else if (k > 0) {
recursion_part(k - 1, sum - c, newcoeff);
}
}
}
private static void printresults(int[] newcoeff) {
for (int x = 0; x<newcoeff.length; x++) {
System.out.println(names[x] + " = " + newcoeff[x]);
}
System.out.println("*********");
}
}
I am open to better ways to achieve the outcome I'm looking for.
p.s. this is not homework and I'm not a student; I just have a tendency to find weird sounding proxy problems.
I included the entire code but here's the output as well. It's a snippet of the 2653 solutions, which is generating more than what I need. If you look at it briefly, you'll see that most are correct, but as you get lower you'll see not all values are being used; I only want the solutions that are using all values; there should be no 0 value-entries.
import java.util.*;
public class Distributor {
private ArrayList<int[]> result = new ArrayList <int[]> ();
public Distributor (final String [] names, int [] low, int [] high)
{
final int rest = 10;
int minimum = 0;
for (int l : low)
minimum += l;
int [] sizes = new int [names.length];
distribute (0, low, high, rest - minimum, sizes);
System.out.println ("total size of results are " + result.size ());
for (int [] ia : result)
show (ia, low);
}
public static void main (String [] args) {
final String [] names = new String [] {"a", "b", "c"};
int [] low = new int [] {2, 2, 1};
int [] high = new int [] {3, 4, 6};
new Distributor (names, low, high);
}
/*
distribute the rest of values over the elements in sizes, beginning with index i.
*/
void distribute (int i, int [] low, int [] high, final int rest, int [] sizes) {
// System.out.println (i + " " + rest + " " + sizes);
if (i == sizes.length - 1) {
if (rest < high [i]) {
sizes[i] = rest;
result.add (Arrays.copyOf (sizes, sizes.length));
}
}
else
for (int c = 0;
c <= java.lang.Math.min (high [i] - low [i], rest);
++c) {
sizes [i] = c;
distribute (i + 1, low, high, rest - c, sizes);
}
}
private static void show (int [] arr, int [] low) {
for (int x = 0; x < arr.length; x++) {
System.out.print (" " + (arr [x] + low[x]));
}
System.out.println ();
}
}
Long variable names are better, if they are clearer than short ones. But they aren't more valuable per se.
More so: Stick to namingConventions, which are CamelCase in Java, not_funky_underlines.
Okay - why should low and high be static? I didn' work up im this direction for 'names' and 'result'. Remains as an exercise to remove the static modifier.
Okay - my impression is, that the sum needs always to be 100, not less, nor above 100. So if 4x20% is the minimum, a maximum of 60% isn't possible. But I understand that the values are variable, and in another settings, you might have 4x10% minimum, and then, a maximum of 60 % would make sense. I didn't test the code for that purpose.
But I substract the minimum (4*20%) from the rest to distribute, so you have to add these values for the final result.
The recursive distribute function starts with the termination condition: The last element is reached. Now the rest - how much it might be - has to be put for this last element.
Else, you take all possible values for this element, and distribute the rest.
I changed the code to take care of the upper limit, and simplified the example on the one side, since it now uses just 3 elements and a maximum of 10 instead of 100. The output shows the real values (min + variable part) and the algorithm only adds solutions which don't violate the upper bound constraint.
Sample output:
2 2 6
2 3 5
2 4 4
3 2 5
3 3 4
3 4 3
total size of results are 6
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With