我有一系列操作和目标号码。
操作可能是
+ 3
- 3
* 4
/ 2
我想通过使用这些操作找出距离目标号码有多近。
我从0开始,我需要按顺序遍历操作,我可以选择使用该操作或不使用它。
因此,如果目标号码为13,我可以使用+ 3
和* 4
来获得12,这是我能够达到目标号码13的最接近的目标。
我想我需要计算所有可能的组合(我猜计算的数量因此是2 ^ n,其中n是操作的数量)。
我尝试在java中使用
执行此操作import java.util.*;
public class Instruction {
public static void main(String[] args) {
// create scanner
Scanner sc = new Scanner(System.in);
// number of instructions
int N = sc.nextInt();
// target number
int K = sc.nextInt();
//
String[] instructions = new String[N];
// N instructions follow
for (int i=0; i<N; i++) {
//
instructions[i] = sc.nextLine();
}
//
System.out.println(search(instructions, 0, N, 0, K, 0, K));
}
public static int search(String[] instructions, int index, int length, int progressSoFar, int targetNumber, int bestTarget, int bestDistance) {
//
for (int i=index; i<length; i++) {
// get operator
char operator = instructions[i].charAt(0);
// get number
int number = Integer.parseInt(instructions[i].split("\\s+")[1]);
//
if (operator == '+') {
progressSoFar += number;
} else if (operator == '*') {
progressSoFar *= number;
} else if (operator == '-') {
progressSoFar -= number;
} else if (operator == '/') {
progressSoFar /= number;
}
//
int distance = Math.abs(targetNumber - progressSoFar);
// if the absolute distance between progress so far
// and the target number is less than what we have
// previously accomplished, we update best distance
if (distance < bestDistance) {
bestTarget = progressSoFar;
bestDistance = distance;
}
//
if (true) {
return bestTarget;
} else {
return search(instructions, index + 1, length, progressSoFar, targetNumber, bestTarget, bestDistance);
}
}
}
}
它还没有用,但我想我更接近于解决我的问题。我只是不知道如何结束我的递归。
但也许我不使用递归,而应该只列出所有组合。我只是不知道该怎么做。
例如,如果我有3个操作并且我想计算所有组合,我得到2 ^ 3个组合
111
110
101
011
000
001
010
100
其中1表示使用了操作,0表示未使用该操作。
这样做应该相当简单,然后选择哪个组合得到最好的结果(最接近目标数的数字),但我不知道如何在java中这样做。
答案 0 :(得分:1)
在伪代码中,您可以尝试强力反向跟踪,如:
// ops: list of ops that have not yet been tried out
// target: goal result
// currentOps: list of ops used so far
// best: reference to the best result achieved so far (can be altered; use
// an int[1], for example)
// opsForBest: list of ops used to achieve best result so far
test(ops, target, currentOps, best, opsForBest)
if ops is now empty,
current = evaluate(currentOps)
if current is closer to target than best,
best = current
opsForBest = a copy of currentOps
otherwise,
// try including next op
with the next operator in ops,
test(opsAfterNext, target,
currentOps concatenated with next, best, opsForBest)
// try *not* including next op
test(opsAfterNext, target, currentOps, best, opsForBest)
保证找到最佳答案。但是,它会一次又一次地重复许多操作。您可以通过避免重复计算来节省一些时间,这可以使用“此子表达式如何评估”的缓存来实现。当您包含缓存时,您将进入“动态编程”领域(=在以后的计算中重用较早的结果)。
编辑:添加更多OO-ish变体
Variant返回最佳结果,并避免使用best[]
数组。需要使用包含字段Answer
和ops
的辅助类result
。
// ops: list of ops that have not yet been tried out
// target: goal result
// currentOps: list of ops used so far
Answer test(ops, target, currentOps, opsForBest)
if ops is now empty,
return new Answer(currentOps, evaluate(currentOps))
otherwise,
// try including next op
with the next operator in ops,
Answer withOp = test(opsAfterNext, target,
currentOps concatenated with next, best, opsForBest)
// try *not* including next op
Answer withoutOp = test(opsAfterNext, target,
currentOps, best, opsForBest)
if withOp.result closer to target than withoutOp.target,
return withOp
else
return withoutOp
答案 1 :(得分:0)
如果目标值为t,并且列表中有n个操作,并且通过组合它们的某些子序列可以创建的最大绝对值是k,并且所有值的乘积的绝对值显示为除法运算的操作数为d,然后有一个简单的 O(dkn)-time和-space dynamic programming算法,用于确定是否可以计算该值我使用了前j个操作的某个子集,并将此答案(单个位)存储在dp[i][j]
:
dp[i][j] = dp[i][j-1] || dp[invOp(i, j)][j-1]
其中invOp(i, j)
计算值i上第j个运算的倒数。请注意,如果第j个操作是乘以x,并且i不能被x整除,那么该操作被认为没有反转,并且术语dp[invOp(i, j)][j-1]
被视为评估为{{1} }。所有其他操作都有独特的反转。
为了避免浮点代码的精度损失问题,首先将原始目标值t以及所有操作数乘以加法和减法运算。这可以确保我们遇到的任何除法运算false
只会应用于已知可被x整除的值。我们基本上将使用1 / d的整数倍进行工作。
由于某些操作(即减法和除法)需要解决更高目标值的子问题,我们通常无法以自下而上的方式计算/ x
。相反,我们可以使用自上而下递归的记忆,从(缩放的)目标值t * d开始,并在每个方向上以1为步长向外工作。
我已在https://ideone.com/hU1Rpq用C ++实现了这一点。 &#34;有趣&#34;部分是dp[i][j]
;在此之前的函数只是管道来处理memoisation表。首先使用目标值指定stdin上的输入,然后是以空格分隔的操作列表,其中运算符紧接其操作数值之前,例如
canReach(i, j)
或
10 +8 +11 /2
第二个例子,它应该给出与第一个相同的答案(9.5),似乎是在ideone(和我的)内存限制附近,尽管可以通过使用10 +4000 +5500 /1000
而不是{{ 1}}和long long int
的2位表,而不是在每个条目上浪费一个完整的字节。
注意,通常,dk或者甚至只是k本身可以是输入大小的指数:例如如果有一个加法,接着是n-1个乘法运算,每个运算都涉及一个大于1的数。通过一个不同的DP来简单地查找可达的最大和最小数字,这并不太难以计算k。使用所有1&lt; = i&lt; = n的第一个i操作,但我们真正需要的是一个上限,并且它很容易得到一个(有点松散):简单地丢弃所有的符号乘法操作数,将所有int
操作转换为_m[][][]
操作,然后执行所有乘法和加法操作(即忽略除法)。
还可以应用其他优化措施,例如通过任何公共因子进行划分。
答案 2 :(得分:0)
这是一个使用memoization的Java 8示例。我想知道是否可以应用退火......
public class Tester {
public static interface Operation {
public int doOperation(int cur);
}
static Operation ops[] = { // lambdas for the opertions
(x -> x + 3),
(x -> x - 3),
(x -> x * 4),
(x -> x / 2),
};
private static int getTarget(){
return 2;
}
public static void main (String args[]){
int map[];
int val = 0;
int MAX_BITMASK = (1 << ops.length) - 1;//means ops.length < 31 [int overflow]
map = new int[MAX_BITMASK];
map[0] = val;
final int target = getTarget();// To get rid of dead code warning
int closest = val, delta = target < 0? -target: target;
int bestSeq = 0;
if (0 == target) {
System.out.println("Winning sequence: Do nothing");
}
int lastBitMask = 0, opIndex = 0;
int i = 0;
for (i = 1; i < MAX_BITMASK; i++){// brute force algo
val = map[i & lastBitMask]; // get prev memoized value
val = ops[opIndex].doOperation(val); // compute
map[i] = val; //add new memo
//the rest just logic to find the closest
// except the last part
int d = val - target;
d = d < 0? -d: d;
if (d < delta) {
bestSeq = i;
closest = val;
delta = d;
}
if (val == target){ // no point to continue
break;
}
//advance memo mask 0b001 to 0b011 to 0b111, etc.
// as well as the computing operation.
if ((i & (i + 1)) == 0){ // check for 2^n -1
lastBitMask = (lastBitMask << 1) + 1;
opIndex++;
}
}
System.out.println("Winning sequence: " + bestSeq);
System.out.println("Closest to \'" + target + "\' is: " + closest);
}
}
值得注意的是,“获胜序列”是OP使用的内容和非内容的位表示(显示为十进制),正如OP在问题中所做的那样。
对于那些来自Java 7的人来说,这就是我为lambdas引用的内容:Lambda Expressionsin GUI Applications。因此,如果你被限制在7,你仍然可以很容易地完成这项工作。