I am trying, to sum up score for a little dice game I made. The dice has the following values: 1-6, the player can select target, LOW = n < 4, 1,2,3. The rest of the targets, 4, 5, 6, 7, 8, 9, 10, 11 and 12.
When a throw is done, sum up the total sum of the dices, based on target value. What this means, is, if LOW is select, then everything below 4, is summed up. Otherwise, the process, must sum each dice till it reaches the target sum, and continue.
If, a throw is done, and I selected 6 as target, and get the following set: {1, 1, 4, 2, 5, 6}. We have a 6, 5+1=6, and 4+2=6, we are left with 1 which is not counted.
Constraints:
1-6 dice values.
Everything (Target) below 4, is summed up.
Everything (Target) selecting between 4, 5, 6, 7, 8, 9, 10, 11, & 12, is processed differently.
6 dices, can produces any number between 1-6. This means, [6,6,6,6,6,6], or [1,1,3,5,4,2], or some other set.
The only thing important, is the sum that is calculated and nothing else, as long as it matches the input of dices.
For example:
If the target is 12 and a list of numbers is [6, 6, 6, 6, 6, 6] then return value should be 36.
If we receive a list of numbers [1, 3, 4, 5, 6, 6] and a target is should be 12 (5+1+6=12 and also 5+4+3=12, however, numbers can only be used once and not reused, therefore only one of the combinations can contribute the result).
Below is a method, which gives the occurrences of a dice.
public static TreeMap<Integer, Integer> countOccurrences(List<Integer> numbers){
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
int i = 0;
for(int n: numbers) {
if(map.containsKey(n)) {
i = map.get(n);
map.put(n, ++i);
}
else
map.put(n, 1);
}
return map;
}
Results:
Occurrences: {1=2, 2=1, 4=1, 5=1, 6=1}
Sample code
public static void main(String[] args) {
System.out.println(sum(combinationSum(List.of(1, 2, 4, 6, 6, 6), 12)));
System.out.println(combinationSum(List.of(1, 3, 3, 6, 5, 6), 12));
System.out.println(combinationSum(List.of(1, 2, 1, 4, 5, 6), 6));
}
public static int sum(List<List<Integer>> numbers)
{
int sum = 0;
int n = 0;
while(n < numbers.size()){
sum += numbers.get(n).stream().mapToInt(Integer::intValue).sum();
++n;
}
return sum;
}
public static List<List<Integer>> combinationSum(List<Integer> candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> ds = new ArrayList<Integer>();
findCombinations(res, ds, target, candidates, 0);
return res;
}
private static void findCombinations(List<List<Integer>> res, List<Integer> ds, int target, List<Integer> arr, int index ){
if(target == 0){res.add(new ArrayList<>(ds)); return;}
for(int i= index ; i<arr.size(); i++){
if(i>index && arr.get(i) == arr.get(i-1)) continue;
if(arr.get(i) > target) break;
ds.add(arr.get(i));
findCombinations( res, ds, target-arr.get(i) , arr, i+1);
ds.remove(ds.size()-1 );
}
}
Produces:
24
[[1, 6, 5], [1, 5, 6], [3, 3, 6], [3, 3, 6], [6, 6]]
[[1, 1, 4], [1, 5], [2, 4], [1, 5], [6]]
Live running: https://www.jdoodle.com/ia/sHY
Update
In order to find the maximum possible score of the list, we maximize the number of non-overlapping combinations that we can construct from it.
For we need to take the following steps:
Find all the possible combinations which produce the target sum. We can not reject any combinations on the fly, because we can't know in advance which of them can be used in the optimal solution. For instance, if target is 6 and a list of number is [3,3,2,2,1,1], there will be the following combinations: [3,3], [2,2,1,1] and [3,2,1] appear two times. If we pick [3,3], which is the shortest combination, we will not be able to construct any combinations and resulting score will be wrong (6). Instead, we need to choose two combinations [3,2,1] which well give the result 12. That proves that we can't rely on the combination size, we need to explore all the combinations and then choose the optimal set of combinations.
Generate all possible groups of combinations that fit to the given list of numbers and find a group having the maximum number of combinations in it.
In the code below both steps are implemented recursively.
A link to Online Demo
Expalanation:
The enty point, this method that kicks in the process.
public static int sumRecursively(List<Integer> numbers, int target) {
Deque<List<Integer>> combinations = new ArrayDeque<>();
generateCombinations(numbers, combinations, new ArrayList<>(), target);
return processCombinations(combinations, numbers, target);
}
Step 1. Generating all the combinations.
Recursive method responsible for generating the set of combinations (implemented as void to avoid wrapping a combination with an additional collection and then throwing this wrapper away):
private static void generateCombinations(List<Integer> numbers,
Queue<List<Integer>> combinations,
List<Integer> combination,
int currentSum) {
if (currentSum == 0) { // base case - a combination has been found
combinations.add(combination);
return;
}
// recursive case
for (Integer next : numbers) { // loop is need only to discard the elements that exceed the currentSum - pay attention to the condition below and break clause at the end of the loop (we will bread out of the loop after the first encountered element that fits to the current sum)
if (next > currentSum) continue;
List<Integer> newNumbers = new ArrayList<>(numbers);
newNumbers.remove(next);
// there two opportunities for each number: use it, or ignore it
// add next number
List<Integer> newCombination = new ArrayList<>(combination);
newCombination.add(next);
getCombinations(newNumbers, combinations, newCombination, currentSum - next);
// ignore next number
getCombinations(newNumbers, combinations, new ArrayList<>(combination), currentSum);
break;
}
}
Step 2. Generating the groups of combinations.
Method below is responsible choosing the group that fits to the given list (i.e. we can construct all the combinations in the group from list elements) and has the maximum number of combinations in it.
All the functionality related to the procissing the the group of combinations (which represents a List<List<Integer>>) is incapsulated in a class CombinationGroup to make the code cleaner.
public static int processCombinations(Deque<List<Integer>> combinations,
List<Integer> numbers,
int target) {
List<CombinationGroup> combinationGroups = new ArrayList<>();
generateGroups(combinationGroups, combinations, new CombinationGroup(numbers.size()));
return combinationGroups.stream()
.filter(group -> group.canConstruct(numbers))
.mapToInt(group -> group.getCombCount() * target)
.max()
.orElse(0);
}
The following method is responsible for creating the all possible groups of previosly descovered combinations. There's also a small optimization: total number of elements in each group should not exceed the number of elements in the source list:
public static void generateGroups(List<CombinationGroup> groups,
Deque<List<Integer>> combinations,
CombinationGroup group) {
if (combinations.isEmpty()) {
groups.add(group);
return;
}
Deque<List<Integer>> combinationsCopy = new ArrayDeque<>(combinations);
List<Integer> comb = null;
while (!combinationsCopy.isEmpty() && (comb == null || !group.canAdd(comb))) {
comb = combinationsCopy.removeLast();
}
// adding the combination
if (comb != null) {
CombinationGroup groupWithNewComb = group.copy();
groupWithNewComb.addCombination(comb);
generateGroups(groups, combinationsCopy, groupWithNewComb);
}
// ignoring the combination
generateGroups(groups, combinationsCopy, group);
}
Class CombinationGroup used in the methods above:
public class CombinationGroup {
private List<List<Integer>> combinations = new ArrayList<>();
private int combCount; // number of combinations
private int size; // total number of elements in the list of combinations
private int sizeLimit;
public CombinationGroup(int sizeLimit) {
this.sizeLimit = sizeLimit;
}
public boolean canAdd(List<Integer> combination) {
return size + combination.size() <= sizeLimit;
}
public boolean addCombination(List<Integer> combination) {
if (!canAdd(combination)) return false;
combinations.add(combination);
size += combination.size();
combCount++;
return true;
}
public CombinationGroup copy() {
CombinationGroup copy = new CombinationGroup(this.sizeLimit);
for (List<Integer> comb : combinations) {
copy.addCombination(comb);
}
return copy;
}
public boolean canConstruct(List<Integer> numbers) {
if (numbers.size() < size) return false;
Map<Integer, Long> frequencyByValueNumb = getFrequencies(numbers.stream());
Map<Integer, Long> frequencyByValueComb = getFrequencies();
return frequencyByValueNumb.keySet().stream() // each element that prent this CombinationGroup appears in the given list of numbers appears at least the same number of times - that means we construct all these combinations from the given list
.allMatch(key -> frequencyByValueNumb.get(key) >= frequencyByValueComb.getOrDefault(key, 0L));
}
public Map<Integer, Long> getFrequencies() {
return getFrequencies(combinations.stream().flatMap(List::stream));
}
public Map<Integer, Long> getFrequencies(Stream<Integer> stream) {
return stream.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
));
}
public int getCombCount() {
return combCount;
}
#Override
public String toString() {
return "CombinationGroup{" +
"combinations=" + combinations +
'}';
}
}
main()
public static void main(String[] args) {
System.out.println(sumRecursively(List.of(1, 3, 4, 5, 6, 6), 12));
System.out.println(sumRecursively(List.of(1, 3, 3, 6, 5), 12));
System.out.println(sumRecursively(List.of(1, 2, 1, 4, 5, 6), 6));
}
Output:
24
12
18
Simplified algorithm
(doesn't maximizes the number of combinations)
In order to ensure that all the elements in each combination are unique, we to track indices that have been already used. I.e. each time we find a combination which sums up to the target number, we should prohibit the usage of elements that have been used in this combination, but not earlier (because there can be many combinations which are not able to produce the target, and therefore any element should eligible to be used until we didn't construct a complete combination that gives the target using this element).
To track the elements that are taken, we need an object that would be visible in every recursive branch. And we are already passing a list of numbers while making every recursive call, what if we would modify it each time we found a combination that produces the target number by removing the elements that have been use in this combination? If we took this road after the first combination, thing would become complicated because we will not be able to rely on the indices (because they can change in an unpredictable way) while constructing a single combination - it's crucial to ensure that each element that belongs to a particular combination is used only once withing a combination. Since values of elements might be identical, we should use the iteration order to construct each combination properly, but each removal of elements would create a mess. So, is there a better way?
We can maintain an array of boolean values, each element is this array would indicate whether a number at a particular index already belongs to a combination that gives the target or not.
Instead of clattering the recursive method with the code that manipulates with this boolean array, I've encapsulated it within a class with simple and self-explanatory methods, and sumRecursively() makes use of an instance of this class.
public class CombinationTracker {
private boolean[] isUsed;
public CombinationTracker(int size) {
this.isUsed = new boolean[size];
}
public boolean indexIsUsed(int ind) {
return isUsed[ind];
}
public boolean allNotUsed(List<Integer> indices) {
// return indices.stream().noneMatch(i -> isUsed[i]); // this line does the same as the loop below
boolean result = true;
for (int idx: indices) {
if (isUsed[idx]) {
result = false;
break;
}
}
return result;
}
public void setIsUsed(List<Integer> indices) {
for (int ind: indices)
setIsUsed(ind);
}
public void setIsUsed(int ind) {
isUsed[ind] = true;
}
}
Using this approach, we are able to construct combinations from numbers that are not used yet, and iterate over the list of numbers starting from a particular position by passing the index while making a recursive call. We can be sure that any of the elements that reside prier to the current position would not be added to the current combination.
Now, a quick recap on recursion.
Every recursive implementation consists of two parts:
Base case - that represents an edge-case (or a set of edge-cases) for which the outcome is known in advance. For this problem, there are two edge-cases:
we've managed to find a combination that gives the target number, i.e. currentSum == target, and the result would be equal to target;
the end of the list is reached (and the combination doesn't result to the target), the result would be 0 (this edge-case resolves automatically by termination condition of the for loop in the recursive case, and therefore no need to treat it separately).
Recursive case - a part of a solution where recursive calls are made and where the main logic resides. In the recursive case we're iterating over the list of numbers and at each iteration step (if the index is not yet used) we are making one or two recursive calls depending on a value of the current element (depending whether we exceed the target or not). In the general, we have two opportunities: either take the current element, or ignore it. The results of these recursive calls would be added together and produce the return value of the recursive case.
Since we need a couple of additional parameters, it's a good practice to create an auxiliary overloaded method (that will be used in the client code) which expects only a list of numbers and a target value and delegates the actual work to the recursive method.
That's how it might look like.
public static int sumRecursively(List<Integer> numbers, int target) {
return sumRecursively(new ArrayList<>(numbers),
new ArrayList<>(),
new CombinationTracker(numbers.size()),
0, 0, target);
}
The actual recursive method:
private static int sumRecursively(List<Integer> numbers,
List<Integer> combination,
CombinationTracker tracker,
int currentIndex,
int currentSum, int target) {
if (currentSum == target && tracker.allNotUsed(combination)) { // base case - a combination has been found
tracker.setIsUsed(combination);
return target;
}
// recursive case
int result = 0;
for (int i = currentIndex; i < numbers.size(); i++) {
int next = numbers.get(i);
if (tracker.indexIsUsed(i)) continue;
if (currentSum + next > target) continue;
// there are two opportunities for each number: either use next number, or ignore it
// add next number
if (next + currentSum <= target) {
List<Integer> newCombination = new ArrayList<>(combination);
newCombination.add(i);
result += sumRecursively(numbers, newCombination, tracker, i + 1, currentSum + next, target);
}
// ignore next number
result += sumRecursively(numbers, new ArrayList<>(combination), tracker, i + 1, currentSum, target);
}
return result;
}
main()
public static void main(String[] args) {
System.out.println(sumRecursively(List.of(1, 3, 4, 5, 6, 6), 12));
System.out.println(sumRecursively(List.of(6, 6, 6, 6, 6, 6), 12));
}
Output:
12
36
UPD.
Got a comment that code "sucks" due to performance issues...
First of all, I do believe you have missed a point the SO community is not a kind of service which solves code interview puzzles, generally speaking, if you came here with a puzzle you already failed, so, such comments are unacceptable.
At second, yes, the code suffers from performance issues just because it is a naive bruteforce solution - I had spent on it about 15 minutes (for example, figuring out all possible combinations with target sum has O(2^N) complexity, if it does not match performance expectations that means any code based on such idea has poor performance). BTW, if you had expectations about the performance you was need to:
provide correct input constraints (saying there is 6 numbers is not correct)
provide good testcases instead of saying code does not work - that allows us to eliminate bad ideas about algorithm.
Some idea:
it seems that we do not need to compute all possible combinations with target sum, because singletons are always preferable over N-lets, pairs are either the same or do not influence on the result or interchangeable with N-lets (e.g. in case of [2,2,8,10,10] we would prefer to eliminate pairs first), but whether it is true for higher N's completely unclear - it is better to have some testcases.
Not sure I properly understand the problem, but I believe the solution is following:
public class TargetNumber {
public static void main(String[] args) {
System.out.println(score(new int[]{1, 2, 1, 4, 5, 6}, 6));
}
public static int score(int[] candidates, int target) {
List<List<Integer>> combinations = getUniqueCombinations(candidates, target);
Map<Integer, Integer> freqs = new HashMap<>();
for (int n : candidates) {
freqs.merge(n, 1, Integer::sum);
}
return score(0, combinations, freqs, target);
}
public static int score(int offset, List<List<Integer>> combinations, Map<Integer, Integer> freqs, int target) {
if (offset == combinations.size()) {
return 0;
}
int result = 0;
List<Integer> comb = combinations.get(offset);
Map<Integer, Integer> nfreq = reduce(freqs, comb);
if (nfreq != null) {
result = Math.max(result, target + score(offset, combinations, nfreq, target));
}
result = Math.max(result, score(offset + 1, combinations, freqs, target));
return result;
}
public static Map<Integer, Integer> reduce(Map<Integer, Integer> freqs, List<Integer> comb) {
Map<Integer, Integer> result = new HashMap<>(freqs);
for (int n : comb) {
if (result.merge(n, -1, Integer::sum) < 0) {
return null;
}
}
return result;
}
public static List<List<Integer>> getUniqueCombinations(int[] candidates, int target) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(candidates);
getUniqueCombinations(candidates, target, 0, new ArrayList<>(), result);
return result;
}
public static void getUniqueCombinations(int[] candidates, int target, int index, List<Integer> solution, List<List<Integer>> result) {
for (int i = index, n = candidates.length; i < n; ) {
int num = candidates[i];
if (num > target) {
break;
}
solution.add(num);
if (num == target) {
result.add(new ArrayList<>(solution));
}
getUniqueCombinations(candidates, target - num, i + 1, solution, result);
solution.remove(solution.size() - 1);
while (i < n && num == candidates[i]) {
i++;
}
}
}
}
Related
I was interviewing for one of the big techs where I was asked a programming question in the problem solving round. The question is very similar to the Two Sum problem in Leet Code except for one tricky constraint. The question goes like this :
Given an array of integers nums, an integer target and an integer limit, return exactly one set of elements that counts up to the given limit and adds up to the given target.
Input: nums = [2,7,11,15], target = 20, limit = 3
Output: [2, 7, 11]
Explanation : The target is 20 and the limit is 3, so, we will have to find 3 numbers from the array that add up to 20.
I wasn't able to solve this during the interview and have been searching for a solution ever since.
The brute force approach is to run as many loops as the limit, which is not viable, considering the fact that the limit may be <= 10,000
And another is to extract sub-arrays of length = limit, run through each and every one, add their elements and return a sub-array that adds up to Target.
But, I am sure there must be a more efficient approach to solve this.
Any ideas?
Edit :
The output that we return may be random and not necessarily contiguous.
The limit has to be met and the number of elements that we return must be equal to the limit.
There is no limit on the size of the array
Use Stack (recursively) to find the array elements which will sum to the desired target within the required array elements limit. Doing it this way will actually find all combinations but only those which use fall on the elements limit are placed into a List.
Please read the comments in code. Delete them later if you like. Here is a runnable to demonstrate this process:
package sumtargetlimit_demo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
public class SumTargetLimit_Demo {
// The desired Target Sum
private int targetSum = 20;
/* The max allowable array elements to use in order to acquire
the desired Target Sum. */
private int numbersLimit = 3;
// A Stack to hold the Array elements which sum to the desired Target Sum.
private Stack<Integer> stack = new Stack<>();
// Store the summation of current elements held in stack.
private int sumInStack = 0;
/* A List Interface of Integer[] array to hold all the
combinations of array elements which sum to target. */
private List<Integer[]> combinationsList = new ArrayList<>();
public static void main(String[] args) {
// Demo started this way to avoid the need for statics.
new SumTargetLimit_Demo().startDemo(args);
}
private void startDemo(String[] args) {
// The int array to work against.
int[] intData = {2, 7, 11, 15};
/* See which array elements can acquire the desired
Target Sum with the maximum number of array elements
specified in the numbersLimit member variable. */
getSummations(intData, 0, intData.length);
// Display the found results to Console window...
if (combinationsList.isEmpty()) {
System.err.println("No integer elements within the supplied Array will");
System.err.println("provide a Taget Sum of " + targetSum + " with a maximum number");
System.err.println("limit of " + numbersLimit + ".");
}
else {
for (Integer[] intArray : combinationsList) {
System.out.println(Arrays.toString(intArray).replaceAll("[\\[\\]]", ""));
}
}
}
// Note: This method is recursive...
public void getSummations(int[] data, int startIndex, int endIndex) {
/* Check to see if the sum of array elements stored in the
Stack is equal to the desired Target Sum. If it is then
convert the array elements in the Stack to an Integer[]
Array and add it to the conmbinationsList List. */
if (sumInStack == targetSum) {
if (stack.size() <= numbersLimit) {
combinationsList.add(stack.toArray(new Integer[stack.size()]));
}
}
for (int currIndex = startIndex; currIndex < endIndex; currIndex++) {
if (sumInStack + data[currIndex] <= targetSum) {
stack.push(data[currIndex]);
sumInStack += data[currIndex];
// Make the currentIndex +1, and then use recursion to carry on.
getSummations(data, currIndex + 1, endIndex);
sumInStack -= stack.pop();
}
}
}
}
Try a much larger int[] array and play with the Target Sum and Number Limit to see how things work.
Another way, to look at this problem is through the eyes of dynamic programming. For any element in the array, there are two cases:
It will be a part of the elements, which make up the sum, in that case, we recursively, find the elements that make the remaining sum, with limit - 1.
It will not be part of the elements, which make up the sum, in this case, we look for the target, in the remaining part of the array.
Here, is the sample following the above logic:
import java.util.*;
class HelloWorld {
static Map<Integer, List<Integer>> cache = new HashMap<>();
public static void main(String[] args) {
int[] array = {9, 2, 15, 11, 7, 23, 54, 50, 12};
int limit = 4;
int target = 35;
// This is to optimize the search for element in case the limit is 1
Arrays.sort(array);
List<Integer> subarray = getElementsWithSumEqualToTarget(array, 0, limit, target);
System.out.println(subarray);
}
static List<Integer> getElementsWithSumEqualToTarget(int[] array, int startingIndex, int limit, int target) {
// If limit is 0, or we have reached the end of the array then sum doesn't exists.
if(limit == 0 || startingIndex >= array.length) {
return null;
} else if(limit == 1) {
// For limit 1, we can do a simple binary search, or linear search in that case Arrays.sort can be removed
int index = Arrays.binarySearch(array, startingIndex, array.length - 1, target);
if(index < 0) {
return null;
}
ArrayList<Integer> list = new ArrayList();
list.add(target);
return list;
} else if (cache.containsKey(target)) {
// If for a given sum, the subarray of elements, is already present, we can return it from the cache.(Memoization)
return cache.get(target);
}
// Case 1: The current element will be part of the sum.
List<Integer> subarray = getElementsWithSumEqualToTarget(array, startingIndex + 1, limit - 1, target - array[startingIndex]);
if(subarray != null) {
subarray.add(array[startingIndex]);
// Add target and subarray to the cache
cache.put(target, subarray);
return subarray;
}
// Case 2: Current element is not part of the sum
subarray = getElementsWithSumEqualToTarget(array, startingIndex + 1, limit, target);
if(subarray != null) {
cache.put(target, subarray);
}
return subarray;
}
}
Please try it out on large datasets, and see how it works. Hopefully, it helps.
I have a weird homework that I have to write a program with a method that takes an array of non-negative integers (array elements can have repeated values) and a value sum as parameters. The method then prints out all the combinations of the elements in array whose sum is equal to sum. The weird part is, the teacher forces us to strictly follow the below structure:
public class Combinations {
public static void printCombinations(int[] arr, int sum) {
// Body of the method
}
public static void main(String[] args) {
// Create 2-3 arrays of integers and 2-3 sums here then call the above
// method with these arrays and sums to test the correctness of your method
}
}
We are not allow to add neither more methods nor more parameters for the current program. I have researched and understood several ways to do this recursively, but with this restriction, I don't really know how to do it. Therefore, I appreciate if you guys help me out.
EDIT: The array can have repeated elements. Here's an example run of the program.
arr = {1, 3, 2, 2, 25} and sum = 3
Outputs:
(1, 2) // 1st and 3rd element
(1, 2) // 1st and 4th element
(3) // 2nd element
As the printCombinations() method accepts the integer array as parameter and you are not allowed to add any additional methods. I couldn't think of Recursion without adding an additional method.
Here is a solution, let me know if this helps. And this is not the best way!
public static void main( String[] args ) throws Exception {
int arr[] = {1, 3, 2, 2, 25, 1, 1};
int sum = 8;
printCombinations(arr, sum);
}
public static void printCombinations(int arr[], int sum){
int count = 0;
int actualSum = sum;
while (count < arr.length) {
int j = 0;
int arrCollection[] = new int[arr.length];
for (int k = 0; k < arrCollection.length; k++){
arrCollection[k] = -99; // as the array can contain only +ve integers
}
for (int i = count; i < arr.length; i++) {
sum = sum - arr[i];
if (sum < 0){
sum = sum + arr[i];
} else if (sum > 0){
arrCollection[j++] = arr[i];
} else if (sum == 0){
System.out.println("");
arrCollection[j++] = arr[i];
int countElements = 0;
for (int k = 0; k < arrCollection.length; k++){
if (arrCollection[k] != -99) {
countElements++;
System.out.print(arrCollection[k] + " ");
}
}
if (countElements == 1){
i = arr.length -1;
}
sum = sum + arr[i];
j--;
}
}
count++;
sum = actualSum;
}
}
This is extremely suited for recursive algorithm.
Think about function, let's call it fillRemaining, that gets the current state of affairs in parameters. For example, usedItems would be a list that holds the items that were already used, availableItems would be a list that holds the items that haven't been tried, currentSum would be the sum of usedItems and goal would be the sum you are searching for.
Then, in each call of fillRemaining, you just have to walk over availableItems and check each one of them. If currentSum + item == goal, you have found a solution. If currentSum + item > goal, you skip the item because it's too large. If currentSum + item < goal, you add item to usedItems and remove it from availableItems, and call fillRemaining again. Of course, in this call currentSum should also be increased by item.
So in printCombinations, you initialize availableItems to contain all elements of arr, and usedItems to empty list. You set currentSum to 0 and goal to sum, and call fillRemaining. It should do the magic.
With the restriction of not being able to add any other methods or parameters, you can also make fields for availableItems, usedItems, currentSum and goal. This way, you don't have to pass them as parameters, but you can still use them. The fields will have to be static, and you would set them in main as described above.
If neither adding fields is allowed, then you have to somehow simulate nested loops with variable depth. In effect, this simulates what would otherwise be passed via stack, but the algorithm is still the same.
In effect, this algorithm would do a depth-first search of (pruned) tree of all possible combinations. Beware however, that there are 2^n combinations, so the time complexity is also O(2^n).
I think that all algorithms which can be solved with recursion can also be solved with stacks instead of recursion (see solution below). But very often it is easier to solve the problems with recursion before attempting the stack based solutions.
My recursive take on this problems would be in Java something like this:
public static void printCombinations(int[] array, int pos, int sum, int[] acc) {
if (Arrays.stream(acc).sum() == sum) {
System.out.println(Arrays.toString(acc));
}
for (int i = pos + 1; i < array.length; i++) {
int[] newAcc = new int[acc.length + 1];
System.arraycopy(acc, 0, newAcc, 0, acc.length);
newAcc[acc.length] = array[i];
printCombinations(array, i, sum, newAcc);
}
}
This function you can call like this:
printCombinations(new int[]{1, 3, 2, 2, 25}, -1, 3, new int[]{});
And it will print this:
[1, 2]
[1, 2]
[3]
Basically it goes through all possible sets in this array and then filters those out which have the sum of 3 in this case. It is not great, there are for sure better, more efficient ways to do this. But my point here is simply to show that you can convert this algorithm to a stack based implementation.
Here it goes how you can implement the same algorithm using stacks instead of recursion:
public static void printCombinationsStack(int[] array, int sum) {
Stack<Integer> stack = new Stack<>();
stack.push(0);
while (true) {
int i = stack.peek();
if (i == array.length - 1) {
stack.pop();
if (stack.isEmpty()) {
break;
}
int last = stack.pop();
stack.push(last + 1);
} else {
stack.push(i + 1);
}
if (stack.stream().map(e -> array[e]).mapToInt(Integer::intValue).sum() == sum) {
System.out.println(stack.stream().map(e -> Integer.toString(array[e]))
.collect(Collectors.joining(",")));
}
}
}
This method can be called like this:
printCombinationsStack(new int[]{1, 3, 2, 2, 25}, 3);
And it outputs also:
1,2
1,2
3
How I came to this conversion of a recursive to a stack based algorithm:
If you observe the positions in the acc array on the first algorithm above, then you will see a pattern which can be emulated by a stack. If you have an initial array with 4 elements, then the positions which are in the acc array are always these:
[]
[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 3]
[0, 2]
[0, 2, 3]
[0, 3]
[1]
[1, 2]
[1, 2, 3]
[1, 3]
[2]
[2, 3]
[3]
There is a pattern here which can easily be emulated with stacks:
The default operation is always to push into the stack, unless you reach the last position in the array. You push first 0 which is the first position in the array. When you reach the last position of the array, you pop once from the array and then pop again and a second popped item which you push back to the stack - incremented by one.
If the stack is empty you break the loop. You have gone through all possible combinations.
Seems duplicate, please go through below link for correct solution with exact code complexity details
find-a-pair-of-elements-from-an-array-whose-sum-equals-a-given-number
I am trying to use recursion to write a method subsetWithSum(ArrayList numbers, int sum), that that takes an arrayList of integers and an integer sum and returns an ArrayList which contains numbers from the given numbers(the provided ArrayList) that sum up to sum. It is not necessary to return more than one combination, and if there is no such subset, it should return null. But my code only returns null for each one.``
Here is my code for the method:
public static ArrayList<Integer> subsetWithSum(ArrayList<Integer> numbers, int sum){
ArrayList<Integer> sumList=new ArrayList<Integer>();
int sumForNumbers=0;
for (int i=0; i<=numbers.size()-1; i++)
sumForNumbers+=numbers.get(i);
if (sumForNumbers==sum)
return numbers;
else if(sumForNumbers>sum || numbers.size()==0)
return null;
else {
for (int i=0; i<numbers.size();i++){
int n=numbers.get(i);
for (int currentIndex=i+1; currentIndex<numbers.size(); currentIndex++)
sumList.add(numbers.get(currentIndex));
for (int currentIndex=0; currentIndex<=numbers.size()-1;currentIndex++){
if ((sumForNumbers+numbers.get(currentIndex))<=sum){
sumList.add(numbers.get(currentIndex));
sumForNumbers+=numbers.get(currentIndex);
}
}
}
return subsetWithSum(sumList, sum);
}
}
and here is my call to the method in the main:
public static void main(String[] args) {
ArrayList<Integer> test = new ArrayList<Integer>();
test.add(3); test.add(11); test.add(1); test.add(5);
System.out.println("Available numbers: " +test);
for(int sum=16; sum<=19; sum++){
ArrayList<Integer> answer = subsetWithSum(test, sum);
System.out.println(sum+" can be made with: "+answer);
here is my present output:
Available numbers: [3, 11, 1, 5]`
16 can be made with: null
17 can be made with: null
18 can be made with: null
19 can be made with: null
my expected output is:
Available numbers: [3, 11, 1, 5]
16 can be made with: [11, 5]
17 can be made with: [11, 1, 5]
18 can be made with: null
19 can be made with: [3, 11, 5]
I find recursion really difficult to understand and any help would be great
Firstly, if you are using Java 8, summing a List<Integer> list is as simple as list.stream().mapToInt(n -> n).sum().
Secondly, recursion always takes a similar form:
func(context)
if context in simple form
return simple result
else
break context down into smaller pieces
call func on smaller pieces
In your case, it would look like
func(total, list)
if sum(list) == total
return list
else if list is not empty
get all solutions from func(total - first item, list without first item)
and func(total, list without first item)
There are a few tricky things to consider here:
How to handle returning both the list and whether it is a valid result
how to remove items and then add them back after the recursive call
Here's a sample solution with a test case.
public class ListSum {
public static void main(String[] args) {
subsetsThatSumTo(18, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)).forEach(System.out::println);
}
public static List<List<Integer>> subsetsThatSumTo(int total, List<Integer> list) {
List<List<Integer>> result = new ArrayList<>();
if (list.stream().mapToInt(n -> n).sum() == total) {
result.add(new ArrayList<>(list));
} else if (!list.isEmpty()) {
subsetsThatSumTo(total - list.get(0), list.subList(1, list.size())).forEach(result::add);
result.forEach(l -> l.add(0, list.get(0)));
subsetsThatSumTo(total, list.subList(1, list.size())).forEach(result::add);
}
return result;
}
}
If you just want to return the first result:
public class ListSum {
public static void main(String[] args) {
System.out.println(subsetThatSumTo(18, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)));
}
public static List<Integer> subsetThatSumTo(int total, List<Integer> list) {
if (list.stream().mapToInt(n -> n).sum() == total)
return new ArrayList<>(list);
if (list.isEmpty())
return null;
List<Integer> result = subsetThatSumTo(total - list.get(0), list.subList(1, list.size()));
if (result != null) {
result.add(0, list.get(0));
return result;
} else {
return subsetThatSumTo(total, list.subList(1, list.size()));
}
}
}
The first thing you should be aware of is that this problem is NP-Complete. This implies its running time is not polynomial. The best known algorithm is exponential. If you find a polynomial algorithm to solve it, and you can prove it correct, you win 1M$ ;)
Reference:
https://en.m.wikipedia.org/wiki/Subset_sum_problem
Now, in this case, you can apply the backtracking pattern:
https://en.m.wikipedia.org/wiki/Backtracking
Just translate the pseudocode into Java and implement the functions. You'll learn a lot about recursion if you study this pattern and practise using it for different problems.
I am attempting to sort an array of integers such that all even numbers precede odd numbers, without using any external libraries.
You may recognize this from: http://codingbat.com/prob/p105771
" Return an array that contains the exact same numbers as the given array, but rearranged so that all the even numbers come before all the odd numbers. Other than that, the numbers can be in any order. You may modify and return the given array, or make a new array. "
I have code that accomplishes this goal:
public int[] evenOdd(int[] nums) {
int c=0;
int c2=0;
int [] nums2=new int[nums.length];
for(int i=0;i<nums.length;i++)
{
if(nums[i]%2==0)
{
nums2[c]=nums[i];
c++;
}
else
{
nums2[nums.length-c2-1]=nums[i];
c2++;
}
}
return nums2;
}
I have also approached the problem by calling array.sort() and then inserting the numbers by index, incrementing by two, then inserting the remainder. This also works.
So, to make a long post short-is there a more elegant way to accomplish this goal in future?
Thanks!
Just a follow-up to my comment. Here is how you can do it in O(n) without extra-space:
public class Main {
public static void main(String[] args) {
evenOdd(new int[]{1, 2, 3, 4, 5, 6, 7});
evenOdd(new int[]{2, 3, 4, 5, 6, 7});
evenOdd(new int[]{1, 1, 1, 1, 1});
evenOdd(new int[]{2, 2, 2, 2});
}
public static void evenOdd(int[] a) {
int firstOdd = 0;
for (int i = 0; i < a.length; ++i) {
if (a[i] % 2 == 0) {
int t = a[firstOdd];
a[firstOdd] = a[i];
a[i] = t;
firstOdd++;
// } else {
// else is redundant, just leave odd in-place
}
}
System.out.println(Arrays.toString(a));
}
}
Create a wrapper object that contains an int, call it SortingWrapper. This object should implement Comparable, such that its natural sort order is based on the sort order of value%2. Then just sort the array with Arrays.sort(). Based on the compareTo() implementation, the evens will naturally (ie, according to their natural sort order) bubble one way, all the odds will bubble the other.
Alternatively, you could just uses Integers as your wrapper and pass a Comparator that does the same thing to Arrays.sort().
edit- here is the general idea. You might have to play around with the sort order, but I think this pretty much works. I suspect that this array will count as "mostly sorted" which would make performance tend more towards O(n) than log(n), based upon the javadoc for Arrays.sort().
public void sortOddsAndEvents(Integer[] input)
{
Arrays.sort(input, new Comparator<Integer>()
{
#Override
public int compare(Integer arg0, Integer arg1)
{
if (arg0.equals(arg1)) return 0;
else return Integer.compare(arg0.intValue()%2, arg1.intValue()%2);
}
});
}
public int[] evenOdd(int[] nums) {
int evenCounter = -1, oddCounter = nums.length;
int[] ordered = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
int current = (nums[i] % 2 == 0) ? ++evenCounter : --oddCounter;
ordered[current] = nums[i];
}
return ordered;
}
I have an array of random values, and a target value.
#!/bin/bash
objective='50'
declare -a values=(1 2 2 6 8 14.5 15 28.7 .. 42)
I need to find a way to extract any combination of numbers in the array 'values' that add up to 50
The array has duplicates, and floating point integers.
A solution set might look like:
50 = 42 + 8
50 = 42 + 6 + 2
Initially I started in bash with some nested for loops, however I'm quickly realizing that this will grow exponentially with my array length.
I took a couple of java classes in college, but I'm still inexperienced in programming. I'm starting to think this may require recursion.
Can anyone with more programming experience point me in the right direction?
Besides nested for loops, how else could you approach this problem?
Here is an algorithm that has time complexity O(M*N) whereas M is Target and N is total size of set. Use analogy with knapsack problem as follows :-
Knapsack capacity = Target
Items are elements in the set with weight & value same as itself
Calculate maximum profit using dynamic programming
maxprofit = Target then there is/are subset which sum up to target.
Retrace the solution.
Java Solution for the same :-
public class SubSetSum {
static int[][] costs;
public static void calSets(int target,int[] arr) {
costs = new int[arr.length][target+1];
for(int j=0;j<=target;j++) {
if(arr[0]<=j) {
costs[0][j] = arr[0];
}
}
for(int i=1;i<arr.length;i++) {
for(int j=0;j<=target;j++) {
costs[i][j] = costs[i-1][j];
if(arr[i]<=j) {
costs[i][j] = Math.max(costs[i][j],costs[i-1][j-arr[i]]+arr[i]);
}
}
}
System.out.println(costs[arr.length-1][target]);
if(costs[arr.length-1][target]==target) {
System.out.println("Sets :");
printSets(arr,arr.length-1,target,"");
}
else System.out.println("No such Set found");
}
public static void printSets(int[] arr,int n,int w,String result) {
if(w==0) {
System.out.println(result);
return;
}
if(n==0) {
System.out.println(result+","+arr[0]);
return;
}
if(costs[n-1][w]==costs[n][w]) {
printSets(arr,n-1,w,new String(result));
}
if(arr[n]<=w&&(costs[n-1][w-arr[n]]+arr[n])==costs[n][w]) {
printSets(arr,n-1,w-arr[n],result+","+arr[n]);
}
}
public static void main(String[] args) {
int[] arr = {1,2,3,8,9,7};
calSets(10,arr);
}
}
I would do it like this:
1.some init
const int N=array size;
int val[N]; // input data
bool flag[N]; // is val used ?
for (int i=0;i<N;i++) flag[i]=false;
sort val[] descending
2.create function bool find_sum(int s);
if it found solution returns true else false
set flag to true for all used values
{
for (int i=0;i<N;i++) // find first usable big number (could be faster with binary search or with tailing last used i index into recursion)
if ((val[i]<=s)&&(!flag[i]))
{
flag[i]=true; // flag it as used
if (val[i]==s) return true; // try to find reminder
if (find_sum(s-val[i])) return true; // if found return true
flag[i]=false; // else unflag val[i] and continue winth next number
}
return false; // if sum not found then return false
}
3.after find_sum(s) your sum consists of all val[i] where flag[i]!=false
[edit1] functional source tested even for your case [6,5,5] and sum=10 it is OK
//---------------------------------------------------------------------------
int find_sum(int *val,int *use,int N,int sum,bool init=true)
{
int i;
if (init)
{
for (i=0;i<N;i++) use[i]=0; // nothibg used yet
for (int e=1;e;) // bubble sort
for (e=0,i=1;i<N;i++)
if (val[i-1]<val[i])
{ e=val[i-1]; val[i-1]=val[i]; val[i]=e; e=1; }
}
for (i=0;i<N;i++) // find first usable big number (could be faster with binary search or with tailing last used i index into recursion)
if ((val[i]<=sum)&&(!use[i]))
{
use[i]=1; // val[i] is used
if (val[i]==sum) return 1; // try to find reminder
if (find_sum(val,use,N,sum-val[i],false)) return 1; // if found return true
use[i]=0; // else val[i] is unused and continue winth next number
}
return 0; // if sum not found then return false
}
//---------------------------------------------------------------------------
void main()
{
int in[]={6,5,5}; // input data
const int N=sizeof(in)/sizeof(int);
int ret,out[N];
if (find_sum(in,out,N,10))
for (int i=0;i<N;i++)
if (out[i])
{
cout << in[i] << " ";
}
}
//---------------------------------------------------------------------------
PS. in your question in the input array are also floating point values
so you have to change int val[],sum to float/double
and add some accuracy for sum comparison to work with floats
if (val[i]==sum) return 1;
change to
if (fabs(val[i]-sum)<1e-10) return 1;
or use any other accuracy instead of 1e-10
You can use recursion yes, you can break the array into sub-parts (I use List).
Start from 0th index of the Parent list and a blank list
Iterate over to subList your Parent from i+1 to the end and thereby increasing your working list from 0 to i
Check for the sum (calculated) equals your objective
Code:
static Integer[] array = { 1, 2, 2, 6, 8, 14, 15, 28, 30, 32, 12, 48, 6, 42 };
static int objective = 50;
public static void main(String args[]) {
add(new ArrayList<Integer>(Arrays.asList(array)),
new ArrayList<Integer>());
}
public static void add(List<Integer> digits, List<Integer> workingList) {
for (int i = 0; i < digits.size(); i++) {
// New sublist to store values from 0 to i
List<Integer> list = new ArrayList<Integer>(workingList);
list.add(digits.get(i));
// Here you call this recursively with your Parent list from i+1
// index and working list from 0 to i
add(digits.subList(i + 1, digits.size()), list);
}
int sum = 0;
for (int element : workingList) {
sum += element;
}
if (sum == objective) {
System.out.println(objective + " = "
+ Arrays.toString(workingList.toArray()));
}
}
Output:
50 = [1, 2, 2, 15, 30]
50 = [1, 2, 6, 8, 15, 12, 6]
50 = [1, 2, 6, 14, 15, 12]
50 = [1, 2, 14, 15, 12, 6]
50 = [1, 2, 15, 32]
50 = [1, 2, 6, 8, 15, 12, 6]
...