鱼C论坛

 找回密码
 立即注册
查看: 2053|回复: 10

[已解决]python小练习(057):回溯法求解最优路径问题

[复制链接]
发表于 2017-1-4 14:03:41 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 jerryxjr1220 于 2017-1-10 12:57 编辑

已知:
0,1,2,3,4五个点到其他各点的路径权重,用字典形式表示。

求:
从起点2到终点4的最优路径(权重总和最小)。

  1. weight={0:{1:2,2:4,3:6,4:8},
  2.         1:{0:1,2:3,3:1,4:4},
  3.         2:{0:2,1:5,3:4,4:9},
  4.         3:{0:5,1:2,2:6,4:6},
  5.         4:{0:7,1:9,2:4,3:7}}
复制代码


除了利用排列组合,还有什么更好的解题思路么?

貌似用回溯+递归应该也能解,但是想不到好的递归结束的条件,容易进入无限循环。

优化后的解法见3楼
最佳答案
2017-1-4 22:26:51
本帖最后由 jerryxjr1220 于 2017-1-4 23:37 编辑

我自己找到优化的方法了,还是借用字典,把所以走过的路径都记录下来,然后依次比较,有比记录更短的路径就添加进去,依次类推,不断循环,直到最后最优值不变了即为最优解。
  1. #coding:utf-8
  2. '''题目要求:起点(0),终点(9),找出权重最小的路径即为最优路径'''

  3. weight={0:{1:3,2:5,3:6,4:8,5:4,6:9,7:8,8:9,9:16,10:3,11:2},
  4.         1:{0:2,2:3,3:3,4:4,5:3,6:7,7:2,8:8,9:12,10:4,11:5},
  5.         2:{0:2,1:5,3:4,4:9,5:3,6:6,7:2,8:5,9:10,10:3,11:3},
  6.         3:{0:5,1:2,2:6,4:6,5:5,6:3,7:6,8:7,9:10,10:4,11:4},
  7.         4:{0:7,1:9,2:2,3:7,5:3,6:8,7:3,8:2,9:10,10:5,11:4},
  8.         5:{0:4,1:3,2:3,3:2,4:2,6:8,7:4,8:5,9:11,10:4,11:5},
  9.         6:{0:2,1:2,2:3,3:3,4:4,5:5,7:6,8:5,9:6,10:4,11:5},
  10.         7:{0:3,1:3,2:2,3:4,4:3,5:4,6:3,8:2,9:9,10:4,11:5},
  11.         8:{0:2,1:2,2:4,3:1,4:2,5:3,6:1,7:3,9:6,10:4,11:5},
  12.         9:{0:1,1:3,2:5,3:7,4:2,5:4,6:6,7:3,8:8,10:4,11:5},
  13.         10:{0:1,1:3,2:5,3:7,4:2,5:4,6:6,7:3,8:8,9:12,11:5},
  14.         11:{0:1,1:2,2:5,3:7,4:2,5:4,6:6,7:3,8:8,9:12,10:4}}

  15. dic = {}

  16. def dp(x,y):
  17.         if (x,y) not in dic:
  18.                 dic[(x,y)] = [weight[x][y], [x,y]]
  19.         else:
  20.                 for ea in dic:
  21.                         a,b = ea
  22.                         if a == x:
  23.                                 for ch in dic:
  24.                                         c,d = ch
  25.                                         if b==c and d==y:
  26.                                                 if dic[(x,y)][0] > dic[(a,b)][0] + dic[(c,d)][0]:
  27.                                                         dic[(x,y)][0] = dic[(a,b)][0] + dic[(c,d)][0]
  28.                                                         dic[(x,y)][1] = dic[(a,b)][1] + dic[(c,d)][1]

  29. for i in range(12):
  30.         for j in range(12):
  31.                 if i != j:
  32.                         dp(i,j)
  33. record = dic[(0,9)][0]

  34. while True:
  35.         for i in range(12):
  36.                 for j in range(12):
  37.                         if i != j:
  38.                                 dp(i,j)
  39.         if record == dic[(0,9)][0]:
  40.                 print (dic[(0,9)])
  41.                 break
  42.         else:
  43.                 record = dic[(0,9)][0]
复制代码


输出:
[12, [0, 11, 11, 4, 4, 8, 8, 9]]
[Finished in 0.2s]

