Understanding this recursion function - java

I am altering/improving this recursive function. My intention is to add a global class variable nrOfFails to store all the iterations where the search was unsuccessful.
I call the function as follows:
{
ArrayList<Integer> solutions = new ArrayList<>();
int[] money1= {2,2,2,5,10,10,20}
int targe1 = 24
System.out.print(solutions(money1,target1,solutions))
}
/**
* Returns the number of ways of creating specified target value as a sum of money starting with c
* #param money the set of coins
* #param c Index of the array
* #param target the amount to give back
* #return number of ways
*/
private static int solutions(int[] money, int c, int target, ArrayList<Integer> s)
{
assert money!=null : "array should be initialized";
assert c>=0&&c<=money.length;
nrOfFails = 0;
if(target==0)
{
showSolution(s);
return 1;
}
if(target<0)
return 0;
if(c>=money.length)
return 0;
else
{
s.add(money[c]);
int with = solutions(money, c + 1, target - money[c], s);
s.remove(s.size()-1);
int without = solutions(money, c + 1, target,s);
return with + without;
}
}
private static void showSolution(ArrayList<Integer> s)
{
System.out.print(s);
}
I came up with a primitive way of 'counting' the unsuccessful iterations, but I would like to use recursion to solve this problem.
As for the primitive solution. I tried to check if at any iteration the content of money[] there was a value that didn't contain a multiple of the target quantity, then we searched in vain. Using a for, and a counter to check if there was or not a common multiple, if there wasn't any then we searched in vain.

Let's consider the "iterations where the search was unsuccessful" which you wish to count.
One such case is when you pass a negative target to the recursive call (which means target - money[c] < 0 in the solutions(money, c + 1, target - money[c], s) recursive call).
Another such case is when you run out of array elements before reaching the target sum (i.e. when c >= money.length).
Therefore you should increment your nrOfFails counter in these two cases. I unified them into a single condition, to make the code shorter:
static int nrOfFails = 0;
private static int solutions(int[] money, int c, int target, ArrayList<Integer> s)
{
assert money != null : "array should be initialized";
assert c >= 0 && c <= money.length;
if (target == 0) {
showSolution(s);
return 1;
} else if (target < 0 || c >= money.length) {
nrOfFails++;
return 0;
} else {
s.add(money[c]);
int with = solutions(money, c + 1, target - money[c], s);
s.remove(s.size() - 1);
int without = solutions(money, c + 1, target, s);
return with + without;
}
}
You have to reset the static variable to 0 prior to the first call to solutions.
Note that you forgot the c argument in your initial call to the recursive method. I added it here. I also added resetting and printing of nrOfFails:
nrOfFails = 0;
ArrayList<Integer> solutions = new ArrayList<>();
int[] money1= {2,2,2,5,10,10,20};
int target = 24;
System.out.println(solutions(money1,0,target,solutions));
System.out.println ("number of fails = " + nrOfFails);
This produces the following output:
[2, 2, 10, 10]
[2, 2, 20]
[2, 2, 10, 10]
[2, 2, 20]
[2, 2, 10, 10]
[2, 2, 20]
6
number of fails = 110

Related

Shortest subsequence length for a given sum Memoized solution giving wrong answer

