Python 消除嵌套循环

Python 消除嵌套循环,python,algorithm,graph-theory,Python,Algorithm,Graph Theory,我被分配了一个任务,我必须建立一个暴力算法。它必须通过一个包含14400个顶点的图来找到最佳路线,一天中24小时的每个顶点有600个。在600个顶点中的每个顶点上,可以为下一个小时选择480个顶点 我曾经尝试过构建一个算法,但是现在的方式不可能遍历图,因为我最终得到了很多嵌套循环。我是Python新手, 有没有办法改进算法 Path = [0] * 2 S = [12, 9, 20]; K = 10 S[:] = [x - K for x in S] for x in range(0,600)

我被分配了一个任务,我必须建立一个暴力算法。它必须通过一个包含14400个顶点的图来找到最佳路线,一天中24小时的每个顶点有600个。在600个顶点中的每个顶点上,可以为下一个小时选择480个顶点

我曾经尝试过构建一个算法,但是现在的方式不可能遍历图,因为我最终得到了很多嵌套循环。我是Python新手, 有没有办法改进算法

Path = [0] * 2
S = [12, 9, 20];
K = 10
S[:] = [x - K for x in S]

for x in range(0,600):     #1.hour              
Path[0]= x
for y in range(-240,240):    # 2.hour
    hour2 = x+y
    if 0 <= hour2 <= 600:
         Path[1] = hour2             
         for y in range(-240,240):   # 3.hour
             hour3 = hour2 + y
             if 0 <= hour3 <= 600:
                 Path[2]= hour3
                 price = sum([a*b for a,b in zip(Path,S)])
                 if maxprice < price:
                     maxprice = price
                     Optimalpath = list(Path)
print Optimalpath
print maxprice
Path=[0]*2
S=[12,9,20];
K=10
S[:]=[x-K表示S中的x]
对于范围(0600)内的x:#1.5小时
路径[0]=x
对于范围内的y(-240240):#2.5小时
小时2=x+y

如果0您可以使用以下组合


考虑将循环体转换为函数

for x in ...
    for y in ...
        for z in ...
            ...
三重循环令人望而生畏。但是,考虑一下:

def process_xy(x, y):
    for z in ...

for x in ...
    for y in ...
        process_xy(x, y)