可以看到比之前的程序快了将近200倍

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2017-1-4 14:31:32 | 显示全部楼层
下面的方法用的是排列组合的方法,但是对于节点非常多的情况,速度会非常慢,所以必须要优化。
  1. #coding:utf-8
  2. '''题目要求:起点(2),终点(4),找出权重最小的路径即为最优路径'''
  3. weight={0:{1:2,2:4,3:6,4:8},
  4.                 1:{0:1,2:3,3:1,4:4},
  5.                 2:{0:2,1:5,3:4,4:9},
  6.                 3:{0:5,1:2,2:6,4:6},
  7.                 4:{0:7,1:9,2:4,3:7}}

  8. import itertools
  9. dp = weight[2][4]
  10. for i in range(1,4):
  11.         permutation = itertools.permutations([0,1,3],i)
  12.         for p in permutation:
  13.                 combination = [2]+list(p)+[4]
  14.                 tmp = 0
  15.                 for c in range(len(combination)-1):
  16.                         tmp += weight[combination[c]][combination[c+1]]
  17.                 if tmp < dp:
  18.                         dp = tmp
  19.                         best = combination
  20. print (best, dp)
复制代码

输出:
[2, 0, 1, 4] 8
[Finished in 0.1s]
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-1-4 16:46:47 | 显示全部楼层
当节点达到12个的时候,用时已经要超过半分钟了。
  1. #coding:utf-8
  2. '''题目要求:起点(0),终点(9),找出权重最小的路径即为最优路径'''
  3. weight={0:{1:3,2:5,3:6,4:8,5:4,6:9,7:8,8:9,9:16,10:3,11:2},
  4.                 1:{0:2,2:3,3:3,4:4,5:3,6:7,7:2,8:8,9:12,10:4,11:5},
  5.                 2:{0:2,1:5,3:4,4:9,5:3,6:6,7:2,8:5,9:10,10:3,11:3},
  6.                 3:{0:5,1:2,2:6,4:6,5:5,6:3,7:6,8:7,9:10,10:4,11:4},
  7.                 4:{0:7,1:9,2:2,3:7,5:3,6:8,7:3,8:2,9:10,10:5,11:4},
  8.                 5:{0:4,1:3,2:3,3:2,4:2,6:8,7:4,8:5,9:11,10:4,11:5},
  9.                 6:{0:2,1:2,2:3,3:3,4:4,5:5,7:6,8:5,9:6,10:4,11:5},
  10.                 7:{0:3,1:3,2:2,3:4,4:3,5:4,6:3,8:2,9:9,10:4,11:5},
  11.                 8:{0:2,1:2,2:4,3:1,4:2,5:3,6:1,7:3,9:6,10:4,11:5},
  12.                 9:{0:1,1:3,2:5,3:7,4:2,5:4,6:6,7:3,8:8,10:4,11:5},
  13.                 10:{0:1,1:3,2:5,3:7,4:2,5:4,6:6,7:3,8:8,9:12,11:5},
  14.                 11:{0:1,1:2,2:5,3:7,4:2,5:4,6:6,7:3,8:8,9:12,10:4}}


  15. import itertools
  16. dp = weight[0][9]
  17. best = [0, 9]
  18. for i in range(1,11):
  19.         permutation = itertools.permutations(list(range(1,9))+[10,11],i)
  20.         for p in permutation:
  21.                 combination = [0]+list(p)+[9]
  22.                 tmp = 0
  23.                 for c in range(len(combination)-1):
  24.                         tmp += weight[combination[c]][combination[c+1]]
  25.                 if tmp < dp:
  26.                         dp = tmp
  27.                         best = combination
  28. print (best, dp)
复制代码

输出:
[0, 11, 4, 8, 9] 12
[Finished in 36.6s]
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-1-4 22:26:51 | 显示全部楼层    本楼为最佳答案   
本帖最后由 jerryxjr1220 于 2017-1-4 23:37 编辑