The Bruteforce approach to find the shortest subsequence length for a given sum is giving the correct 2,2,9 outputs for the inputs given in the main method but when memoized getting wrong output 3,3,9. Can anyone help with this please? Thanks.
class ShortestSubsequenceWithSum {
public static int shortestSubsequenceWithSum(int[] num, int s, int cnt, Integer []dp) {
if(s == 0){
return cnt;
}
if(s < 0){
return Integer.MAX_VALUE;
}
if(dp[s] != null){
return dp[s];
}
int res = Integer.MAX_VALUE;
for(int i=0; i<num.length; i++){
int rem = s - num[i];
int ways = shortestSubsequenceWithSum(num, rem, cnt +1, dp);
res = Math.min(res, ways);
dp[s] = res;
}
// System.out.println("Returning value at # " + s);
return dp[s];
}
public static void main(String[] args) {
int[] num = {1, 1, 2, 3};
Integer dp[] = new Integer[5+1];
System.out.println(shortestSubsequenceWithSum(num, 5,0, dp));
num = new int[]{1, 2, 7, 1};
dp = new Integer[9+1];
System.out.println(shortestSubsequenceWithSum(num, 9,0, dp));
num = new int[]{1};
dp = new Integer[9+1];
System.out.println(shortestSubsequenceWithSum(num, 9,0, dp));
}
}
The problem here is that the way your recursive method works at present isn't amenable to memoization.
It looks like you are using your dp array to store the currently-known minimum count of numbers required to make a total. So dp[5] will be the minimum count of numbers required to make a total of 5. If dp[5] = 3 then you have found a way to make a total of 5 from three numbers, but not yet found a way to make 5 from fewer than three numbers.
Your method shortestSubsequenceWithSum presently returns the minimum count of numbers required to reach the total plus the number of recursive calls currently made. If you want to use memoization you will have to adjust this method to return the minimum count of numbers required to reach the total, regardless of how many levels of recursion there have so far been.
The changes you will need to make are:
In the handling for the case s == 0, return 0 rather than cnt. This represents being able to make a total of 0 from zero numbers.
Change the line dp[s] = res to dp[s] = res + 1. res contains the (minimum) count of numbers that make up s - num[i] for each value of i so far, so we add 1 for choosing num[i] in a combination that adds up to s.
These should be enough to get your code working. However, you can in fact move the line dp[s] = res + 1 immediately outside the for loop: we may as well wait for the final value of res before assigning it to dp[s]. You can also remove the parameter cnt from your method shortestSubsequenceWithSum, and all the calls to this method, as this parameter isn't being used any more.
This should leave you with the following:
/**
* Returns the minimum count of numbers from the given array that can
* make up a total. The same number can be chosen multiple times.
* #param num The numbers that can be chosen from.
* #param s The total to reach.
* #param dp An array that memoizes pre-computed values of this method.
* #return The minimum count of numbers from 'num' that totals 's'.
*/
public static int shortestSubsequenceWithSum(int[] num, int s, Integer []dp) {
if(s == 0){
return 0;
}
if(s < 0){
return Integer.MAX_VALUE;
}
if(dp[s] != null){
return dp[s];
}
int res = Integer.MAX_VALUE;
for(int i=0; i<num.length; i++){
int rem = s - num[i];
int ways = shortestSubsequenceWithSum(num, rem, dp);
res = Math.min(res, ways);
}
dp[s] = res + 1;
// System.out.println("Returning value at # " + s);
return dp[s];
}
Finally, I'll note that this code does not handle the case that there are no combinations that add up to the total. I'll leave fixing that as an exercise for you, see below for an example test case:
int[] num = {2, 4, 6};
Integer dp[] = new Integer[5+1];
System.out.println(shortestSubsequenceWithSum(num, 5, dp));

Pick from both sides?

