我是编程新手,所以如果我没有正确地提出这个问题,我很抱歉。
我有以下代码:
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 * a3 == sum) {
System.out.println(i + "," + j + "," + k);
}
}
}
}
它的基本功能是告诉我a1
,a2
和a3
的不同组合等于上述总和(在本例中为100)。这工作正常,但我现在正试图将它应用于更大的数据集,我不知道如何在没有手动编程for循环或高级知道我将拥有多少变量的情况下(可能在10到6000之间) )。我基本上有一个sql查询从数组加载该数据。
Java或python(我正在学习两者)是否有办法自动创建嵌套的for
和if
循环?
非常感谢。
答案 0 :(得分:13)
递归。
这听起来像是你要解决的问题:
您当前的示例:20x 1 + 5x 2 + 10x 3 = 100
所以一般来说你做的是:A 1 x 1 + A 2 x 2 + .. 。+ A n x n = SUM
所以你传入一个常量数组{A 1 ,A 2 ,...,A n } 你要解决{x 1 ,x 2 ,...,x n }
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);
}
}
}
并打电话给你使用的例子:
findVariables(new int[] {20, 5, 20}, 100, new int[] {0,0,0}, 0, 0)
答案 1 :(得分:7)
虽然它可能无法扩展,但这是一个非常简单的暴力python解决方案,不需要递归:
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
此外,还有一些方法可以计算任意列表列表的笛卡尔积,而无需递归。 (lol
=列表清单)
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)]
如果您只是在学习python,那么您可能不熟悉yield
语句或zip
函数;在这种情况下,下面的代码将更清晰。
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
通过跳过i + j + k
大于sum
的值,您在代码中做了一件很聪明的事情。这些都没有。但是有可能修改后两个来做到这一点,但失去了一般性。
答案 2 :(得分:3)
使用Java,一些通用的简单实现至少需要两个类:
某些委托传递给递归算法,因此您可以在执行所在的任何位置接收更新。类似的东西:
public interface IDelegate {
public void found(List<CombinationFinder.FoundElement> nstack);
}
实施,例如:
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;
}
}
}
最后,设置并运行(测试):
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();
}
}
});
将输出36个解决方案。
使用此概念,您可以拥有任意数量的内部循环,甚至可以根据需要自定义每个循环。您甚至可以毫无问题地重用对象(即:a1.setNext(a1);
)。
** 更新 **
仅仅因为我喜欢monty的solution,我无法抗拒测试它,这是结果,稍微调整一下。
免责声明 所有积分都会转到monty以获取算法
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
}
}
答案 3 :(得分:2)
可能有一种方法可以将变量放入List并使用递归来消除所有循环。然而,这种强力方法的运行时间随着变量的数量呈指数增长。 (即算法可能无法在我们的生命周期内完成数千个变量的数量)。
有一些关于如何更有效地解决丢番图方程的文章。数论不是我的专业领域,但希望这些将有所帮助。
答案 4 :(得分:2)
基于@ monty的解决方案,但有一些调整。最后一个常数可以通过除法来确定。
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);
}
当我将int
更改为double
时,我不会在99%的情况下使用float
,因为您获得的舍入错误不值得您保存的内存。
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);
}
编译并运行良好。