我自己找到优化的方法了,还是借用字典,把所以走过的路径都记录下来,然后依次比较,有比记录更短的路径就添加进去,依次类推,不断循环,直到最后最优值不变了即为最优解。
  1. #coding:utf-8
  2. '''题目要求:起点(0),终点(9),找出权重最小的路径即为最优路径'''

  3. weight={0:{1:3,2:5,3:6,4:8,5:4,6:9,7:8,8:9,9:16,10:3,11:2},
  4.         1:{0:2,2:3,3:3,4:4,5:3,6:7,7:2,8:8,9:12,10:4,11:5},
  5.         2:{0:2,1:5,3:4,4:9,5:3,6:6,7:2,8:5,9:10,10:3,11:3},
  6.         3:{0:5,1:2,2:6,4:6,5:5,6:3,7:6,8:7,9:10,10:4,11:4},
  7.         4:{0:7,1:9,2:2,3:7,5:3,6:8,7:3,8:2,9:10,10:5,11:4},
  8.         5:{0:4,1:3,2:3,3:2,4:2,6:8,7:4,8:5,9:11,10:4,11:5},
  9.         6:{0:2,1:2,2:3,3:3,4:4,5:5,7:6,8:5,9:6,10:4,11:5},
  10.         7:{0:3,1:3,2:2,3:4,4:3,5:4,6:3,8:2,9:9,10:4,11:5},
  11.         8:{0:2,1:2,2:4,3:1,4:2,5:3,6:1,7:3,9:6,10:4,11:5},
  12.         9:{0:1,1:3,2:5,3:7,4:2,5:4,6:6,7:3,8:8,10:4,11:5},
  13.         10:{0:1,1:3,2:5,3:7,4:2,5:4,6:6,7:3,8:8,9:12,11:5},
  14.         11:{0:1,1:2,2:5,3:7,4:2,5:4,6:6,7:3,8:8,9:12,10:4}}

  15. dic = {}

  16. def dp(x,y):
  17.         if (x,y) not in dic:
  18.                 dic[(x,y)] = [weight[x][y], [x,y]]
  19.         else:
  20.                 for ea in dic:
  21.                         a,b = ea
  22.                         if a == x:
  23.                                 for ch in dic:
  24.                                         c,d = ch
  25.                                         if b==c and d==y:
  26.                                                 if dic[(x,y)][0] > dic[(a,b)][0] + dic[(c,d)][0]:
  27.                                                         dic[(x,y)][0] = dic[(a,b)][0] + dic[(c,d)][0]
  28.                                                         dic[(x,y)][1] = dic[(a,b)][1] + dic[(c,d)][1]

  29. for i in range(12):
  30.         for j in range(12):
  31.                 if i != j:
  32.                         dp(i,j)
  33. record = dic[(0,9)][0]

  34. while True:
  35.         for i in range(12):
  36.                 for j in range(12):
  37.                         if i != j:
  38.                                 dp(i,j)
  39.         if record == dic[(0,9)][0]:
  40.                 print (dic[(0,9)])
  41.                 break
  42.         else:
  43.                 record = dic[(0,9)][0]
复制代码


输出:
[12, [0, 11, 11, 4, 4, 8, 8, 9]]
[Finished in 0.2s]

可以看到比之前的程序快了将近200倍
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-1-5 08:49:34 | 显示全部楼层
把上述的解法封装了一下,然后加入“途经”其他节点的功能。

带权重的最优路径解法是现在用的许多导航地图算法的基础,所以还是很有必要研究一下的。