The problem statement is :
Given an integer array A of size N.
You can pick B elements from either left or right end of the array A to get maximum sum.
Find and return this maximum possible sum.
NOTE: Suppose B = 4 and array A contains 10 elements then:
You can pick first four elements or can pick last four elements or can pick 1 from front and 3 from back etc . you need to return the maximum possible sum of elements you can pick.
public class Solution {
ArrayList<Integer> c = new ArrayList<>();
ArrayList<Integer> A= new ArrayList<>();
public int solve(ArrayList<Integer> A, int B) {
if (B>A.size()){
int sum=0;
for(int i=0;i<A.size();i++)
sum= sum+A.get(i);
return sum;
}
int max_sum=0;
for(int i=0;i<A.size();i++){
if((max_sum<suffix(A.size()-(B-i))+prefix(i-1)) ){
max_sum=suffix(A.size()-(B-i))+prefix(i-1);
}
}
return max_sum;
}
int prefix_sum=0;
int prefix(int a) {
for(int p=0;p<a+1;p++){
c=A;
prefix_sum=prefix_sum + c.get(p);
}
return prefix_sum;
}
int suffix_sum=0;
int suffix(int b){
c=A;
for(int q=b;q<c.size();q++){
suffix_sum=suffix_sum+c.get(q);
}
return suffix_sum;
}
}
I am getting runtime error, I have tried to implement the suffix and prefix methods which return the sum from the index[ 0, i] and sum from [i, N-i] respectively, then in the solve function I am trying to find the sum of prefix [a-1] +suffix[N-(b-a)] and find out the maximum sum, the syntax is completely correct, there is something wrong with the logic I assume, please help me find the correct solution by correcting this code instead of providing an alternative method
package com.array;
import java.util.Arrays;
import java.util.List;
public class PickFromBothSides {
public static void main(String[] args) {
Integer[] arr = { 5, -2, 3, 1, 2 };
System.out.println(solve(Arrays.asList(arr), 3));
}
public static int solve(List<Integer> A, int B) {
int n = A.size();
int result = 0;
for (int i = 0; i < B; i++) {
result += A.get(i);
}
int sum = result;
for (int i = 0; i < B; i++) {
sum -= A.get(B - 1 - i);
sum += A.get(n - 1 - i);
result = Math.max(result, sum);
}
return result;
}
}
Runtime O(n)
Space complexity O(1)
You are declaring int prefix_sum=0; and int suffix_sum=0; as fields, not as local variables of the respective methods.
You are calling suffix(A.size()-(B-i)) so with your example that is 10 - (4 -i) which is 6 + i. You iterate through i being in the range {0, ..., 10} so the value 6 + i will be all the numbers 6 through 16. You cannot index in the array above 9, so you get an exception.
You need to change
for(int i=0;i<A.size();i++){
to
for(int i=0; i <= B; i++){
because you are trying to ask each iteration "how many numbers are taken from the beginning"? 0, 1, 2, 3 or 4 if B is 4
Other upgrades:
You are calling suffix(A.size()-(B-i))+prefix(i-1)) twice in a row. Call it only once, store it in a variable and reuse.
You are calling prefix(i-1) but inside prefix() you are using the parameter a as a + 1. You don't need to subtract one and add one to the same thing

Array is ignoring last index for some number of array length

import java.util.Scanner;
public class Test {
public static void display(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.printf("%d\n", arr[i]);
}
System.out.printf("\n");
}
// this one is the problematic one.
public static int min(int[] arr, int start, int end) {
int middle = start + (end - start)/2;
if (start >= middle) {
return arr[start];
}
int min_1 = min(arr, start, middle);
int min_3 = (min_1 <= arr[middle])? min_1 : arr[middle];
int min_2 = min(arr, middle+1, end);
return (min_3 <= min_2)? min_3 : min_2;
}
public static void main(String[] args) {
int len;
Scanner in = new Scanner(System.in);
System.out.printf("Enter the length of array: ");
len = in.nextInt();
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = (int)(10*Math.random()+1);
}
arr[len-1] = 0;
display(arr);
System.out.printf("%d is the min", min(arr, 0, arr.length-1));
}
}
Problem is: the method min() works for the other places of minimum number (which is "0" in this example) for example if I put "0" in the 3rd place (by changing arr[len-1] = 0 part), method returns me 0. If I change the input length "len" to 3,7,13 or 14 it returns me 0, which is correct however for other lengths such as 4,5,8,9, it returns the second minimum number. And what is even more interesting is that if I make the end point "arr.length" instead of "arr.length-1", it won't give an error for some lengths (for instance for len = 6, it gives ArrayIndexOutOfBoundsException), moreover it works correctly and returns 0 in the situations where it doesn't give an error. I really don't understand what is the exact problem (Note: I must find the min number recursively and by finding the min of first half and second half and comparing them, so other solutions will not help me :/).
let's consider an execution passing 4, and let's say it generates arr = [6, 10, 2, 0]
The first invocation of min() will be passed 0, 3 as start,end. middle will be 0 + (3 - 0) / 2 = 1
so, you'll call
min_1 = min(arr, 0, 1)
in this execution min will have middle = 0 + (1 - 0) / 2 = 0, end hence it will return arr[0], i.e. 6.
We're back in the outer min(), where min_3 is computed as the min between (6 and 10, i.e. 6. Then we compute min_2 = min(arr, 2, 3)
we're now in this inner execution of min. Here middle = 2 + (3 - 2) / 2 = 2. As middle == start, we return arr[2], i.e. 2.
We're back in the outer min, where we finally compute the min between min_3 and min_1, which is 2, and return it. Note that we never processed arr[3]
The problem here is your condition to stop the recursion. The idea here is that you want to stop recursion when you've degenerated to a case where you're looking at an interva of size 1. The condition for that should not be start >= middle, but rather start >= end.

