消除嵌套循环

时间:2016-02-21 00:26:40

标签: 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):     #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

我只显示了前3个小时的嵌套循环,但如果可能的话,它必须在24小时内全部迭代。

或者我认为这个问题都错了?

2 个答案:

答案 0 :(得分:4)

在24个阶段中的每个阶段,至少有240种可能性(通常为 许多人为480)。所以至少有24**240个可能的路径。这不止于此 10**57路径。你无法用蛮力解决这个问题。该 但是,使用linear programming methods可以解决问题。

作为BJ Myers suggested,您可以使用递归来生成所有可能的路径。 假设您有generator function生成所有可能的长度为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]

您可以使用generate_paths2生成长度为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]

但是等等! generate_paths3实际上与...功能相同 generate_paths2。当然有更好的方法。我们可以写一个递归 可以执行所有操作的功能generate_paths1generate_paths2generate_paths3可以 - 以及更多:

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

虽然能够生成所有路径很棒,但路径太多了。 如果我们将问题识别为linear programming (LP) problem,我们可以做得更好。

你的问题可以表达为这样的LP问题:

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

LP问题解决方案的一个特性是:

  

如果存在可行解并且(线性)目标函数是     有界,那么最优值总是在最优的边界上获得     水平集。 (我的重点)

因此,您可以将moves = range(601)替换为

    moves = range(0, 601, 120)
    # [0, 120, 240, 360, 480, 600]

因为当S为正时,最优解将倾向于使用600来最大化价格,而当S为负时,使用0来最小化损失。中间的其他值是最佳解决方案从0移动到600或从600移动到0所需的最大跳数。

这会减少6**24的路径数量,远小于240**24,但仍然太大而无法接受蛮力解决方案。

使用scipy.optimimize.linprog你可以解决最佳路径 - 即使是完整的24阶段问题 - 就像这样:

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

答案 1 :(得分:2)

您可以使用以下各项的组合。

考虑将循环体转换为函数。

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)

您不仅减少了代码缩进,还执行了以下操作:

  1. 您创建了一个较小的单元,可以独立调试和测试(process_xy
  2. 剩下的嵌套循环,在那里,做一些更简单的事情 - 只需调用一个函数。
  3. 请注意:

    for x0 in a0:
        for x1 in a1:
            for x2 in a2:
                 ....
    

    相当于

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

    但是,当嵌套范围不依赖于外部范围时,这非常有用。

相关问题