how can I program a large number of for loops
I'm new to programming so I'm sorry in phrasing if I'm not asking this question correctly.
I have the following code:
int sum = 100;
int a1 = 20;
int a2 = 5;
int a3 = 10;
for (int i = 0; i * a1 <= sum; i++) {
for (int j = 0; i * a1 + j * a2 <= sum; j++) {
for (int k = 0; i * a1 + j * a2 + k * a3 <= sum; k++) {
if (i * a1 + j * a2 + k * 开发者_JS百科a3 == sum) {
System.out.println(i + "," + j + "," + k);
}
}
}
}
basically what it does is tell me the different combinations of a1
, a2
, and a3
that equal the above sum (in this case 100). This works fine but I'm trying to apply it to a larger dataset now and I'm not sure how to do without manually programming the for loops or knowing in advanced how many variables I'll have(could be anywhere from 10 to 6000). I basically have a sql query that loads that data from an array.
Is there a way in Java OR python (I'm learning both) to automatically create nested for
and if
loops?
Thanks so much in advance.
Recursion.
This is what it sounds like you are trying to solve:
your current example: 20x1 + 5x2 + 10x3 = 100
so in general you are doing: A1x1 + A2x2 + ... + Anxn = SUM
so you pass in an array of constants {A1, A2, ..., An} and you want to solve for {x1, x2, ..., xn}
public void findVariables(int[] constants, int sum,
int[] variables, int n, int result) {
if (n == constants.length) { //your end condition for the recursion
if (result == sum) {
printArrayAsList(variables);
}
} else if (result <= sum){ //keep going
for (int i = 0; result + constants[n]*i <= sum; i++) {
variables[n] = i;
findVariables(constants, sum, variables, n+1, result+constants[n]*i);
}
}
}
and to call for your example you'd use:
findVariables(new int[] {20, 5, 20}, 100, new int[] {0,0,0}, 0, 0)
Although it may not scale, here's a really simple brute-force python solution that doesn't require recursion:
import itertools
target_sum = 100
a = 20
b = 5
c = 10
a_range = range(0, target_sum + 1, a)
b_range = range(0, target_sum + 1, b)
c_range = range(0, target_sum + 1, c)
for i, j, k in itertools.product(a_range, b_range, c_range):
if i + j + k == 100:
print i, ',', j, ',', k
Also, there are ways to compute the cartesian product of an arbitrary list of lists without recursion. (lol
= list of lists)
def product_gen(*lol):
indices = [0] * len(lol)
index_limits = [len(l) - 1 for l in lol]
while indices < index_limits:
yield [l[i] for l, i in zip(lol, indices)]
for n, index in enumerate(indices):
index += 1
if index > index_limits[n]:
indices[n] = 0
else:
indices[n] = index
break
yield [l[i] for l, i in zip(lol, indices)]
If you're just learning python, then you might not be familiar with the yield
statement or the zip
function; in that case, the below code will be clearer.
def product(*lol):
indices = [0] * len(lol)
index_limits = [len(l) - 1 for l in lol]
index_accumulator = []
while indices < index_limits:
index_accumulator.append([lol[i][indices[i]] for i in range(len(lol))])
for n, index in enumerate(indices):
index += 1
if index > index_limits[n]:
indices[n] = 0
else:
indices[n] = index
break
index_accumulator.append([lol[i][indices[i]] for i in range(len(lol))])
return index_accumulator
You did a smart thing in your code by skipping those values for which i + j + k
is greater than sum
. None of these do that. But it's possible to modify the second two to do that, with some loss of generality.
With Java, some generic simplistic implementation would require at least two classes :
Some delegate to pass to the recursive algorithm, so you can receive updates wherever the execution is at. Something like :
public interface IDelegate {
public void found(List<CombinationFinder.FoundElement> nstack);
}
The for implementation, something like :
public class CombinationFinder {
private CombinationFinder next;
private int multiplier;
public CombinationFinder(int multiplier) {
this(multiplier, null);
}
public CombinationFinder(int multiplier, CombinationFinder next) {
this.multiplier = multiplier;
this.next = next;
}
public void setNext(CombinationFinder next) {
this.next = next;
}
public CombinationFinder getNext() {
return next;
}
public void search(int max, IDelegate d) {
Stack<FoundElement> stack = new Stack<FoundElement>();
this.search(0, max, stack, d);
}
private void search(int start, int max, Stack<FoundElement> s, IDelegate d) {
for (int i=0, val; (val = start + (i*multiplier)) <= max; i++) {
s.push(i);
if (null != next) {
next.search(val, max, s, d);
} else if (val == max) {
d.found(s);
}
s.pop();
}
}
static public class FoundElement {
private int value;
private int multiplier;
public FoundElement(int value, int multiplier) {
this.value = value;
this.multiplier = multiplier;
}
public int getValue() {
return value;
}
public int getMultiplier() {
return multiplier;
}
public String toString() {
return value+"*"+multiplier;
}
}
}
And finally, to setup and run (test) :
CombinationFinder a1 = new CombinationFinder(20);
CombinationFinder a2 = new CombinationFinder(5);
CombinationFinder a3 = new CombinationFinder(10);
a1.setNext(a2);
a2.setNext(a3);
a1.search(100, new IDelegate() {
int count = 1;
@Override
public void found(List<CombinationFinder.FoundElement> nstack) {
System.out.print("#" + (count++) + " Found : ");
for (int i=0; i<nstack.size(); i++) {
if (i>0) System.out.print(" + ");
System.out.print(nstack.get(i));
}
System.out.println();
}
}
});
Will output 36 solutions.
With this concept, you can have as many inner loops as you want, and even customize each one if you want through inheritance. You can even reuse objects (ie: a1.setNext(a1);
) with no problem at all.
** UPDATE **
Simply because I like monty's solution, I couldn't resist into testing it, and here's the result, tweaked a little.
DISCLAIMER all credits goes to monty for the algorithm
public class PolynomialSolver {
private SolverResult delegate;
private int min = 0;
private int max = Integer.MAX_VALUE;
public PolynomialSolver(SolverResult delegate) {
this.delegate = delegate;
}
public SolverResult getDelegate() {
return delegate;
}
public int getMax() {
return max;
}
public int getMin() {
return min;
}
public void setRange(int min, int max) {
this.min = min;
this.max = max;
}
public void solve(int[] constants, int total) {
solveImpl(constants, new int[constants.length], total, 0, 0);
}
private void solveImpl(int[] c, int[] v, int t, int n, int r) {
if (n == c.length) { //your end condition for the recursion
if (r == t) {
delegate.solution(c, v, t);
}
} else if (r <= t){ //keep going
for (int i=min, j; (i<=max) && ((j=r+c[n]*i)<=t); i++) {
v[n] = i;
solveImpl(c, v, t, n+1, j);
}
}
}
static public interface SolverResult {
public void solution(int[] constants, int[] variables, int total);
}
static public void main(String...args) {
PolynomialSolver solver = new PolynomialSolver(new SolverResult() {
int count = 1;
@Override
public void solution(int[] constants, int[] variables, int total) {
System.out.print("#"+(count++)+" Found : ");
for (int i=0, len=constants.length; i<len; i++) {
if (i>0) System.out.print(" + ");
System.out.print(constants[i]+"*"+variables[i]);
}
System.out.println(" = " + total);
}
});
// test some constants = total
solver.setRange(-10, 20);
solver.solve(new int[] {20, 5, 10}, 100); // will output 162 solutions
}
}
There may be a way to put the variables into a List and use recursion to eliminate all of the loops. However, the running time of this brute force approach grows exponentially with the number of variables. (i.e. the algorithm may not complete in our lifetimes for numbers of variables in the thousands).
There are some articles on how to solve Diophantine equations more efficiently. Number theory is not my area of expertise, but hopefully these will be of assistance.
http://www.wikihow.com/Solve-a-Linear-Diophantine-Equation
Based on @monty's solution but with a few tweaks. The last constant can be determined by division.
public static void findVariables(int[] constants, int sum) {
findVariables0(constants, sum, new int[constants.length], 0);
}
private static void findVariables0(int[] constants, int remaining, int[] variables, int n) {
if(n == constants.length - 1) {
// solution if the remaining is divisible by the last constant.
if (remaining % constants[n] == 0) {
variables[n] = remaining/constants[n];
System.out.println(Arrays.toString(variables));
}
} else {
for (int i = 0, limit = remaining/constants[n]; i <= limit; i++) {
variables[n] = i;
findVariables0(constants, remaining - i * constants[n], variables, n+1);
}
}
}
public static void main(String... args) {
findVariables(new int[] { 5,3,2 }, 100);
}
When I changed int
to double
I wouldn't use float
for 99% of cases due to the rounding error you get is not worth the memory you save.
public static void findVariables(double[] constants, double sum) {
findVariables0(constants, sum, new double[constants.length], 0);
}
private static void findVariables0(double[] constants, double remaining, double[] variables, int n) {
if(n == constants.length - 1) {
// solution if the remaining is divisible by the last constant.
if (remaining % constants[n] == 0) {
variables[n] = remaining/constants[n];
System.out.println(Arrays.toString(variables));
}
} else {
for (int i = 0, limit = (int) (remaining/constants[n]); i <= limit; i++) {
variables[n] = i;
findVariables0(constants, remaining - i * constants[n], variables, n+1);
}
}
}
public static void main(String... args) {
findVariables(new double[]{5.5, 3, 2}, 100);
}
This compiles and runs fine.
精彩评论