I am a little confuse about the dynamic programming solution for combination sum, that you are given a list of numbers and a target total, and you want to count how many ways you can sum up to this target sum. Numbers can be reused multiple times. I am confused about the inner loop and outer loop that whether they are interchangeable or not. Can some explain the difference between the following two, and in what case we would use one but not the other, or they are the same.
int [] counts = new int[total];
counts[0] = 1;
// (1)
for(int i = 0; i <= total; i++) {
for(int j = 0; j < nums.length; j++) {
if(i >= nums[j])
counts[i] += counts[i - nums[j]];
}
}
// (2)
for(int j = 0; j < nums.length; j++)
for(int i = nums[j]; i <= total; i++) {
counts[i] += counts[i - nums[j]];
}
}
The two versions are indeed different, yielding different results.
I'll use nums = {2, 3} for all examples below.
Version 1 finds the number of combinations with ordering of elements from nums whose sum is total. It does so by iterating through all "subtotals" and counting how many combinations have the right sum, but it doesn't keep track of the elements. For example, the count for 5 will be 2. This is the result of using the first element (with value 2) and finding 1 combination in nums[3] and another combination for the second element (value 3) with the 1 combination in nums[2]. You should pay attention that both combinations use a single 2 and a single 3, but they represent the 2 different ordered lists [2, 3] & [3, 2].
Version 2 on the other hand find the number of combinations without ordering of elements from nums whose sum is total. It does so by counting how many combinations have the right sum (fur each subtotal), but contrary to version 1, it "uses" each element completely before moving on to the next element thus avoiding different orderings of the same group. When counting subtotals with the first element (2), all counts will initially be 0 (except the 0 sum sentinel), and any even subtotal will get the new count of 1. When the next element used, it is as if it's coming after all 2's are already in the group, so, contrary to version 1, only [2, 3] is counted, and not [3, 2].
By the way, the order of elements in nums doesn't affect the results, as can be understood by the logic explained.
Dynamic programming works by filling out entries in a table assuming that previous entries in the table have been fully completed.
In this case, we have counts[i] is dependent on counts[i - nums[j]]; for every entry j in nums.
In this code snippet
// (1)
for(int i = 0; i < total; i++) {
for(int j = 0; j < nums.length; j++) {
if(i >= nums[j])
counts1[i] += counts1[i - nums[j]];
}
}
We fill the table in order from 0 to total in that order. This is the action of the outer loop. The inner loop goes through our different nums and updates the current entry in our table based on the previous values, which are all assumed to be completed.
Now look at this snippet
// (2)
for(int j = 0; j < nums.length; j++){
for(int i = nums[j]; i < total; i++) {
counts2[i] += counts2[i - nums[j]];
}
}
Here we are iterating through our list of different counts and updating our totals. This breaks the concept of dynamic programming. None of our entries can ever be assumed to be complete until we are completely finished with our table.
Are they the same? The answer is no they are not. The following code illustrates the fact:
public class dyn {
public static void main(String[] args) {
int total = 50;
int[] nums = new int[]{1, 5, 10};
int [] counts1 = new int[total];
int [] counts2 = new int[total];
counts1[0] = 1;
counts2[0] = 1;
// (1)
for(int i = 0; i < total; i++) {
for(int j = 0; j < nums.length; j++) {
if(i >= nums[j])
counts1[i] += counts1[i - nums[j]];
}
}
// (2)
for(int j = 0; j < nums.length; j++){
for(int i = nums[j]; i < total; i++) {
counts2[i] += counts2[i - nums[j]];
}
}
for(int k = 0; k < total; k++){
System.out.print(counts1[k] + ",");
}
System.out.println("");
for(int k = 0; k < total; k++){
System.out.print(counts2[k] + ",");
}
}
}
This will output 2 different lists.
They are different because we are updating our counts[i] with incomplete information from earlier in the table. counts[6] assumes you have the entry for counts[5] and counts[1], which in turn assume you have the entries for counts[4], counts[3], counts[2], and counts[0]. Thus, each entry is dependent on (in the worst case all of) the previous entries in the table.
Addendum:
Interesting (perhaps obvious) side-note:
The two methods produce the same list up until the smallest pairwise sum of entries in nums.
Why?
This is when the information from previous entries becomes incomplete (with respect to the first loop). That is, if we have int[] nums = new int[]{3, 6}, then counts[3+6] will not be computed correctly, because either
count[3] will not be right or count[6] will not align with the result obtained using the first loop, depending on which stage of the computation we have done yet.
In light of criticism of my previous answer, I thought I'd take a more mathematical approach.
As in #Amit 's answer, I will use nums = {2, 3} in examples.
Recurrence Relations
The first loop computes
S(n) = S(n-3) + S(n-2)
Or, more generally, for some set {x_1, x_2, x_3, ... ,x_k}:
S(n) = S(n- x_1) + S(n- x_2) + ... + S(n- x_k)
It should be clear that each S(n) is dependent on (possibly all) previous values, and so we must start on 0 and populate the table upwards to our desired total.
The second loop computes a recurrence S_2(n) with the following definitions:
S_1(n) = S_1(n-2)
S_2(n) = S_1(n) + S_2(n-3)
More generally, for some set {x_1, x_2, x_3, ... ,x_k}:
S_1(n) = S_1(n- x_1)
S_2(n) = S_1(n) + S_2(n- x_2)
...
S_k(n) = S_{k-1}(n) + S_k(n- x_k)
Each entry in this sequence is like those from the first loop; it is dependent on the previous entries. But unlike the first loop, it is also dependent on earlier sequences.
Put perhaps more concretely:
S_2 is dependent on not only (possibly all) previous entries of S_2, but also on previous entries of S_1.
Thus, when we want to compute the first recurrence, we begin at 0 and compute each entry, for each number in our nums.
When we want to compute the second recurrence, we compute each intermediate recurrence one at a time, each time storing the result in counts.
In Plain English
What do these two recurrences compute? As #Amit 's answer explains, they compute the number of combinations that sum to total, with and without preserving order. It's easy to see why, again using our example of nums = {2, 3}:
Note my use of the word list to denote something ordered, and the word set to denote something unordered.
I use append to mean adding to the former, and add to denote adding to the latter.
If you know
how many lists of numbers add to 2,
and how many add to 3,
and I ask you
how many add to 5?
You can append a 3 to every one of the former lists, and a 2 to every one of the latter lists.
Thus (how many add to 5) = (how many add to 3) + (how many add to 2)
Which is our first recurrence.
For the second recurrence,
If you know
how many sets of just 2's add to 5 (0)
how many sets of just 2's and 3's add to 2 (1)
You can just take all of the first number, and you can add a 3 to all the sets in the second number.
Note how "sets of just 2's" is a special case of "sets of just 2's and 3's". "sets of just 2's and 3's" depends on "sets of just 2's", just like in our recurrence!
Recursive functions written in java
The following recursive function computes the values for the first loop, with example values 3 and 2.
public static int r(int n){
if(n < 0)
return 0;
if(n == 0)
return 1;
return r(n-2) + r(n-3);
}
The following set of recursive functions computes the values for the second loop, with example values 3 and 2.
public static int r1(int n){
if(n < 0)
return 0;
if(n == 0)
return 1;
return r1(n-2);
}
public static int r2(int n){
if(n < 0){
return 0;
}
return r1(n) + r2(n-3);
}
I have checked them up to 10 and they appear to be correct.
Related
I need to find the missing number in an array in O(n^2) time. I can rearrenge the array, so it is in order, but I have a difficult time finding the missing number without running another for loop, but I can't do that.
Here is my code:
The missing number is 3 here.
public static void main(String[] args){
int ar []={0,1,6,2,5,7,4};
int n = ar.length;
int temp = 0;
int m = 0;
for(int i = 0; i<n;i++){
for(int j = 1; j<n;j++){
if(ar[j-1] > ar[j]){
temp = ar[j-1];
ar[j-1]=ar[j];
ar[j]=temp;
if(ar[j-1]!=j-1) m=j;
}
else ar[j]=ar[j];
if(ar[j-1]!=j-1) m=j;
}
}
System.out.println(m);
}
If the input array contains all the numbers between 0 and ar.length except one missing number (as in your {0,1,6,2,5,7,4} example), you can find the missing number in linear time.
Just calculate the sum of all the numbers of the full array (the one including the missing number) - that array has ar.length + 1 elements from 0 to ar.length, so its sum is (ar.length - 0)*(ar.length + 1)/2 - and subtract from it the actual sum (which you can compute in a simple linear time loop). The difference would be the missing number.
If there are multiple missing numbers (or there may be duplicate numbers), and you need to find the first missing number, sorting the array in O(n*log(n) time and then making a single (linear time) pass over the sorted array would still be better than an O(n^2) solution.
If you can only do 2 loops, and must do 2 loops for O(n^2), then I suggest the following:
Loop thru all values (outer loop).
For each value, loop thru all values (inner loop) and find smallest value higher than current value.
If no higher value found, skip (current value is highest value)
If smallest value found is not current value + 1, then you found the missing value
How about this?
int array[] = {0,1,6,2,5,7,4};
int arrayTemp[] = array;
ArrayList<Integer> missing = new ArrayList();
Arrays.sort(arrayTemp);
for (int i=1; i<arrayTemp.length; i++) {
for (int j=arrayTemp[i-1]+1; j<arrayTemp[i]; j++) {
missing.add(j);
}
}
for (int i=arrayTemp[arrayTemp.length-1]+1; i<=arrayTemp[arrayTemp.length-1]; i++) {
missing.add(i);
}
System.out.println(missing.toString());
Output:
{0,1,6,2,5,7,4} -> [3]
{0,1,6,2,5,7,4,14} -> [3, 8, 9, 10, 11, 12, 13]
I have an ArrayList of int.
The main program - calls a method to get a list of the sum of all (n member) combination of the members of the list. Where n can be anywhere between 2 - 6. E.g. Original List is {1,2,3,4,5}; Then the output should be {6, 7, 8, 8, 9, 10, 9, 10, 11, 12} where n = 3;
I am looking for the optimum way to do this. Right now, the way I have written the program (which is working) is without recursion. I have methods for all numbers i.e.
MyMethod2 -- gives me all the sum of 2 member combinations of MyArrayList
MyMethod3 -- gives me all the sum of 3 member combinations of MyArrayList
MyMethod4 -- gives me all the sum of 4 member combinations of MyArrayList
......
So, you can see that there is a lot of duplicate set of codes.
Also the way the program has currently been written (e.g. My Method3):
MyMethod3
ArrayList<Integer> sum = new ArrayList<Integer>();
for (i = 0; i < MyArrayList.size(); i++){
for (j = i + 1; j < MyArrayList.size(); j++){
for (k = j + 1; k < MyArrayList.size(); k++){
int total = MyArrayList.get(i) + MyArrayList.get(j) + MyArrayList.get(k);
sum.add(total);
}
}
}
return sum;
The MyMethod for n = 6, can become pretty long. Also "n" can change in the future.
Is there a better way to do this using recursion to minimize duplicate code, and using the number n as a variablefor the method call. Is there a standard package in Java that can help with recursion.
Adding the Code based on #Maertin suggestion - which worked for me
ArrayList<Integer> myArray = new ArrayList<Integer>();
myArray.add(5);
myArray.add(6);
myArray.add(4);
myArray.add(2);
myArray.add(1);
ArrayList<Integer> finalSumArray = combineTwoArrayList(3, myArray, myArray);
public static ArrayList<Integer> combineTwoArrayList(int n, ArrayList<Integer> origArray, ArrayList<Integer> finalSumArray) {
if (n == 1) return finalSumArray;
ArrayList<Integer> finalSumArray = new ArrayList<Integer>();
for (int i = 0; i < sumArray.size() - 1; i++){
for (int j = i + 1; j < origArray.size(); j++){
finalSumArray.add(sumArray.get(i) + origArray.get(j));
}
}
--n;
return combineTwoArrayList(n, origArray, finalSumArray);
}
You are correct in wanting to do this via recursion, because now, instead of having three separate methods, you could have one method with a parameter n for n-member combinations.
public int nCombinationSum( int n, int i, ArrayList<Integer> arr, ArrayList<Integer> sumArr) {
/* Gets n-combination sums and puts into sumArr
Input: n consecutive element combination, current index i in arr, and output ArrayList
Output: Gets n consecutive element combinations in arr from index i to index (i + n) and puts into sumArr
*/
//**BASE CASE**
//if index out of arr bounds
if( i + n > arr.size() )
return 0;
//**RECURSIVE CASE**
else {
//sum of values in arr from i to (i + n)
int currComboSum = 0;
for( int j = 0; j < n; j++ )
currComboSum += arr.get(j);
//adding sum to next element in resultant array
sumArr.add( currComboSum );
return nCombinationSum( n, i + 1, arr, sumArr );
}
}
USAGE
In your main method, you can call nCombinationSum and provide it with the kind of combination (n), starting index (in your case, 0), and arrayList (arr), and the arrayList you want to append the sums in (sumArr).
This also has the potential added benefit of allowing you to add any n-combination sum starting from a certain index. If you would like, you could add an end index as well, but this is fairly extended as it is.
EDIT: Please edit your question to reflect that you want the result to be an arrayList of sums, rather than the total sum. It is not clear right now.
Basically, what you want to do with recursion, in general, is to set a base case and a recursive case.
Your base case would be if your index is out of bounds, because you're going to call all elements from index i to i + n.
For the recursive case, use the algorithm below to account for each element in arr, then just keep returning the function with the next index value, and the function will continue running until it is out of bounds.
Algorithm
Getting sum of n-combination elements
Appending that sum into resultant array sumArr
Feel free to refer to the code above for reference.
You can use recursion. Basically, you should have only two for loops. (which is the code for two member combinations). When you compute 'total', pass each 'total' value to an ArrayList MyArrayList2. Now for MyMethod3, you use the elements of MyArrayList2 and the original ArrayList and find new 'total' values again and pass that to MyArrayList3. For MyMethod4, you use the elements of MyArrayList3 and the original ArrayList and find new 'total' values again and pass that to MyArrayList4.... ....
I want to write a code in java that helps you determin whether the numbers can represent degrees of vertices in a graph. We were told in the algorithmic graph theory class that there's a simple way to solve that with an algorithm that works like this:
You put the numbers in descending order
3,3,2,1
Then you take the biggest one (in this case it's 3), 'erase it' (set it to zero in an array, I guess) and then from n-following numbers subtract 1 (n equals the value of this biggest number, in this case you subtract one from the following 3 numbers)
0,2,1,0
If needed, put the numbers in descending order once again and repeat the algorithm, erase the biggest number and subtract 1 from n-following numbers.
Do this until you either:
a) come across negative number(s), in which case it means that your sequence of numbers cannot represent degrees of vertices in a graph or
b) you are at the end of your algorithm and all the numbers are zeroes which means that these numbers can represent degrees of vertices in a graph
For these particular numbers it should go like this
3,3,2,1
0,2,1,0
0,0,0,-1
And as there's -1, it means that this sequence of numbers couldn't represent degrees of vertices in a graph.
So far my code for the algorithm and checking whether it is done looks like this (I already have the main method with scanner in it so the user can write down how many vertices and what degrees he wants to find out about, this is just where I don't know what to do next):
public void alg() {
int size = this.vertices.length;
do {
this.check();
for (int i = 0; i < size; i++) {
int biggest = i;
int max = this.vertices[biggest];
this.vertices[biggest] = 0;
for (int j = 0; j <= max + 1; j++) {
this.vertices[j+1]--;
System.out.println(Arrays.toString(this.vertices));
biggest++;
}
}
} while (this.finished != true);
}
public void check() {
Integer[] zeroes = new Integer[this.vertices.length];
Arrays.fill(zeroes, 0); //this probably isn't necessary
int count = 0;
for (int i = 0; i < this.vertices.length; i++) {
if (this.vertices[i] < 0) {
this.finished = true;
System.out.println("not a graph");
}
if(this.vertices[i] == 0) {
count++;
}
}
if (Arrays.equals(this.vertices, zeroes)) {
System.out.println("a graph");
}
}
So if the user's sequence of numbers is 3,3,2,1, the programme will print out only this and won't continue
[3, 3, 2, 1]
[0, 2, 2, 1]
[0, 2, 1, 1]
[0, 2, 1, 0]
I guess I'm missing something in the loops but I can't figure out what. Thanks for the help in advance!
The problem seems to be in your inner for loop. In your example it runs from j = 0 to j = 4.
Inside the loop you try to access this.vertices[j+1] which would be at the end of this loop the 5th entry of this array. You should get an exception by the Java Runtime here.
The second problem is, that you always start at the first index of you array.
Try changing this for loop to
for (int j = i+1; j <= max + i ; j++) {
this.vertices[j]--;
System.out.println(Arrays.toString(this.vertices));
biggest++;
}
You need to consider that even if max is 8 the array size could be 3, so you would get an IndexOutOfBoundsException using just max as a stop condition for the for j loop. What you need to use is min(max,size).
I'm writing a program for a class at school, and when the independents couldn't help, I turn to you...
I encounter my issue when I attempt to find the average - the variables either don't add correctly or they don't divide correctly. For example, an input of [4], [2], [4], [2], will give me 7.0, when it should be 3.0. Similarly, [2], [2], [4], [4], will give 2.0.
As far as I'm aware, the rest of the code functions exactly as it should. I'm including only what should effect it, but I can post the rest if required.
public class ArrayFunctions
{
String elementNumber =
JOptionPane.showInputDialog("How many elements do you want?");
int number = Integer.parseInt(elementNumber);
//assigns how many elements are in the array, based on user input
int[] min_array = new int[number];
int recalculate = 0;
public void arrayValues()
{
for (int i = 1; i < (number + 1); i++)
{
String elementInfo =
JOptionPane.showInputDialog("Input value for element " + i);
int element = Integer.parseInt(elementInfo);
//assigns values for elements, based on user input
min_array[(i - 1)] = element;
}
System.out.println('\u000C'); /*using BlueJ, this clears the console*/
for (int i = 1; i < (number + 1); i++)
{
System.out.println(min_array[(i - 1)]);
}
//prints the values of the elements in the array
}
...
public double avg()
{
for (int i = 1; i < (min_array.length); i++)
{
recalculate = (recalculate + min_array[(i - 1)]);
}
//should add together the values of all the elements
//this may be where it stops working as intended
double array_avg = (recalculate / min_array.length);
return array_avg;
//should divide the sum of all the elements by how many elements there are
//this is the other place where it might stop working.
}
Again, I can post more code if required. Sorry about bad/lacking comments and poor structure at times, I need to get this written, because I've a due date for this. :/
for (int i = 1; i < (min_array.length); i++)
{
recalculate = (recalculate + min_array[(i - 1)]);
}
This loop is going between index 0 (1 - 1) and index min_array.length - 2 due to your boolean condition in the for loop, stating that it should go while i is LESS than the array's length, and then also subtracting it by 1 in the code.
A possible solution would be to simply go until it's less than OR equal to the size, or simply start your loop at 0 and stop the (i - 1) stuff in the average calculation.
for (int i = 0; i < min_array.length; i++)
{
recalculate += min_array[i];
}
Also, on a side note, you're basically making that same mistake in the GUI stuff as well above; I've corrected it (as well as kept your methodology of using 1-based indexing for asking the user to fill in values, rather than 0-based indexing)
for (int i = 0; i < number; i++){
String elementInfo =
JOptionPane.showInputDialog("Input value for element " + (i + 1));
int element = Integer.parseInt(elementInfo);
min_array[i] = element;
}
System.out.println('\u000C'); /*using BlueJ, this clears the console*/
for (int i = 0; i < number; i++){
System.out.println(min_array[i]);
}
I see that you're going from index 0 to index array.length - 2, instead of -1. That's the problem. I hope this helps
public double avg()
{
for (int i = 0; i < (min_array.length); i++)
{
recalculate = (recalculate + min_array[i]);
}
//should add together the values of all the elements
//this may be where it stops working as intended
double array_avg = (recalculate / min_array.length);
return array_avg;
//should divide the sum of all the elements by how many elements there are
//this is the other place where it might stop working.
}
Also always start a for loop with i=0 for counting purposes
I asked this question before, but my post was cluttered with a whole bunch of other code and wasn't clearly presented, so I'm going to try again. Sorry, I'm new here
Shell sort, how I wrote it, only works sometimes. Array a is an array of 100 integers unsorted, inc is an array of 4 integers whose values are the intervals that shell sort should use (they descend and the final value is always 1), count is an array which stores the counts for different runs of shell sort, cnt represents the count value which should be updated for this run of shell sort.
When I run shell sort multiple times, with different sets of 4 intervals, only sometimes does the sort fully work. Half the time the array is fully sorted, the other half of the time the array is partially sorted.
Can anyone help? Thanks in advance!
public static void shellSort(int[] a, int[] inc, int[] count, int cnt) {
for (int k = 0; k < inc.length; k++) {
for (int i = inc[k], j; i < a.length; i += inc[k]) {
int tmp = a[i];
count[cnt] += 1;
for (j = i - inc[k]; j >= 0; j -= inc[k]) {
if (a[j] <= tmp)
break;
a[j + inc[k]] = a[j];
count[cnt] += 1;
}
a[j + inc[k]] = tmp;
count[cnt] += 1;
}
}
}
One problem is that you're only sorting one inc[k]-step sequence for each k, while you should sort them all (you're only sorting {a[0], a[s], a[2*s], ... , a[m*s]}, leaving out {a[1], a[s+1], ... , a[m*s+1]} etc.). However, that should only influence performance (number of operations), not the outcome, since the last pass is a classical insertion sort (inc[inc.length-1] == 1), so that should sort the array no matter what happened before.
I don't see anything in the code that would cause failure. Maybe the inc array doesn't contain what it should? If you print out inc[k] in each iteration of the outer loop, do you get the expected output?
There is an error in your i loop control:
for (int i = inc[k], j; i < a.length; i += inc[k]) {
Should be:
for (int i = inc[k], j; i < a.length; i++) {
The inner j loop handles the comparison of elements that are inc[k] apart. The outer i loop should simply increment by 1, the same as the outer loop of a standard Insertion sort.
In fact, the final pass of Shellsort with an increment of 1 is identical to a standard Insertion sort.