I have a math problem that I solve by trial and error (I think this is called brute force), and the program works fine when there are a few options, but as I add more variab
The algorithm to solve this issue is closed to the process we learn for manual mathematical division or also to convert from decimal to another base like octal or hexadecimal - except that two examples only look for a single canonical solution.
To be sure the recursion ends, it is important to order the data array. To be efficient and limit the number of recursions, it is also important to start with higher data values.
Concretely, here is a Java recursive implementation for this problem - with a copy of the result vector coeff for each recursion as expected in theory.
import java.util.Arrays;
public class Solver
{
public static void main(String[] args)
{
int target_sum = 100;
// pre-requisite: sorted values !!
int[] data = new int[] { 5, 10, 20, 25, 40, 50 };
// result vector, init to 0
int[] coeff = new int[data.length];
Arrays.fill(coeff, 0);
partialSum(data.length - 1, target_sum, coeff, data);
}
private static void printResult(int[] coeff, int[] data) {
for (int i = coeff.length - 1; i >= 0; i--) {
if (coeff[i] > 0) {
System.out.print(data[i] + " * " + coeff[i] + " ");
}
}
System.out.println();
}
private static void partialSum(int k, int sum, int[] coeff, int[] data) {
int x_k = data[k];
for (int c = sum / x_k; c >= 0; c--) {
coeff[k] = c;
if (c * x_k == sum) {
printResult(coeff, data);
continue;
} else if (k > 0) {
// contextual result in parameters, local to method scope
int[] newcoeff = Arrays.copyOf(coeff, coeff.length);
partialSum(k - 1, sum - c * x_k, newcoeff, data);
// for loop on "c" goes on with previous coeff content
}
}
}
}
But now that code is in a special case: the last value test for each coeff is 0, so the copy is not necessary.
As a complexity estimation, we can use the maximum depth of recursive calls as data.length * min({ data }). For sure, it will not scale well and the limited factor is the stack trace memory (-Xss JVM option). The code may fail with a stack overflow error for a large data set.
To avoid this drawbacks, the "derecursion" process is useful. It consists in replacing the method call stack by a programmatic stack to store an execution context to process later. Here is the code for that:
import java.util.Arrays;
import java.util.ArrayDeque;
import java.util.Queue;
public class NonRecursive
{
// pre-requisite: sorted values !!
private static final int[] data = new int[] { 5, 10, 20, 25, 40, 50 };
// Context to store intermediate computation or a solution
static class Context {
int k;
int sum;
int[] coeff;
Context(int k, int sum, int[] coeff) {
this.k = k;
this.sum = sum;
this.coeff = coeff;
}
}
private static void printResult(int[] coeff) {
for (int i = coeff.length - 1; i >= 0; i--) {
if (coeff[i] > 0) {
System.out.print(data[i] + " * " + coeff[i] + " ");
}
}
System.out.println();
}
public static void main(String[] args)
{
int target_sum = 100;
// result vector, init to 0
int[] coeff = new int[data.length];
Arrays.fill(coeff, 0);
// queue with contexts to process
Queue contexts = new ArrayDeque();
// initial context
contexts.add(new Context(data.length - 1, target_sum, coeff));
while(!contexts.isEmpty()) {
Context current = contexts.poll();
int x_k = data[current.k];
for (int c = current.sum / x_k; c >= 0; c--) {
current.coeff[current.k] = c;
int[] newcoeff = Arrays.copyOf(current.coeff, current.coeff.length);
if (c * x_k == current.sum) {
printResult(newcoeff);
continue;
} else if (current.k > 0) {
contexts.add(new Context(current.k - 1, current.sum - c * x_k, newcoeff));
}
}
}
}
}
From my point of view, it is difficult to be more efficient in a single thread execution - the stack mechanism now requires coeff array copies.