BFS用于算术运算

时间:2016-01-15 10:22:33

标签: python algorithm breadth-first-search

使用最少的操作将数字m转换为n。允许的操作是减1并乘以2。

例如:4和6.答案是2。 第一次操作:-1 - > 4-1 = 3。 第二次操作:* - > 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))

队列实施已完成here

感谢保罗: 下面是我在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)

3 个答案:

答案 0 :(得分:2)

您的算法是指数级的,在每个额外的&#34;广度级别&#34;为上一级别的每个值添加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&#34;广度等级&#34; = 21次操作。在该级别,您的队列和seen_list将包含~2 ^ 20个值;并且对于队列中的每个值,您进行线性搜索以查看它是否存在于列表中,因此该级别将包含2 ^ 20 * 2 ^ 20个比较= 2 ^ 40~1,000亿个比较。这需要时间,而且只是最后一个级别。

你应该想到一个更好的算法。对于初学者来说,如果你的当前值高于目标值,那么加倍它是没有意义的,因为它肯定只会增加额外的步骤。 单独考虑这个问题会将此案例的步数从数百万减少到21(您只需减去1直到达到目标值,这通常会在src > dest)时发生

答案 1 :(得分:1)

由于指数增长(2 ^ (n - 1) to 2^n trys被执行,其中n是所需步数),因此BFS在这里不是一个选项。而是尝试找到生成所需数字的逻辑规则。

a为输入数字,b应该生成的数字。

有三种情况:

  • a == b,这个案例很简单,只是为了完整而列出
  • a > b,解决方案a - b次由-1
  • 减去
  • a < b:这是更棘手的部分

最小数量的操作需要最少的乘法和减法次数。由于以下事实,可以轻松地将子图最小化:(a - 1) * 2 = a * 2 - 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 = 0s = 0 - 以及a > b - n = 0s = 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

答案 2 :(得分:0)

这段代码有望实现上述非常有效的算法。

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。 更多信息:请参阅先生解决方案中的解释,更好地解释了这一点。