Algorithm to find the narrowest intervals, m of which will cover a set of numbers

Let's say you have a list of n numbers. You are allowed to choose m integers (lets call the integer a). For each integer a, delete every number that is within the inclusive range [a - x, a + x], where x is a number. What is the minimum value of x that can get the list cleared?
For example, if your list of numbers was
1 3 8 10 18 20 25
and m = 2, the answer would be x = 5.
You could pick the two integers 5 and 20. This would clear the list because it deletes every number in between [5-5, 5+5] and [20-5, 20+5].
How would I solve this? I think the solution may be related to dynamic programming. I do not want a brute force method solution.
Code would be really helpful, preferably in Java or C++ or C.
Hints
Suppose you had the list
1 3 8 10 18 20 25
and wanted to find how many groups would be needed to cover the set if x was equal to 2.
You could solve this in a greedy way by choosing the first integer to be 1+x (1 is the smallest number in the list). This would cover all elements up to 1+x+x=5. Then simply repeat this process until all numbers are covered.
So in this case, the next uncovered number is 8, so we would choose 8+x=10 and cover all numbers up to 10+x=12 in the second group.
Similarly, the third group would cover [18,24] and the fourth group would cover [25,29].
This value of x needed 4 groups. This is too many, so we need to increase x and try again.
You can use bisection to identify the smallest value of x that does cover all the numbers in m groups.
A recursive solution:
First, you need an estimation, you can split in m groups, then estimated(x) must be ~ (greather - lower element) / 2*m. the estimated(x) could be a solution. If there is a better solution, It has lower x than extimated(x) in all groups! and You can check it with the first group and then repeat recursively. The problem is decreasing until you have only a group: the last one, You know if your new solution is better or not, If there'is better, you can use it to discard another worse solution.
private static int estimate(int[] n, int m, int begin, int end) {
return (((n[end - 1] - n[begin]) / m) + 1 )/2;
}
private static int calculate(int[] n, int m, int begin, int end, int estimatedX){
if (m == 1){
return estimate(n, 1, begin, end);
} else {
int bestX = estimatedX;
for (int i = begin + 1; i <= end + 1 - m; i++) {
// It split the problem:
int firstGroupX = estimate(n, 1, begin, i);
if (firstGroupX < bestX){
bestX = Math.min(bestX, Math.max(firstGroupX, calculate(n, m-1, i, end, bestX)));
} else {
i = end;
}
}
return bestX;
}
}
public static void main(String[] args) {
int[] n = {1, 3, 8, 10, 18, 20, 25};
int m = 2;
Arrays.sort(n);
System.out.println(calculate(n, m, 0, n.length, estimate(n, m, 0, n.length)));
}
EDIT:
Long numbers version: Main idea, It search for "islands" of distances and split the problem into different islands. like divide and conquer, It distribute 'm' into islands.
private static long estimate(long[] n, long m, int begin, int end) {
return (((n[end - 1] - n[begin]) / m) + 1) / 2;
}
private static long calculate(long[] n, long m, int begin, int end, long estimatedX) {
if (m == 1) {
return estimate(n, 1, begin, end);
} else {
long bestX = estimatedX;
for (int i = begin + 1; i <= end + 1 - m; i++) {
long firstGroupX = estimate(n, 1, begin, i);
if (firstGroupX < bestX) {
bestX = Math.min(bestX, Math.max(firstGroupX, calculate(n, m - 1, i, end, bestX)));
} else {
i = end;
}
}
return bestX;
}
}
private static long solver(long[] n, long m, int begin, int end) {
long estimate = estimate(n, m, begin, end);
PriorityQueue<long[]> islands = new PriorityQueue<>((p0, p1) -> Long.compare(p1[0], p0[0]));
int islandBegin = begin;
for (int i = islandBegin; i < end -1; i++) {
if (n[i + 1] - n[i] > estimate) {
long estimatedIsland = estimate(n, 1, islandBegin, i+1);
islands.add(new long[]{estimatedIsland, islandBegin, i, 1});
islandBegin = i+1;
}
}
long estimatedIsland = estimate(n, 1, islandBegin, end);
islands.add(new long[]{estimatedIsland, islandBegin, end, 1});
long result;
if (islands.isEmpty() || m < islands.size()) {
result = calculate(n, m, begin, end, estimate);
} else {
long mFree = m - islands.size();
while (mFree > 0) {
long[] island = islands.poll();
island[3]++;
island[0] = solver(n, island[3], (int) island[1], (int) island[2]);
islands.add(island);
mFree--;
}
result = islands.poll()[0];
}
return result;
}
public static void main(String[] args) {
long[] n = new long[63];
for (int i = 1; i < n.length; i++) {
n[i] = 2*n[i-1]+1;
}
long m = 32;
Arrays.sort(n);
System.out.println(solver(n, m, 0, n.length));
}
An effective algorithm can be(assuming list is sorted) ->
We can think of list as groups of 'm' integers.
Now for each group calculate 'last_element - first_element+1', and store maximum of this value in a variable say, 'ans'.
Now the value of 'x' is 'ans/2'.
I hope its pretty clear how this algorithm works.
I think it's similarly problem of clusterization. For example You may use k-means clustering algorithm: do partitions of initial list on m classes and for x get maximum size divided by two of obtained classes.
1) You should look into BEST CASE, AVERAGE CASE and WORST CASE complexities with regards to TIME and SPACE complexities of algorithms.
2) I think David Pérez Cabrera has the right idea. Let's assume average case (as in the following pseudo code)
3) Let the list of integers be denoted by l
keepGoing = true
min_x = ceiling(l[size-1]-l[0])/(2m)
while(keepGoing)
{
l2 = l.copy
min_x = min_x-1
mcounter = 1
while(mcounter <= m)
{
firstElement = l2[0]
// This while condition will likely result in an ArrayOutOfBoundsException
// It's easy to fix this.
while(l2[0] <= firstElement+2*min_x)
{ remove(l2[0]) }
mcounter = mcounter+1
}
if(l2.size>0)
keepGoing = false
}
return min_x+1
4) Consider
l = {1, 2, 3, 4, 5, 6, 7}, m=2 (gives x=2)
l = {1, 10, 100, 1000, 10000, 100000, 1000000}, m=2
l = {1, 10, 100, 1000, 10000, 100000, 1000000}, m=3

