[英]BFS for arithmetic operations
用最少的操作將數字m轉換為n。 允許的操作是減1並乘以2。
例如:4和6.答案是2.第1次操作:-1 - > 4-1 = 3.第2次操作:* - > 3 * 2 = 6。
我正在使用BFS方法來處理特定的輸入(src = 26,dst = 5)這需要很長時間。 難道我做錯了什么?
from queue_class import queue
class node:
def __init__(self, value, level, parent):
self.level = level
self.value = value
self.parent = parent
def get_minimum_distance(src, target, q):
if src == target:
return 0
seen_list = []
data = node(src, 0, -1)
q.enqueue(data)
while not q.isempty():
data = q.dequeue()
if data == "sentinel":
break
if data.value == target:
# let's print what has got me here
while data.parent != -1:
print(data.value)
data = data.parent
return "finally reached"
if data.value in seen_list:
continue
seen_list.append(data.value)
# two operations are allowed i.e. -1 and multiplication by 2
# check if two numbers have opposite sign and if they have
# then check if the current number being subtracted from is a negative
# number. If it is, then there is no point subtracting 1 from that
if ((data.value ^ target) < 0 and data.value > 0) or (data.value ^ target >= 0):
q.enqueue(node(data.value - 1, data.level + 1, data))
q.enqueue(node(data.value * 2, data.level + 1, data))
return -1
q = queue(1 << 20)
print(get_minimum_distance(26, 5, q))
隊列實現在這里完成。
感謝Paul:下面是我在python下面的代碼中提出的代碼,它完美無缺。
def get_minimum_operations(src, dst):
step = 0
operations = []
if src == dst:
return 0
if dst < src:
return src-dst
while dst > src:
if dst & 0x01:
step += 1
operations.append("-1")
dst = (dst+1) >> 1
operations.append("*2")
step += 1
for i in range(0, src-dst):
operations.append("-1")
return (((src - dst) + step), operations)
src = 38
dst = 100
output = ""
(steps, operations) = get_minimum_operations(src, dst)
print(steps)
try:
while operations:
i = operations.pop()
if i == "*2":
if output == "":
output += "(" + str(src) + "*2" + ")"
else:
output = "(" + output + "*2" + ")"
if i == "-1":
if output == "":
output += "(" + str(src) + "-1" + ")"
else:
output = "(" + output + "-1" + ")"
except IndexError:
pass
print(output)
您的算法是指數級的,在每個額外的“廣度級別”,您為上一級別的每個值添加2個新值。 例如:
26 (breadth = 0)
25, 52 (breadth = 1)
24, 50, 51, 104 (breadth = 2)
23, 48, 49, 100, 50 (skipped because seen), 102, 103, 208 (breadth = 3)
22, 46, 47, 96, 48 (skip), 98, 99, 200 (breadth = 4)
21, 44, 45, 92, 46, 94, 95, 192, 97, 196, 199, 400 (breadth = 5)
案例src = 26,dst = 5的解決方案是減去1直到達到5,這需要21“廣度級別”= 21次操作。 在該級別,您的隊列和您的seen_list
將包含~2 ^ 20個值; 並且對於隊列中的每個值,您進行線性搜索以查看它是否存在於列表中,因此該級別將包含2 ^ 20 * 2 ^ 20個比較= 2 ^ 40~1,000億個比較。 這需要時間,而這只是最后一個級別。
你應該想到一個更好的算法。 對於初學者來說,如果你當前的價值高於目標價值,那就沒有必要加倍,因為它肯定只會增加額外的步驟。僅此考慮因素將使這種情況下的步數從數百萬減少到21(你會只需減去1直到達到目標值,這通常會在src > dest
時發生
BFS在這里不是一個選項,因為指數增長(執行2 ^ (n - 1) to 2^n
trys,其中n是所需步數)。 而是嘗試找到用於生成所需數字的邏輯規則。
設a
為輸入數字, b
為應生成的數字。
有三種情況:
a == b
,這個案例很簡單,只是為了完整而列出 a > b
,解是a - b
次減去-1
a < b
:這是更棘手的部分 最小數量的操作需要最少的乘法和減法次數。 由於以下事實,可以容易地最小化子圖: (a - 1) * 2 = a * 2 - 2
。 因此,我們可以通過在乘法之前簡單地減去任意數量來減少任意數量的減法數量。
由於我們只能減法和乘法,所以最小乘法數是min n => a * 2 ^ n >= b
。
使用這個事實,我們可以確定減去的數量: s = b - 2 ^ n * a
。
實現在偽代碼中看起來像這樣(不能提供python代碼):
//using the same variable-names as above in the description
minOp(int a , int b)
//find minimum number of multiplications
int n
for(n = 0 ; a << n < b ; n++)
noop
//find amount to substract
int s = (a << n) - b
for(int i = 0 ; i < n ; i++)
print("(")
print(a)
//calculate operations
while(n > 0)
//calculate number of times we need to substract here (minimization of substractions)
while(s >= 1 << n)
print(" - 1")
s -= 1 << n
print(")")
//divide by two
print(" * 2")
n -= 1
while(s >= 1 << n)
print(" - 1")
s -= 1 << n
print(" = ")
print(b)
該實現還包括情況a == b
- 其中n = 0
且s = 0
- 並且a > b
- 具有n = 0
且s = a - b
。
在上面的java實現中的測試運行將產生此輸出:
(((4)* 2 - 1)* 2 - 1)* 2 = 26
上述計算的簡化顯示了該算法背后的思想:
((4 * 2 - 1) * 2 - 1) * 2 = 26
(4 * 2 * 2 - 2 - 1) * 2 = 26
4 * 2 * 2 * 2 - 3 * 2 = 26
32 - 6 = 26
感謝@ user3386109的解釋:
假設起始值為A,目標值為B.第一步是創建一個從B開始的目標值列表,然后除以2(必要時向上舍入)。 例如,如果B為26,那么目標值列表將為26,13,7,4,2,1。如果起始值A是這些目標值中的任何一個,那么您可以輕松爬到目標B(乘以2並在必要時減1。 如果A不是這些值中的一個,那么您首先從A中減去1,直到達到其中一個目標值。 例如,如果A為6,則需要兩次減法才能達到4,然后從4爬升到26.如果A為12,則需要5次減法才能達到7,依此類推。 顯然,如果A大於B,那么你只需減去一個直到達到B
該代碼有望實現上述非常有效的算法。
private static int solve(int n, int m) {
int steps = 0;
int cur = n;
ArrayList<Integer> arr = new ArrayList<>();
arr.add(m);
for (int i = 0; !arr.contains(1); ++i)
arr.add((int) Math.round((double) arr.get(i) / 2));
while (cur != m) {
if (arr.contains(cur))
cur *= 2;
else
cur--;
steps++;
}
return steps;
}
說明::
想象一下樓梯,從(n)開始你必須到達它的頂部(即:數字m),所以你決定列出所有最好的步驟,然后你參考你的號碼,看看是否它存在於你為最佳解決方案制作的列表中,如果它存在,那么你只需按照步驟操作即可獲得最佳解決方案,如果不是,你必須將自己調整到最佳步驟(比如減去1)和然后你就到達目的地的最佳軌道和vooooom。 更多:請參閱先生解決方案中的解釋,更好地解釋了這一點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.