您不仅减少了代码缩进,还完成了以下工作:

  • 您已经创建了一个可以独立调试和测试的较小单元(
    process_xy
  • 剩下的嵌套循环则执行更简单的操作——只需调用一个函数

  • 请注意:

    for x0 in a0:
        for x1 in a1:
            for x2 in a2:
                 ....
    
    相当于

    import itertools
    
    for (x0, x1, x2) in itertools.product(a0, a1, a2):
        ...
    

    这在嵌套范围不依赖于外部范围时非常有用。

    在24个阶段中的每个阶段,至少有240种可能性(通常为 多达480人)。因此,至少存在
    24**240
    可能的路径。这比
    10**57
    路径。用暴力解决这个问题是不可能的。这个 然而,问题可以通过使用来解决


    同样,您可以使用递归来生成所有可能的路径。 假设您有一个生成长度为1的所有可能路径的。这很简单:

    def generate_paths1():
        for i in range(600):
            yield [i]
    
    您可以使用
    generate_paths1
    生成长度为2的所有可能路径:

    def generate_paths2():
        for path in generate_paths1():
            current = path[-1]
            low = max(current-240, 0)
            high = min(current+240, 600)
            for i in range(low, high):
                yield path+[i]
    
    您可以使用
    生成路径2
    生成所有长度为3的路径:

    def generate_paths3():
        for path in generate_paths2():
            current = path[-1]
            low = max(current-240, 0)
            high = min(current+240, 600)
            for i in range(low, high):
                yield path+[i]
    
    但是等等<代码>生成路径3
    的功能实际上与
    生成路径2
    。当然还有更好的办法。我们可以写一个递归函数 可以执行所有操作的函数
    生成路径1
    生成路径2
    ,以及
    生成路径3
    可以--以及更多:

    def generate_paths(N):
        # moves = range(0, 601, 120)   # see below for why this is an improvement
        moves = range(601)
        if N == 1:
            for i in moves:
                yield [i]
        else:
            for path in generate_paths(N-1):
                current = path[-1]
                low = max(current-240, 0)
                high = min(current+240, 600)
                for i in [i for i in moves if low <= i <= high]:
                    yield path+[i]
    
    N = 3
    for path in generate_paths(N):
        ...
    
    因为当S为正时,最优解倾向于使用600使价格最大化,当S为负时,使用0使损失最小化。介于两者之间的其他值是最佳解决方案需要从0移动到600或从600移动到0的最大跳数

    这将减少到
    6**24
    的路径数,该路径比
    240**24
    小得多,但仍然太大,无法采用暴力解决方案


    使用该工具可以解决最佳路径问题——即使是完整的24阶段问题——如下所示:

    Maximize price = sum([a*b for a, b in zip(S, path)])
    Subject to:
    
        x[1] - x[0] <= 240
        x[0] - x[1] <= 240
        x[2] - x[1] <= 240
        x[1] - x[2] <= 240
        ... 
    
    import numpy as np
    import scipy.optimize as optimize
    
    """
    Minimize: np.dot(S, x)
    Subject to: np.dot(A, x) <= b
    """
    N = 24
    K = 10
    S = np.random.randint(-K//2, +K//2, size=N)
    A = np.zeros((2*(N-1), N), dtype=int)
    for i in range(N-1):
        A[2*i, i:i+2] = (1, -1)
        A[2*i+1, i:i+2] = (-1, 1)
    b = np.array([240]*A.shape[0])
    bounds = [(0, 600)]*N
    result = optimize.linprog(c=-S, A_ub=A, b_ub=b, bounds=bounds)
    
    optimal_path = result.x
    max_price = np.dot(S, optimal_path)
    print('''S: {}
    optimal_path: {}
    price: {}'''.format(S, optimal_path, max_price))
    

    递归。非常感谢你的帮助。有一个问题,你是如何计算最优解所需的最大跳数的?我通过用蛮力找到非常小的
    N
    的解,获得了一些直觉。后来我意识到这是一个线性规划问题,因此知道最优解总是出现在“边界”(给定约束)上。这里,这意味着移动的最大量是240。也就是说,如果你在0,希望达到600,但最多只能跳240,那么你将跳240,然后是480,然后在600着陆。相反,如果你在600,并且希望得到0,你会跳(最大可能的数量)到360(=600-240),然后120到达0。因此,最佳解决方案使用的唯一值在
    [0、120、240、360、480、600]
    中。还有一个问题。用蛮力法。我可以看出你写的递归函数是有效的。但我无法理解的是它是如何创建路径的,因为从我的角度来看,N没有改变。当
    N=1
    (简单)和
    N=2
    (更难,但不太难)时,您可能希望手动查看代码。请注意,当
    N=2
    时,您在generate_path(N-1)中为path找到了
    。这是更改
    N
    的地方<代码>生成路径(2)
    调用
    生成路径(1)
    。类似地,
    生成路径(3)
    调用
    生成路径(2)
    。通常(对于N>=2),
    生成路径(N)
    调用
    生成路径(N-1)
    import numpy as np
    import scipy.optimize as optimize
    
    """
    Minimize: np.dot(S, x)
    Subject to: np.dot(A, x) <= b
    """
    N = 24
    K = 10
    S = np.random.randint(-K//2, +K//2, size=N)
    A = np.zeros((2*(N-1), N), dtype=int)
    for i in range(N-1):
        A[2*i, i:i+2] = (1, -1)
        A[2*i+1, i:i+2] = (-1, 1)
    b = np.array([240]*A.shape[0])
    bounds = [(0, 600)]*N
    result = optimize.linprog(c=-S, A_ub=A, b_ub=b, bounds=bounds)
    
    optimal_path = result.x
    max_price = np.dot(S, optimal_path)
    print('''S: {}
    optimal_path: {}
    price: {}'''.format(S, optimal_path, max_price))
    
    S: [ 0  1  3  4 -5 -1  0 -3 -4  0  3  2 -5  1 -4 -1 -3  2  0 -2  0  4 -2  2]
    optimal_path: [ 360.  600.  600.  360.  120.    0.    0.    0.    0.  240.  480.  240.
        0.  240.    0.    0.    0.  240.    0.  120.  360.  600.  360.  600.]
    price: 8520.0