Coin Exchange in java Code stackOverflowError

cannot find the problem, everytime when I'm running this code it goes stackoverflow due to this line
countCombine += count(array,money - (array[i]*(int)(money/array[i])));
basically problem is very easy.
Given a value N, if we want to make change for N cents, and we have infinite supply of each of S = { S1, S2, .. , Sm} valued coins, how many ways can we make the change? The order of coins doesn’t matter.
For example, for N = 4 and S = {1,2,3}, there are four solutions: {1,1,1,1},{1,1,2},{2,2},{1,3}. So output should be 4. For N = 10 and S = {2, 5, 3, 6}, there are five solutions: {2,2,2,2,2}, {2,2,3,3}, {2,2,6}, {2,3,5} and {5,5}. So the output should be 5.
public class CoinExchangeProblem {
int countCombine = 0;
private int count(int array[],int money){
// sort the array
// Arrays.sort(array);
// System.out.println(Arrays.toString(array));
if (money == 0) {
return 1;
} else if (money < 0) {
return 0;
}
for(int i = 0; i < array.length; i++){
countCombine += count(array,money - (array[i]*(int)(money/array[i])));
}
return countCombine;
}
public static void main(String[] args) {
CoinExchangeProblem coinExch = new CoinExchangeProblem();
System.out.println(coinExch.count(new int[]{1,2,3}, 4));
// System.out.println(coinExch.count(new int[]{2, 5, 3, 6}, 10));
}
}
When this part
(array[i]*(int)(money/array[i]))
equals zero your are a victim of infinite recursion where you are calling the function with the same amount of money
You can change it to :
if(money >= array[i])
countCombine += count(array,money - (array[i]*(int)(money/array[i])));
so you will never get a zero here, but test it for more examples as i didn't test it a lot , but I think that it is logically right

Categories

Resources