Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 电话号码排列从按钮x开始,在按钮y结束,z长_Python_Algorithm_Permutation - Fatal编程技术网

Python 电话号码排列从按钮x开始,在按钮y结束,z长

Python 电话号码排列从按钮x开始,在按钮y结束,z长,python,algorithm,permutation,Python,Algorithm,Permutation,我试图找到给定x,y和z的可能排列的总数。 x是初始数字,y是最终数字,z是按下的按钮总数。 我应该只能像国际象棋中的骑士一样,以“L”形从一个数字移动到另一个数字。 例如,如果您刚刚拨打了1,则下一个号码必须是6或8。如果你刚刚拨了6,下一个号码必须是1或7。 目前,我的实现输出了我给出的所有数字的正确答案。然而,它的速度非常慢,因为计算时间是指数级的。我想知道的是如何在线性时间内或多或少地计算这个。z将始终介于1和100之间(含1和100) ##Computes the number of

我试图找到给定x,y和z的可能排列的总数。 x是初始数字,y是最终数字,z是按下的按钮总数。 我应该只能像国际象棋中的骑士一样,以“L”形从一个数字移动到另一个数字。 例如,如果您刚刚拨打了1,则下一个号码必须是6或8。如果你刚刚拨了6,下一个号码必须是1或7。 目前,我的实现输出了我给出的所有数字的正确答案。然而,它的速度非常慢,因为计算时间是指数级的。我想知道的是如何在线性时间内或多或少地计算这个。z将始终介于1和100之间(含1和100)

##Computes the number of phone numbers one
##can dial that start with the digit x, end
##in the digit y, and consist of z digits in
##total. Output this number as a
##string representing the number in base-10.
##Must follow "knights rule" moves, like chess
##########_________##########
##########|1||2||3|##########
##########|_||_||_|##########
##########|4||5||6|##########
##########|_||_||_|##########
##########|7||8||9|##########
##########|_||_||_|##########
##########|_||0||_|##########
##########^^^^^^^^^##########
dict = {0: [4, 6], 1: [6, 8], 2: [7, 9], 3: [4, 8],
    4: [0, 3, 9], 5: [], 6: [0, 1, 7], 7: [2, 6], 8: [1, 3],
    9: [2, 4]}


def recAnswer(current, y, z, count, total):
    if count == z and current == y:
            total += 1
            return total
    count+=1
    if count > z:
            return total
    for i in dict.get(current):
            total = recAnswer(i, y, z, count, total)
    return total

def answer(x, y, z):
    if x == y:
            if z%2 == 0:
                    return '0'
    elif x == 5 or y == 5:
            if z == 1 and x == y:
                    return '1'
            else:
                    return '0'
    elif x%2 != 0 and y%2 == 0:
            if z%2 != 0:
                    return '0'
    elif x%2 == 0 and y%2 != 0:
            if z%2 != 0:
                    return '0'
    elif x%2 == 0 and y%2 ==0:
            if z%2 == 0:
                    return '0'
    elif x%2 != 0 and y%2 != 0:
            if z%2 == 0:
                    return '0'

    total = recAnswer(x,y,z,1,0)
    return str(total)

def test():
    for i in xrange(1,15,1):
            print i,":",answer(1,3,i)

    print answer(6, 2, 5)
    print answer(1, 6, 3)
    print answer(1, 1, 99)


test()

代码运行缓慢的原因是,您最终会一次又一次地访问(并重新计算)相同的组合。您可以使用一种称为“记忆”的技术缩短重新计算部分

记忆化很容易添加,但是让我们重新设计递归函数,以便调用函数进行累积,而不是函数本身。换句话说,不要传递总计,只返回此子路径的组合:

def recAnswer(current, y, z, count):
    if count == z and current == y:
        return 1

    count += 1
    if count > z:
        return 0

    total = 0
    for i in dict.get(current):
        total += recAnswer(memo, i, y, z, count)

    return total
此更改不会更改计算本身;结果仍然是一样的

现在,让我们缩短对相同参数的所有重复调用。我们将字典
memo
传递给函数。这个dict的键是函数参数的元组。作为递归函数的第一步,检查计算是否已经完成。作为初始计算的最后一步,将溶液添加到dict中:

def recAnswer(memo, current, y, z, count):
    # dict key is the tuple of arguments
    key = (current, y, z, count)

    # Have we been here before? If so, return memoized answer
    if key in memo:
        return memo[key]

    if count == z and current == y:
        return 1

    count += 1
    if count > z:
        return 0

    total = 0
    for i in dict.get(current):
        total += recAnswer(memo, i, y, z, count)

    # Store answer for later use
    memo[key] = total

    return total
然后用一个空的dict进行计算,当然:

total = recAnswer({}, x, y, z, 1)
附录:现在我已经了解了
@decorator
s,我将用memonization来装饰函数,这样原始函数就不会改变。正如Janne在评论中提到的,我还要做一个修改:我将把目标值和当前计数合并成一个变量,这个变量从目标值开始,然后倒计时到零,而不是倒计时到目标值

