[英]Choose best combinations of operators to find target number
我有一系列操作和目標號碼。
這些行動可能是
+ 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中這樣做。
在偽代碼中,您可以嘗試強力反向跟蹤,如:
// 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變體
變量返回最佳結果,並避免使用best[]
數組。 需要使用輔助類使用字段ops
和result
Answer
。
// 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
如果目標值是t,並且列表中有n個操作,並且您可以通過組合它們的某些子序列來創建的最大絕對值是k,並且所有值的乘積的絕對值表示為a的操作數。除法操作是d,然后有一個簡單的O(dkn)時間和空間 動態編程算法,它確定是否可以使用前j個操作的某個子集計算值i並將該答案(單個位)存儲在dp[i][j]
:
dp[i][j] = dp[i][j-1] || dp[invOp(i, j)][j-1]
其中invOp(i, j)
計算值i invOp(i, j)
個運算的倒數。 注意,如果第j個操作是乘以x,並且i不能被x整除,那么該操作被認為沒有逆,並且術語dp[invOp(i, j)][j-1]
被視為評估為false
。 所有其他操作都有獨特的反轉。
為避免浮點代碼的精度損失問題,首先將原始目標值t以及所有操作數乘以加法和減法運算。 這確保了我們遇到的任何除法運算/ x
將僅被應用於已知可被x整除的值。 我們將基本上使用1 / d的整數倍進行工作。
因為一些操作(即減法和除法)需要求解更高目標值的子問題,所以我們通常不能以自下而上的方式計算dp[i][j]
。 相反,我們可以使用自上而下遞歸的記憶,從(縮放的)目標值t * d開始,並在每個方向上以1為步長向外工作。
我已經在https://ideone.com/hU1Rpq上用C ++實現了這一點。 “有趣”部分是canReach(i, j)
; 在此之前的函數只是管道來處理memoisation表。 首先使用目標值指定stdin上的輸入,然后是以空格分隔的操作列表,其中運算符緊接其操作數值之前,例如
10 +8 +11 /2
要么
10 +4000 +5500 /1000
第二個例子,它應該給出與第一個相同的答案(9.5),似乎是在ideone(和我的)內存限制附近,雖然這可以通過使用long long int
而不是int
和2位表來擴展。 for _m[][][]
而不是在每個條目上浪費一個完整的字節。
請注意,一般來說,dk甚至只是k本身可以是輸入大小的指數:例如,如果有一個加法,接着是n-1個乘法運算,每個運算都涉及一個大於1的數。它也不是很難通過一個不同的DP來精確計算k,它只是查找使用所有1 <= i <= n的第一個i操作可達到的最大和最小數字,但我們真正需要的只是一個上限,並且它很容易獲得一(有所松動)之一:簡單地放棄所有的乘法操作數的符號,將所有-
操作+
操作,然后執行所有的乘法和加法運算(即忽略師)。
還可以應用其他優化,例如通過任何公共因子進行划分。
這是一個使用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,你仍然可以很容易地完成這項工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.