BFS for arithmetic operations

拟墨画扇 提交于 2019-12-02 07:17:20

问题


Convert a number m to n with minimum operations. The operations allowed were subtraction by 1 and multiplication by 2.

For Eg : 4 and 6. Answer is 2. 1st operation : -1 -> 4-1 = 3. 2nd operation : * -> 3 * 2 =6.

I am using BFS approach for a particular input (src=26, dst= 5) it is taking long time. Am I doing something wrong?

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))

Queue implementation is done here.

Thanks to Paul: Below is the code I came up with below code in python and it works perfectly.

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)

回答1:


BFS is not exactly an option here, due to the exponential growth (2 ^ (n - 1) to 2^n trys are performed, where n is the number of required steps). Instead try to find logic rules for generating the required number.

Let a be the input-number and b the number that should be produced.

There are three cases:

  • a == b, this case is trivial and just listed for completeness
  • a > b, the solution is a - b times substracting by -1
  • a < b: This is the more tricky part

The smallest number of operations requires the minimum number of multiplications and substractions. Substractions can easily be minimized due to the following fact: (a - 1) * 2 = a * 2 - 2. Thus we can easily reduce the number of substractions by any number that is a power of 2 by simply substracting before multiplying.
Since we can only substract and multiply, the minimum number of multiplications is min n => a * 2 ^ n >= b.
Using this fact we can determine the amount to substract: s = b - 2 ^ n * a.

The implementation would look like this in pseudocode (can't provide python code):

//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)

This implementation aswell covers the cases a == b - with n = 0 and s = 0 - and a > b - with n = 0 and s = a - b.

A test-run in a java-implementation of the above would produce this output:

(((4) * 2 - 1) * 2 - 1) * 2 = 26

The simplification of the above calculation shows the idea behind this algorithm:

((4 * 2 - 1) * 2 - 1) * 2 = 26
(4 * 2 * 2 - 2 - 1) * 2 = 26
4 * 2 * 2 * 2 - 3 * 2 = 26
32 - 6 = 26

Thanks to @user3386109 for this explanation:
Assume that the start value is A, and the goal value is B. The first step is to create a list of target values starting at B, and proceeding by dividing by 2 (rounding up if necessary). For example, if B is 26, then the list of target values would be 26, 13, 7, 4, 2, 1. If the start value A is any of those target values, then you can easily climb to the goal B (by multiplying by 2 and subtracting 1 if necessary). If A is not one of those values, then you begin by subtracting 1 from A until you reach one of those target values. For example, if A is 6, then two subtractions are needed to reach 4, and then you climb from 4 to 26. If A is 12, five subtractions are needed to reach 7, and so on. Obviously, if A is larger than B, then all you do is subtract one until you reach B




回答2:


Your algorithm is exponential, at each additional "breadth level" you add 2 new values for each value in the previous level. For example:

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)

The solution for the case src=26, dst=5 is to subtract 1 until you reach 5, that takes 21 "breadth levels" = 21 operations. At that level both your queue and your seen_list will contain ~2^20 values; and for each value in the queue you do a linear search to see if it's present in the list, so that level will consist of 2^20 * 2^20 comparisons = 2^40 ~ 1 thousand billion comparisons. That takes time, and it's just the last level.

You should think of a better algorithm. For starters, if your current value is higher than the target, there is no point in doubling it as it will surely only add additional steps.. This consideration alone will reduce the number of steps for this case from millions to 21 (you'll just subtract 1 until you reach the target value, and this will happen in general when src > dest)




回答3:


This code hopefully implements the above-mentioned very efficient algorithm.

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;
}

Explanation::

Imagine a stair and starting from (n) you've got to reach its top (i.e: the number m), so you decided to make list of all the best steps to take, then you refer to the number you have and see if it exists in that list you made for an optimal solution, if it's there then you just follow the steps and you'll get the best solution, if not you'll have to align yourself to the optimal steps (say by subtracting 1) and then you're on the optimal track and vooooom to your destination. For more: please, refer to the explanation in mr.paul solution up there explains this better.



来源:https://stackoverflow.com/questions/34808907/bfs-for-arithmetic-operations

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!