在python中,我如何在一段时间后退出递归DFS算法?

在python中,我如何在一段时间后退出递归DFS算法?,python,algorithm,recursion,timeout,exit,Python,Algorithm,Recursion,Timeout,Exit,我在python中有一个递归的深度优先搜索函数,我想在一段时间后完全退出(整个堆栈)。递归函数知道剩余时间(它传递了一个名为time_remaining的变量),当剩余时间小于100 ms时,我希望退出整个递归堆栈,并返回一些默认值。在python中实现这一点的最佳方法是什么?本质上,您需要启动一个线程,在一组分配的时间之后停止一个全局时间变量,然后,允许您的搜索函数在之后运行,并在“最大时间”条件为false或处于某种状态时进行检查,该状态告诉您程序不再以递归方式继续 这是一个递归函数的例子,

我在python中有一个递归的深度优先搜索函数,我想在一段时间后完全退出(整个堆栈)。递归函数知道剩余时间(它传递了一个名为time_remaining的变量),当剩余时间小于100 ms时,我希望退出整个递归堆栈,并返回一些默认值。在python中实现这一点的最佳方法是什么?

本质上,您需要启动一个线程,在一组分配的时间之后停止一个全局时间变量,然后,允许您的搜索函数在之后运行,并在“最大时间”条件为false或处于某种状态时进行检查,该状态告诉您程序不再以递归方式继续

这是一个递归函数的例子,我已经传递了一个迭代,所以你可以看到它运行并人为地减慢了速度。您需要启动一个线程来启动全局计时器,然后在每次调用递归函数时检查该全局计时器

import time
from threading import Thread

# create a global variable time that is ture
global_time = True 

# start the timer
def start_timer():
        time.sleep(0.1) # 1/10 second
        # Make sure you're referencing the GLOBAL time variable.
        global global_time
        # After 1/10 second, the timer will stop.
        global_time = False
        print("time stopped")

# The itteration variable is just for display.
def dfs(itteration):
        # Put your search logic here.

        # Again, make sure you're referencing the GLOBAL time variable.
        global global_time
        if global_time == True:
                # Artificially slowing down the search function.
                time.sleep(0.01)
                # Just print the iteration so you can see what's going on.
                itteration = itteration + 1 
                print(itteration)
                return dfs(itteration)
        else:
                return "DONE"

# First start the timer.
timer_thread = Thread(target = start_timer)
timer_thread.start()
# Run the search function.
print(dfs(0))
由于计时器是1/10秒,直到它为假,并且每次计时将花费1/100,因此它应该只运行10次。这是我的结果:

python time.py 
1
2
3
4
5
6
7
8
9
time stopped
DONE
使用
global
或sle非常重要。dfs功能不会知道
global\u time
是您在第4行设置的时间,并且会一直持续下去

注意,我的python版本是3.5,线程在2.7版本中可能有所不同


不相关的注意:DFS效率非常低。您应该使用BFS()或(已证明是),尽管这与问题无关

这里有一种方法可以做到这一点。它使用一个异常来展开堆栈,并使用一个装饰器来检查每次调用递归时剩余的时间

import time

class TimeRemainingExpired(Exception):
    pass

def enforce_time_remaining(f):
    """ decorator to check time remaining and raise if expired """
    def new_f(*args, **kwargs):
        if kwargs.get('_time_remaining_end_time') is None:
            kwargs['_time_remaining_end_time'] = \
                time.time() + kwargs['time_remaining']
            print(kwargs['_time_remaining_end_time'])
            print(kwargs['time_remaining'])
        if time.time() >= kwargs['_time_remaining_end_time']:
            raise TimeRemainingExpired
        return f(*args, **kwargs)

    return new_f


@enforce_time_remaining
def recurse(**kwargs):
    time.sleep(.01)
    recurse(**kwargs)

print(time.time())
try:
    recurse(time_remaining=1)
except TimeRemainingExpired:
    print('Time Expired')
print(time.time())

共享您的函数。在时间用完时有条件地返回?您自己尝试过吗?你能分享你尝试过的吗?@wwii-如果算法是n层的话就不起作用了-它必须继续往上爬。@Stephernauch不确定我能分享什么,因为我不知道如何实现它。谢谢@display name,但这仍然需要在整个堆栈中展开。如果您只调用dfs(迭代),将其分配给一个变量,并在dfs调用后打印该变量,而不是返回dfs(迭代),那么它不会先打印完成,然后打印数字9到1的背景词吗?我需要整个堆栈停止(甚至抛出错误)。也许我所要求的是不可能的?如果你不启动计时器线程,没有任何东西会将其设置为false,这意味着
dfs
将永远运行。也许我不明白你的问题?请注意,您可以摆脱
iteration
。这只是为了展示。我不知道你所说的“仍然需要在整个堆栈中展开”是什么意思。这里没有退路。它只是检查一个全局变量,并且恰好在打印
time stopped
之后完成了9次迭代。我所说的在堆栈中展开的意思是,当子递归返回done时,它会将控制传递给调用它的父函数。然后,它将控制权传递回其父级,以此类推。如果我有几个节点,这没关系。但是假设我在搜索树的10亿层深处,我的时间不多了(想想国际象棋)——我需要退出,返回一个值,并将控制权传递回堆栈中最顶层的函数。这与仅在时间到时返回有区别吗?它会在时间到期时抛出异常。@StephenRouch这绝对是一种干净的实现方法,但是有没有一种方法可以直接到达调用函数堆栈的顶部,而不是每一层都往上走呢?这并不是每一层都往上走。因为它抛出了一个异常,所以它会一直向上,直到捕捉到异常为止。在本例中,
TimeRemainingExpired
被捕获在最后三行中。事实上,在本例中,递归函数休眠时间为
0.01
秒,整个过程为1秒,因此将下降100级,然后将整个堆栈与异常一起展开。似乎异常会一直传递到递归堆栈的上一个调用方,直到它在顶部被捕获为止-类似于只返回第六条注释