Python访问街区10个点,并俩俩绘制一条线,得到5条线,求最短的距离和?

23 篇文章 0 订阅
12 篇文章 5 订阅

Python访问街区10个点,并俩俩绘制一条线,得到5条线,求最短的距离和?

上一篇博客介绍了Python访问街区所有节点最短路径问题,并结合matplotlib可视化, 这一篇博客也基于博友的提问,将介绍平面图中散落10个点,两两配对,共得5条边,计算边的长度和,如何找到长度和最小的配对方案?

依然以上一篇的10个街区点为例,理一下思路:

  1. 10个点,俩俩配对,共得到5条边,很明显这是不考虑顺序的。可以用高中学到的排列组合来计算共有:C(2,10) * C(2,8) * C(2,6) * C(2,4) * C(2,2) / A(5,5) 种组合方法
    = 10 * 9/(2 * 1) * (8 * 7/(2 * 1))*(6 * 5/(2 * 1)) * (4 * 3/(2 * 1)) * (2 * 1/(2 * 1))/ (5 * 4 * 3 * 2 * 1)
    = 45 * 28 * 15 * 6/(5 * 4 * 3 * 2 * 1)
    = 9 * 7 * 5 * 3 = 945种方法。

  2. 计算每种路径5条边距离的总和

  3. 找出距离总和最小的路径,及对应的截取点坐标。

  4. 为了方便理解,用matplotlib可视化。

1. 效果图

5条线不考虑顺序的排列组合共有945种,最短路径为 ABDFCEGHIJ,距离和为449米,效果图如下:
在这里插入图片描述

如下图所示,10个街区 从A~J 用红色五角星表示,最短路径边分别为AB、DF、CE、GH、IJ
在这里插入图片描述

2. 源码

# 求最短路径问题(N个点全排列)
import math

import numpy as np

# 街区点
node = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', "J"]

# 街区点对应坐标
node_coor = [(310, 385), (360, 305), (550, 330), (440, 270), (550, 250),
             (360, 225), (305, 150), (305, 90), (440, 120), (550, 65)]


# 计算俩个坐标点的距离
def cal_dis(pt1, pt2):
    x0, y0 = pt1
    x1, y1 = pt2
    # print('\t\tdis: ', pt1, pt2, math.sqrt((math.pow((x0 - x1), 2) + math.pow((y0 - y1), 2))))
    return math.sqrt((math.pow((x0 - x1), 2) + math.pow((y0 - y1), 2)))


# 计算一条路径的总距离
def get_dis(nodes):
    # 初始化总距离
    total_dis = 0

    # 遍历路径
    for i in range(len(nodes) // 2):
        # 间隔2个点计算路径 01 23 45 67 89
        dis = cal_dis(node_coor[node.index(nodes[i * 2])], node_coor[node.index(nodes[i * 2 + 1])])
        total_dis = total_dis + dis
    return total_dis


print('递归 start--------------------')
# 递归方法解决
nodes = node.copy()
path_set = set()


# 排列组合(先2个全排列,然后每一小组排序忽略顺序~)
# 相当于C(2,10)*C(2,8)*C(2,6)*C(2,4)*C(2,2)/A(5,5) = 10*9/(2*1)*(8*7/(2*1))*(6*5/(2*1))
# *(4*3/(2*1))*(2*1/(2*1))/ (5*4*3*2*1)
# = 45*28*15*6/(5*4*3*2*1)
# = 9*7*5*3 = 945种
def permutations(position):
    if position == len(nodes) - 2:
        # print(nodes, "".join(["".join(sorted(set(nodes[i * 2] + nodes[i * 2 + 1]))) for i in range(len(nodes) // 2)]))
        path_set.add("".join(["".join(sorted(set(nodes[i * 2] + nodes[i * 2 + 1]))) for i in range(len(nodes) // 2)]))
    else:
        for index in range(position, len(nodes)):
            nodes[index], nodes[position] = nodes[position], nodes[index]
            permutations(position + 2)
            nodes[index], nodes[position] = nodes[position], nodes[index]


permutations(0)  # 全排列

print(sorted(path_set))
print('共有路径:', len(path_set))

dict_path_dis = {}
# 计算距离
for path in path_set:
    dict_path_dis[path] = get_dis(list(np.array(list(path))))

# 获取最小的value对应的key,即获取最短路径距离对应的路径
key_min = min(dict_path_dis.keys(), key=(lambda k: dict_path_dis[k]))
print('递归——Minimum path & dis: ', key_min, dict_path_dis[key_min])

print('绘图start——————————')
# 构建最短路径的街区点及坐标
min_node = list(np.array(list(key_min)))
min_path_coor = []
for i in min_node:
    min_path_coor.append(node_coor[node.index(i)])

print('min_node: ', min_node)
print('min_path_coor: ', min_path_coor)

import matplotlib.pyplot as plt

fig, ax = plt.subplots()  # 创建一个图表
x1 = [x for (x, y) in min_path_coor]
y1 = [y for (x, y) in min_path_coor]

for i, (node_name, (x, y)) in enumerate(zip(min_node, min_path_coor)):
    # 绘制坐标点及坐标点上方文字
    plt.scatter(x, y, s=120, c='red', marker='*')
    plt.text(x=x, y=y + 2, s=node_name + '(' + str(x) + ',' + str(y) + ')', ha='center', va='baseline',
             fontdict={'color': 'black',
                       'size': 8})  # 中心点上方文字
    if (i % 2 == 0):
        print(node_name, '->', min_node[i + 1])
        # print(i, i + 1, [x, min_path_coor[i + 1][0]], [y, min_path_coor[i + 1][0]])
        ax.plot([x, min_path_coor[i + 1][0]], [y, min_path_coor[i + 1][1]])  # 绘制线

plt.show()

参考

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序媛一枚~

您的鼓励是我创作的最大动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值