bestway(s,e,a,b)函数中,s代表起始点,e代表终点,a和b代表途径点(可选)。

  1. #coding:utf-8
  2. '''题目要求:起点(0),终点(9),找出权重最小的路径即为最优路径'''

  3. weight={0:{1:3,2:5,3:6,4:8,5:4,6:9,7:8,8:9,9:16,10:3,11:2},
  4.         1:{0:2,2:3,3:3,4:4,5:3,6:7,7:2,8:8,9:12,10:4,11:5},
  5.         2:{0:2,1:5,3:4,4:9,5:3,6:6,7:2,8:5,9:10,10:3,11:3},
  6.         3:{0:5,1:2,2:6,4:6,5:5,6:3,7:6,8:7,9:10,10:4,11:4},
  7.         4:{0:7,1:9,2:2,3:7,5:3,6:8,7:3,8:2,9:10,10:5,11:4},
  8.         5:{0:4,1:3,2:3,3:2,4:2,6:8,7:4,8:5,9:11,10:4,11:5},
  9.         6:{0:2,1:2,2:3,3:3,4:4,5:5,7:6,8:5,9:6,10:4,11:5},
  10.         7:{0:3,1:3,2:2,3:4,4:3,5:4,6:3,8:2,9:9,10:4,11:5},
  11.         8:{0:2,1:2,2:4,3:1,4:2,5:3,6:1,7:3,9:6,10:4,11:5},
  12.         9:{0:1,1:3,2:5,3:7,4:2,5:4,6:6,7:3,8:8,10:4,11:5},
  13.         10:{0:1,1:3,2:5,3:7,4:2,5:4,6:6,7:3,8:8,9:12,11:5},
  14.         11:{0:1,1:2,2:5,3:7,4:2,5:4,6:6,7:3,8:8,9:12,10:4}}

  15. dic = {}

  16. def dp(x,y):
  17.     if (x,y) not in dic:
  18.         dic[(x,y)] = [weight[x][y], [x,y]]
  19.     else:
  20.         for ea in dic:
  21.             a,b = ea
  22.             if a == x:
  23.                 for ch in dic:
  24.                     c,d = ch
  25.                     if b==c and d==y:
  26.                         if dic[(x,y)][0] > dic[(a,b)][0] + dic[(c,d)][0]:
  27.                             dic[(x,y)][0] = dic[(a,b)][0] + dic[(c,d)][0]
  28.                             dic[(x,y)][1] = dic[(a,b)][1] + dic[(c,d)][1]
  29. #initiate:
  30. for i in range(12):
  31.     for j in range(12):
  32.         if i != j:
  33.             dp(i,j)

  34. def find(s,e):
  35.         global dic
  36.         record = dic[(s,e)][0]
  37.         while True:
  38.             for i in range(12):
  39.                 for j in range(12):
  40.                     if i != j:
  41.                         dp(i,j)
  42.             if record == dic[(s,e)][0]:
  43.                 return dic[(s,e)]
  44.             else:
  45.                 record = dic[(s,e)][0]

  46. def bestway(s,e,a=None,b=None):
  47.         if not b:
  48.                 if not a:
  49.                         return find(s,e)
  50.                 else:
  51.                         result1 = find(s,a)
  52.                         result2 = find(a,e)
  53.                         return [result1[0]+result2[0], result1[1]+result2[1]]
  54.         else:
  55.                 result1 = find(s,a)
  56.                 result2 = find(a,b)
  57.                 result3 = find(b,e)
  58.                 return [result1[0]+result2[0]+result3[0], result1[1]+result2[1]+result3[1]]

  59. print (bestway(0,9,7,3))
复制代码


输出:
[17, [0, 1, 1, 7, 7, 8, 8, 3, 3, 6, 6, 9]]
[Finished in 0.1s]
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-6-27 00:04:17 From FishC Mobile | 显示全部楼层
本帖最后由 qaz123765 于 2017-6-27 00:05 编辑

厉害,我只能想到之后的和直达的比较,写不出来,另外可以把最后的两个for循环去掉一个,写在while里面,取第二次值
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-9-21 13:32:46 | 显示全部楼层
厉害
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-2-25 22:07:59 | 显示全部楼层
  1. weight={0:{1:2,2:4,3:6,4:8},
  2.         1:{0:1,2:3,3:1,4:4},
  3.         2:{0:2,1:5,3:4,4:9},
  4.         3:{0:5,1:2,2:6,4:6},
  5.         4:{0:7,1:9,2:4,3:7}}

  6. t = [i for i in range(5)]
  7. f ={}
  8. def hui(t,n,m,a,b):
  9.     t1 = t[:]
  10.     t1.remove(n)
  11.     for i in t1:
  12.         b1 = b + weight[n][i]
  13.         f[i] = n
  14.         if i == m:
  15.             if b1 < a:
  16.                 print(b1)
  17.                 v = i
  18.                 f1 = []
  19.                 while True:
  20.                     f1.append(v)
  21.                     v = f[v]
  22.                     if v == 2:
  23.                         f1.append(v)
  24.                         break
  25.                 f1.reverse()
  26.                 print(f1)
  27.                 return 1
  28.             else:
  29.                 return False

  30.         n1 = i
  31.         hui(t1,n1,m,a,b1)
  32.             
  33.         
  34. hui(t,2,4,9,0)
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-2-26 09:12:09 | 显示全部楼层
本帖最后由 JAY饭 于 2018-2-26 11:31 编辑

