使用递归(即深度优先搜索)来编写它可能更容易,因为这将简化中间状态的簿记.
如果你想保持呼吸优先的方法,请确保状态列表支持有效删除第一个元素,即使用java.util.Queue,例如java.util.ArrayDeque.我提到这是因为最常用的List实现(即java.util. ArrayList)需要复制其整个内容以删除第一个元素,这使得如果列表很大,删除第一个元素非常昂贵.
120 states (with 5 numbers each). All of these have to undergo the same ritual, so it’s no wonder it’s taking so long.
实际上,它会非常令人惊讶.毕竟,2GHz CPU每秒执行20亿个时钟周期.即使检查状态需要多达100个时钟周期,这仍然意味着每秒2000万个状态!
另一方面,如果我正确理解游戏规则,那么候选解决方案的集合由6个数字的所有顺序给出(其中有6个!= 720),其中4个运算符中的一个在5个空格中之间,以及操作符的定义评估顺序.也就是说,我们共有6个! * 4 ^ 5 * 5! = 88 473 600候选解决方案,因此处理应在几秒钟内完成.
PS:完整的解决方案可能不会非常耗时,所以如果你愿意,我也可以发布代码 – 我只是不想破坏你的学习经验.
更新:我已经编写了代码.它比我想象的要难,因为找到所有解决方案的要求意味着我们需要在不展开堆栈的情况下打印解决方案.因此,我保留了每个州的历史.经过测试,我对性能不太满意(大约10秒),所以我添加了memoization,即每组数字只处理一次.随着运行时间下降到大约3秒.
由于Stackoverflow没有扰流标签,我增加了缩进,因此您必须向右滚动才能看到任何内容:-)
package katas.countdown;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
enum Operator {
plus("+", true),
minus("-", false),
multiply("*", true),
divide("/", false);
final String sign;
final boolean commutes;
Operator(String sign, boolean commutes) {
this.sign = sign;
this.commutes = commutes;
}
int apply(int left, int right) {
switch (this) {
case plus:
return left + right;
case minus:
return left - right;
case multiply:
return left * right;
case divide:
int mod = left % right;
if (mod == 0) {
return left / right;
} else {
throw new ArithmeticException();
}
}
throw new AssertionError(this);
}
@Override
public String toString() {
return sign;
}
}
class Expression implements Comparable {
final int value;
Expression(int value) {
this.value = value;
}
@Override
public int compareTo(Expression o) {
return value - o.value;
}
@Override
public int hashCode() {
return value;
}
@Override
public boolean equals(Object obj) {
return value == ((Expression) obj).value;
}
@Override
public String toString() {
return Integer.toString(value);
}
}
class OperationExpression extends Expression {
final Expression left;
final Operator operator;
final Expression right;
OperationExpression(Expression left, Operator operator, Expression right) {
super(operator.apply(left.value, right.value));
this.left = left;
this.operator = operator;
this.right = right;
}
@Override
public String toString() {
return "(" + left + " " + operator + " " + right + ")";
}
}
class State {
final Expression[] expressions;
State(int... numbers) {
expressions = new Expression[numbers.length];
for (int i = 0; i < numbers.length; i++) {
expressions[i] = new Expression(numbers[i]);
}
}
private State(Expression[] expressions) {
this.expressions = expressions;
}
/**
* @return a new state constructed by removing indices i and j, and adding expr instead
*/
State replace(int i, int j, Expression expr) {
Expression[] exprs = Arrays.copyOf(expressions, expressions.length - 1);
if (i < exprs.length) {
exprs[i] = expr;
if (j < exprs.length) {
exprs[j] = expressions[exprs.length];
}
} else {
exprs[j] = expr;
}
Arrays.sort(exprs);
return new State(exprs);
}
@Override
public boolean equals(Object obj) {
return Arrays.equals(expressions, ((State) obj).expressions);
}
public int hashCode() {
return Arrays.hashCode(expressions);
}
}
public class Solver {
final int goal;
Set visited = new HashSet<>();
public Solver(int goal) {
this.goal = goal;
}
public void solve(State s) {
if (s.expressions.length > 1 && !visited.contains(s)) {
visited.add(s);
for (int i = 0; i < s.expressions.length; i++) {
for (int j = 0; j < s.expressions.length; j++) {
if (i != j) {
Expression left = s.expressions[i];
Expression right = s.expressions[j];
for (Operator op : Operator.values()) {
if (op.commutes && i > j) {
// no need to evaluate the same branch twice
continue;
}
try {
Expression expr = new OperationExpression(left, op, right);
if (expr.value == goal) {
System.out.println(expr);
} else {
solve(s.replace(i, j, expr));
}
} catch (ArithmeticException e) {
continue;
}
}
}
}
}
}
}
public static void main(String[] args) {
new Solver(812).solve(new State(75, 50, 2, 3, 8, 7));
}
}
}
根据要求,每个解决方案仅报告一次(如果两个解决方案的中间结果集合相同,则认为两个解决方案相同).根据维基百科的描述,并非所有数字都需要使用.然而,还有一个小错误,因此可能不止一次报告此类解决方案.