如何使这种广度优先搜索更快?

时间:2019-02-27 16:46:37

标签: python python-3.x performance search

我有一个搜索算法,该算法寻找加法和乘法函数的组合以从一定范围的数字中达到某个范围的数字。它正在搜索最短的程序,该程序是类似于AAMMA之类的程序,在该程序中,将初始编号加,加,乘,乘,加,其中起始编号在r到s之间。它必须适用于p到q范围内的每个数字。

输入是a和m,您要为每个函数加上和乘以(num + a),(num * m)。我正在做的是尝试每种功能的组合,直到找到可以使用的功能为止,如果该分支太大,则停止该分支。如果找到有效的“程序”,请尝试在起始范围内的所有其他数字上使用该程序。它会一直这样做,直到找到没有分支而又不会超出范围的分支为止。

我知道搜索不是非常典型,但是我认为不存在重复的可能性,因此我没有包含找到的列表。

它适用于较小的范围和输入,例如

Problem3("1 2 2 3 10 20")

但是对于更大的范围,这将永远花费我的测试用例

Problem3("8 13 28 91 375383947 679472915")

我什至还没看完。从这里,我最好的方法是什么(多线程)(不希望),以某种方式使我的内部函数更快,或者只是取消了这种方法。

def Problem3(s):
    a,m,p,q,r,s = list(map(int, s.split(" ")))

    print(str(a) + "-C-" + str(m) + " processor")
    print("Input guarenteed between " + str(p) + " and " + str(q))
    print("Output is real number between " + str(r) + " and " + str(s))

    open_set = queue.Queue()
#   curr path depth
    open_set.put([p, "", 0])

    while not open_set.empty():

        subroot = open_set.get()

        multiCurr = subroot[0] * m
        addCurr = subroot[0] + a
        depth = subroot[2] + 1

        if r <= addCurr <= s:
            truePath = True
            #If we find a working path, we need to check if it works for the other things
            path = subroot[1] + "A"
            for x in range(p, q+1):
                for op in path:
                    if op == "A":
                        x += a
                    if op == "M":
                        x *= m
                if r <= x <= s:
                    pass
                else:
                    truePath = False
                    break
            if truePath:
                print("Found " + path + " at depth " + str(depth) + " with starting number " + str(p) + ", output " + str())

        if r <= multiCurr <= s:
            truePath = True
            path = subroot[1] + "M"
            for x in range(p, q+1):
                for op in path:
                    if op == "A":
                        x += a
                    if op == "M":
                        x *= m
                if r <= x <= s:
                    pass
                else:
                    truePath = False
                    break
            if truePath:
                print("Found " + path + " at depth " + str(depth) + " with starting number " + str(p) + ", output " + str())

        if addCurr > s and multiCurr > s:
            pass
        elif multiCurr > s:
            open_set.put([addCurr, subroot[1] + "A", depth])
        elif addCurr > s:
            open_set.put([multiCurr, subroot[1] + "M", depth])
        else:
            open_set.put([multiCurr, subroot[1] + "M", depth])
            open_set.put([addCurr, subroot[1] + "A", depth])

1 个答案:

答案 0 :(得分:0)

您不需要测试range(p, q + 1)序列中的每个值。您只需要测试pq。如果它适用于最低和最高的数字,则它将适用于介于两者之间的所有值,因为该问题已被简化为乘法和加法。实际上,您只需要测试program(q)的进度,将其保持在s以下,直到您创建了最短的程序即可将program(p)置于r或以上。

但是,对于呼吸优先搜索而言,这并不是一个很大的问题;您的第二个示例将需要测试17.6个<兆>万亿个可能状态;最短的解决方案是44个字符长,因此一口气搜索将探索2 ** 44个状态,因此确切地说是17,592,186,044,416!即使使用像C这样的编译程序语言,也要花费很长时间才能找到使用这种搜索的解决方案。相反,您只需使用一点数学就可以生成字符串。

您可以使用int(math.log(s // q, m))计算此处所需的最大乘法数,这是从m开始但仍低于{{1}时可以与q相乘的次数。 }。您再也无法使用更多乘法了!使用s,您可以找到将math.ceil(math.log(r / p, m))置于p或以上的最小乘法数。为了使程序长度最小,请选择这两个数字中的较低者。

然后,在每次r乘法之前,开始拟合A个加法。为此,将M作为要跟随的i个字符的数量,然后将Mr除以s。这些告知m ** ia的数字p加法,再加上随后的乘法,使其最接近qr;通过与当前sp的区别,您可以计算出可以在此处插入的q字符的最小数量,以保持在A范围内。对于[r, s],向上取整,对于p,向下取整。

对随后的每个q操作重复此过程,每次使用结果更新Mp值:

q

这是一个封闭形式的解决方案,无需搜索。结果保证是最短的:

import math

def problem3(s):
    a, m, p, q, r, s = map(int, s.split())
    p_mult = math.ceil(math.log(math.ceil(r / p), m))
    q_mult = int(math.log(s // q, m))
    mult = min(p_mult, q_mult)
    program = []
    for i in range(mult, -1, -1):
        p_additions = math.ceil((math.ceil(r / (m ** i)) - p) / a)
        q_additions = ((s // (m ** i)) - q) // a
        additions = min(p_additions, q_additions)
        program += [additions * 'A']
        if i:
            p, q = (p + (additions * a)) * m, (q + (additions * a)) * m
            program += ['M']
    return ''.join(program)