上面这个写错了,不能包括所求位置后面的路径。重写了下,可以是可以可,但特别慢,三四十秒。我再想下

(答案已经更新,说下思路,利用回溯递归的方法,将每一步的路径简化为一个同位置的列表,每走一步,移出对应的位置元素,
递归的结束条件就是对应的列表长度为0,筛选条件是,每一步加权重数得到的和不能超过最直接的那一步的权重,否则跳出迭代,
之前就是忽略这一点。然后用字典记录路径,方便回溯,在途经这里我用的是循环加1的方法,一直到所有的途经出现在相对最短
路径中)
  1. weight={0:{1:3,2:5,3:6,4:8,5:4,6:9,7:8,8:9,9:16,10:3,11:2},
  2.         1:{0:2,2:3,3:3,4:4,5:3,6:7,7:2,8:8,9:12,10:4,11:5},
  3.         2:{0:2,1:5,3:4,4:9,5:3,6:6,7:2,8:5,9:10,10:3,11:3},
  4.         3:{0:5,1:2,2:6,4:6,5:5,6:3,7:6,8:7,9:10,10:4,11:4},
  5.         4:{0:7,1:9,2:2,3:7,5:3,6:8,7:3,8:2,9:10,10:5,11:4},
  6.         5:{0:4,1:3,2:3,3:2,4:2,6:8,7:4,8:5,9:11,10:4,11:5},
  7.         6:{0:2,1:2,2:3,3:3,4:4,5:5,7:6,8:5,9:6,10:4,11:5},
  8.         7:{0:3,1:3,2:2,3:4,4:3,5:4,6:3,8:2,9:9,10:4,11:5},
  9.         8:{0:2,1:2,2:4,3:1,4:2,5:3,6:1,7:3,9:6,10:4,11:5},
  10.         9:{0:1,1:3,2:5,3:7,4:2,5:4,6:6,7:3,8:8,10:4,11:5},
  11.         10:{0:1,1:3,2:5,3:7,4:2,5:4,6:6,7:3,8:8,9:12,11:5},
  12.         11:{0:1,1:2,2:5,3:7,4:2,5:4,6:6,7:3,8:8,9:12,10:4}}

  13. t = [i for i in range(12)]
  14. f ={}
  15. f2 = []
  16. f3 = {}
  17. def hui(t,n,m,a,b):
  18.     if len(t) == 0:
  19.         return 1
  20.     t1 = t[:]
  21.     t1.remove(n)
  22.     for i in t1:
  23.         b1 = b + weight[n][i]
  24.         if b1 > a:
  25.             continue
  26.         f[i] = n
  27.         if i == m:
  28.             if b1 <= a:

  29.                 a = b1
  30.                 f2.append(b1)
  31.                 v = i
  32.                 f1 = []
  33.                 while True:
  34.                     f1.append(v)
  35.                     v = f[v]
  36.                     if v == 0:
  37.                         f1.append(v)
  38.                         break
  39.                 f1.reverse()
  40.                 if b1 not in f3:
  41.                     f3[b1] = [f1]
  42.                 else:
  43.                     f3[b1] += [f1]

  44.             continue
  45.         n1 = i
  46.         hui(t1,n1,m,a,b1)
  47.             
  48. def tujing(c,d):
  49.     global f3,f2,f
  50.     n,m,a,b = 0,9,15,0
  51.     while True:
  52.         
  53.         f,f3,f2,f4 = {},{},[],[]
  54.         
  55.         hui(t,n,m,a,b)
  56.         for i in f2:
  57.             for j in f3[i]:
  58.                 if c in j and d in j:
  59.                     f4.append((i,j))
  60.         if len(f4) == 0:
  61.             a += 1
  62.         else:
  63.             print(min(f4))
  64.             
  65.             break
  66.         
  67. tujing(3,7)
复制代码

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-2-26 10:34:04 | 显示全部楼层
加了个continue缩短到9秒,还不够
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-2-26 10:48:46 | 显示全部楼层
又加了一个continue,加上一个筛选条件,秒出,可以啦,用的是回溯递归的方法,本来早就可以解决,一直打游戏思路都是乱的,一开始就瞎写。浪费大量时间。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-25 11:36

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表