首先是备忘录装饰器,它将是一个包含要装饰的函数的类,
func
和备忘录字典。此类必须使用所需数量的参数实现函数调用:

class memoized(object):
    """Decorator function that adds the memoization"""

    def __init__(self, func):
        self.func = func
        self.memo = {}

    def __call__(self, current, target, count): 
        key = (current, target, count)
        if key not in self.memo:
            self.memo[key] = self.func(current, target, count)
        return self.memo[key]
现在是在
def
初始化之前使用decorator的简化函数:

@memoized
def recAnswer(current, target, count):
    """Unmemoized original function"""

    if count == 0:
        return int(current == target)       # False: 0, True: 1

    total = 0
    for next in dict[current]:
        total += recAnswer(next, target, count - 1)

    return total
@memonized
装饰程序通过
memonized.\uuuu call\uuuu
调用函数
recAnswer。按如下方式调用递归函数:

total = recAnswer(x, y, z - 1)
(这里的-1考虑到在原始代码中,计数从1开始。)

可能还有改进的余地。例如,您可以使用splat语法设置
memorized
decorator类变量的参数数量,以便可以将memorizer重新用于其他函数:

def __call__(self, *args): 
    if args not in self.memo:
        self.memo[args] = self.func(*args)
    return self.memo[args]

所有这一切的结果是,如果您遇到一个问题,反复重新计算同一组参数,只需跟踪以前计算的结果就可以大大加快速度,而无需修改基本的实现。

代码之所以慢,是因为您最终访问了(和重新计算)一次又一次地重复相同的组合。你可以用一种叫做记忆的技术缩短重新计算部分

memonization很容易添加,但是让我们重新设计递归函数,以便调用函数进行累加,而不是函数本身。换句话说,不要传递总计,只返回此子路径的组合:

def recAnswer(current, y, z, count):
    if count == z and current == y:
        return 1

    count += 1
    if count > z:
        return 0

    total = 0
    for i in dict.get(current):
        total += recAnswer(memo, i, y, z, count)

    return total
此更改不会更改计算本身;结果仍然相同

现在,让我们将对相同参数的所有重复调用缩短。我们将字典
memo
传递给函数。此dict的键是函数参数的元组。作为递归函数的第一步,检查计算是否已经完成。作为初始计算的最后一步,将解决方案添加到dict:

def recAnswer(memo, current, y, z, count):
    # dict key is the tuple of arguments
    key = (current, y, z, count)

    # Have we been here before? If so, return memoized answer
    if key in memo:
        return memo[key]

    if count == z and current == y:
        return 1

    count += 1
    if count > z:
        return 0

    total = 0
    for i in dict.get(current):
        total += recAnswer(memo, i, y, z, count)

    # Store answer for later use
    memo[key] = total

    return total
然后用一个空的dict进行计算,当然:

total = recAnswer({}, x, y, z, 1)
附录:现在我已经了解了
@decorator
s,我将用memonization来修饰函数,这样原始函数就不会改变。好吧,我将再做一次更改,正如Janne在评论中提到的那样:我将把目标cond和当前count合并成一个变量,从e目标值并倒计时到零,而不是倒计时到目标值

首先是备忘录装饰器,它将是一个包含要装饰的函数的类,
func
和备忘录字典。该类必须使用所需数量的参数实现一个functon
\uuu调用\uu

class memoized(object):
    """Decorator function that adds the memoization"""

    def __init__(self, func):
        self.func = func
        self.memo = {}

    def __call__(self, current, target, count): 
        key = (current, target, count)
        if key not in self.memo:
            self.memo[key] = self.func(current, target, count)
        return self.memo[key]
现在是在
def
初始化之前使用decorator的简化函数:

@memoized
def recAnswer(current, target, count):
    """Unmemoized original function"""

    if count == 0:
        return int(current == target)       # False: 0, True: 1

    total = 0
    for next in dict[current]:
        total += recAnswer(next, target, count - 1)

    return total
@memonized
装饰程序通过
memonized.\uuuu call\uuuuuu
调用函数
recAnswer
,该函数处理记忆。调用递归函数如下:

total = recAnswer(x, y, z - 1)
(这里的-1考虑到在原始代码中,计数从1开始。)

可能还有改进的余地。例如,您可以使用splat语法设置
memoized
decorator类变量的参数数量,以便可以将memoizer重新用于其他函数:

def __call__(self, *args): 
    if args not in self.memo:
        self.memo[args] = self.func(*args)
    return self.memo[args]
所有这一切的结果是,如果您在反复评估同一组参数时遇到问题,只需跟踪以前计算的结果就可以大大加快速度,而无需修改基本实现。


这更适合codereview.stackexchange.com,因为您不需要面对任何问题,而只需要面对这些问题