基于Python的A*算法实现路径规划

目录

一、A*算法

二、算法具体步骤

三、算法基本程序流程

四、算法代码实现

五、运行结果


一、A*算法

A*(A-Star)算法是P.E.Hart、N.J.Nilsson和B.Raphael等人在1986年综合Dijkstra算法和BFS算法的优点而提出来的一种非常有效的启发式路径搜索算法。

A*算法的基本思想是把到达节点的代价g(n)和从该节点到目标节点的代价h(n)结合起来对节点进行评价。

f(n)=g(n)+h(n)

式中 f(n)——从初始状态经由状态n到目标状态的代价估计;

       g(n)——在状态空间中从初始状态到状态n的实际代价;

       h(n)——从状态n到目标状态的最佳路径的估计代价。

注意:对于路径搜索问题,状态就是图中的节点,代价就是距离。

 h(n)在评价函数中起关键作用,决定了A*算法效率的高低。若h(n)为0,那么就只是g(n)有效果,A*算法就成为Dijkstra算法,这样就能寻找到最短路径。若h(n)的预算代价小于节点到目标的真实代价,那么此时A*算法同样可以达到搜索出最优路径的目的。如果h(n)越小,那么A*算法经过扩展得到的结点就会增加,此时的运行速率就会降低。若h(n)的预算距离精确到与某一节点到目标点之间的真实代价相等,那么此时A*算法就可以更快寻找到最佳路径,同时其也不会进行额外拓展,此时的速率将达到最快。若h(n)所付出的代价是要高于某一节点与目标点,那么此时可能就无法寻找到最佳路径,但是速率提升了。而另一种情况就是,若h(n)比g(n)大很多,此时g(n)的作用基本被忽略,那么算法就变成了BFS算法。

在路径规划中,我们通常用曼哈顿(Manhattan)距离或者欧式(Euclid)距离来预估费用。

二、算法具体步骤

第一步:假设起始节点是A,目标节点是B,初始化 open list 和 close list 两个表,把起始节点A放入 open list 中。

第二步:查找 open list 中的节点,假如 open list 为空,那么失败退出,说明没有找到路径。

第三步:假如 open list 不是空的,从 open list 中取出F值最小的节点n,同时放入 open list 中。

第四步:查看n是不是目标节点B。如果是,则成功退出,搜索到最优路径;如果不是,就转到第五步。

第五步:判断n节点是否有子节点,若无则转到第三步。

第六步:搜索n节点所有子节点,计算n的每一个子节点m的F(m)。

1)假如m已经在 OPEN 表中,则对刚刚计算的F(m)新值和在表中的F(m)旧值进行比较,如果新值小,说明找到一条更好的路径,则以新值代替表中的旧值。修改这个节点的父指针,将它的父指针指向当前的节点n。

2)假如m在 open list 中,则将节点m和它的子节点刚刚算出的F新值和它们以前计算的F旧值进行比较。如果新值小,说明找到一条更好的路径,则用新值代替旧值。修改这些节点的父指针,把它们的父指针指向F值小的节点。

3)假如m既不在 open list 也不在 close list,就把它们加入 open list 中。接着给m加一个指向它父结点n的指针。最后找到目标节点之后可以根据这个指针一步一步查找回来,得出最终的路径。

第七步:跳到第三步,继续循环,直到搜索出路径或者找不到退出为止。 

三、算法基本程序流程

四、算法代码实现

import time, sys
from PyQt5.QtWidgets import QDialogButtonBox, QDialog, QMainWindow, QGridLayout, QTextEdit, QLineEdit, QWidget, \
    QMessageBox, QApplication, QLabel, QPushButton, QHBoxLayout, QVBoxLayout
from PyQt5.QtCore import Qt, QTimer, QObject, pyqtSignal, QBasicTimer
from PyQt5.QtGui import QPainter, QColor, QFont, QPen
import json


class config:
    WIDTH = 20  # 地图列数
    HEIGHT = 20  # 地图行数
    blockLength = 30  # 绘制画面时每一个节点方块的边长


class point:  # 点类(每一个唯一坐标只有对应的一个实例)
    _list = []  # 储存所有的point类实例
    _tag = True  # 标记最新创建的实例是否为_list中的已有的实例,True表示不是已有实例

    def __new__(cls, x, y):  # 重写new方法实现对于同样的坐标只有唯一的一个实例
        for i in point._list:
            if i.x == x and i.y == y:
                point._tag = False
                return i
        nt = super(point, cls).__new__(cls)
        point._list.append(nt)
        return nt

    def __init__(self, x, y):
        if point._tag:
            self.x = x
            self.y = y
            self.father = None
            self.F = 0  # 当前点的评分  F=G+H
            self.G = 0  # 起点到当前节点所花费的消耗
            self.cost = 0  # 父节点到此节点的消耗
        else:
            point._tag = True

    @classmethod
    def clear(cls):  # clear方法,每次搜索结束后,将所有点数据清除,以便进行下一次搜索的时候点数据不会冲突。
        point._list = []

    def __eq__(self, T):  # 重写==运算以便实现point类的in运算
        if type(self) == type(T):
            return (self.x, self.y) == (T.x, T.y)
        else:
            return False

    def __str__(self):
        return '(%d,%d)[F=%d,G=%d,cost=%d][father:(%s)]' % (self.x, self.y, self.F, self.G, self.cost, str(
            (self.father.x, self.father.y)) if self.father != None else 'null')


class A_Search:  # 核心部分,寻路类
    def __init__(self, arg_start, arg_end, arg_map):
        self.start = arg_start  # 储存此次搜索的开始点
        self.end = arg_end  # 储存此次搜索的目的点
        self.Map = arg_map  # 一个二维数组,为此次搜索的地图引用
        self.open = []  # 开放列表:储存即将被搜索的节点
        self.close = []  # 关闭列表:储存已经搜索过的节点
        self.result = []  # 当计算完成后,将最终得到的路径写入到此属性中
        self.count = 0  # 记录此次搜索所搜索过的节点数
        self.useTime = 0  # 记录此次搜索花费的时间--在此演示中无意义,因为process方法变成了一个逐步处理的生成器,统计时间无意义。
        # 开始进行初始数据处理
        self.open.append(arg_start)

    def cal_F(self, loc):
        print('计算值:', loc)
        G = loc.father.G + loc.cost
        H = self.getEstimate(loc)
        F = G + H
        print("F=%d G=%d H=%d" % (F, G, H))
        return {'G': G, 'H': H, 'F': F}

    def F_Min(self):  # 搜索open列表中F值最小的点并将其返回,同时判断open列表是否为空,为空则代表搜索失败
        if len(self.open) <= 0:
            return None
        t = self.open[0]
        for i in self.open:
            if i.F < t.F:
                t = i
        return t

    def getAroundPoint(self, loc):  # 获取指定点周围所有可通行的点,并将其对应的移动消耗进行赋值。
        l = [(loc.x, loc.y + 1, 10), (loc.x + 1, loc.y + 1, 14), (loc.x + 1, loc.y, 10), (loc.x + 1, loc.y - 1, 14),
             (loc.x, loc.y - 1, 10), (loc.x - 1, loc.y - 1, 14), (loc.x - 1, loc.y, 10), (loc.x - 1, loc.y + 1, 14)]
        for i in l[::-1]:
            if i[0] < 0 or i[0] >= config.HEIGHT or i[1] < 0 or i[1] >= config.WIDTH:
                l.remove(i)
        nl = []
        for i in l:
            if self.Map[i[0]][i[1]] == 0:
                nt = point(i[0], i[1])
                nt.cost = i[2]
                nl.append(nt)
        return nl

    def addToOpen(self, l,
                  father):  # 此次判断的点周围的可通行点加入到open列表中,如此点已经在open列表中则对其进行判断,如果此次路径得到的F值较之之前的F值更小,则将其父节点更新为此次判断的点,同时更新F、G值。
        for i in l:
            if i not in self.open:
                if i not in self.close:
                    i.father = father
                    self.open.append(i)
                    r = self.cal_F(i)
                    i.G = r['G']
                    i.F = r['F']
            else:
                tf = i.father
                i.father = father
                r = self.cal_F(i)
                if i.F > r['F']:
                    i.G = r['G']
                    i.F = r['F']
                # i.father=father
                else:
                    i.father = tf

    def getEstimate(self, loc):  # H :从点loc移动到终点的预估花费
        return (abs(loc.x - self.end.x) + abs(loc.y - self.end.y)) * 10

    def DisplayPath(self):  # 在此演示中无意义
        print('搜索花费的时间:%.2fs.迭代次数%d,路径长度:%d' % (self.useTime, self.count, len(self.result)))
        if self.result != None:
            for i in self.result:
                self.Map[i.x][i.y] = 8
            for i in self.Map:
                for j in i:
                    if j == 0:
                        print('%s' % '□', end='')
                    elif j == 1:
                        print('%s' % '▽', end='')
                    elif j == 8:
                        print('%s' % '★', end='')
                print('')
        else:
            print('搜索失败,无可通行路径')

    def process(self):  # 使用yield将process方法变成一个生成器,可以逐步的对搜索过程进行处理并返回关键数据
        while True:
            self.count += 1
            tar = self.F_Min()  # 先获取open列表中F值最低的点tar
            if tar == None:
                self.result = None
                self.count = -1
                break
            else:
                aroundP = self.getAroundPoint(tar)  # 获取tar周围的可用点列表aroundP
                self.addToOpen(aroundP, tar)  # 把aroundP加入到open列表中并更新F值以及设定父节点
                self.open.remove(tar)  # 将tar从open列表中移除
                self.close.append(tar)  # 已经迭代过的节点tar放入close列表中
                if self.end in self.open:  # 判断终点是否已经处于open列表中
                    e = self.end
                    self.result.append(e)
                    while True:
                        e = e.father
                        if e == None:
                            break
                        self.result.append(e)
                    yield (tar, self.open, self.close)
                    break

            # self.repaint()
            # print('返回')
            yield (tar, self.open, self.close)
            time.sleep(5)  # 暂停
        # self.useTime = time2 - time1


class GameBoard(QMainWindow):  # 可视化类,pyqt5进行编写。
    def __init__(self):
        print('初始化地图...')
        self.Map = []
        for i in range(config.HEIGHT):
            col = []
            for j in range(config.WIDTH):
                col.append(0)
            self.Map.append(col)
        self.startPoint = None
        self.endPoint = None
        self.search = None
        self.centerTimer = None
        self.yi = None
        self.special = None
        self.displayFlush = False
        super().__init__()
        print('初始化UI...')
        self.initUI()

    def initUI(self):
        # 开始初始化UI部分
        # 创建UI控件
        self.label_tips = QLabel(
            "<p style='color:green'>使用说明:</p>右键单击格子选定起始点,左键格子选定格子为墙壁或删除墙壁。\n<p style='color:green'>颜色说明:</p>\n黄色代表起点,绿色代表终点,黑色代表墙壁,红色代表待搜索的open列表,灰色代表已搜索过的close列表,蓝色代表当前搜索到的路径",
            self)
        self.label_display = QLabel("", self)
        self.button_start = QPushButton("开始搜索", self)
        self.button_clearSE = QPushButton("重选起始点", self)
        self.button_clearWall = QPushButton("清空地图墙壁", self)
        self.button_saveMap = QPushButton("保存地图", self)
        self.button_loadMap = QPushButton("加载地图", self)

        # 设置控件属性
        self.label_tips.setWordWrap(True)
        self.label_display.setWordWrap(True)
        # 设置控件样式
        self.label_display.setStyleSheet("border:1px solid black")
        self.label_display.setAlignment(Qt.AlignLeft)
        self.label_display.setAlignment(Qt.AlignTop)
        # 设置控件的尺寸和位置
        self.label_tips.resize(200, 150)
        self.button_saveMap.resize(80, 30)
        self.button_loadMap.resize(80, 30)
        self.label_display.resize(200, 300)

        self.label_tips.move(100 + (config.WIDTH - 1) * config.blockLength, 0)
        self.label_display.move(100 + (config.WIDTH - 1) * config.blockLength, 400)
        self.button_start.move(100 + (config.WIDTH - 1) * config.blockLength, 200)
        self.button_clearSE.move(100 + (config.WIDTH - 1) * config.blockLength, 250)
        self.button_clearWall.move(100 + (config.WIDTH - 1) * config.blockLength, 300)
        self.button_saveMap.move(100 + (config.WIDTH - 1) * config.blockLength, 350)
        self.button_loadMap.move(200 + (config.WIDTH - 1) * config.blockLength, 350)
        # 给控件绑定事件
        self.button_start.clicked.connect(self.button_StartEvent)
        self.button_clearSE.clicked.connect(self.button_Clear)
        self.button_clearWall.clicked.connect(self.button_Clear)
        self.button_saveMap.clicked.connect(self.button_SaveMap)
        self.button_loadMap.clicked.connect(self.button_LoadMap)
        # UI初始化完成
        self.setGeometry(0, 0, 150 + (config.WIDTH * config.blockLength - config.blockLength) + 200,
                         150 + (config.HEIGHT * config.blockLength - config.blockLength))
        self.setMinimumSize(150 + (config.WIDTH * config.blockLength - config.blockLength) + 200,
                            150 + (config.HEIGHT * config.blockLength - config.blockLength))
        self.setMaximumSize(150 + (config.WIDTH * config.blockLength - config.blockLength) + 200,
                            150 + (config.HEIGHT * config.blockLength - config.blockLength))
        self.setWindowTitle('A*搜索')
        self.show()

    def addDisplayText(self, text):
        if self.displayFlush:
            self.label_display.setText(text + '\n')
            self.displayFlush = False
        else:
            self.label_display.setText(self.label_display.text() + text + '\n')

    def mousePressEvent(self, event):
        x, y = event.x() - 50, event.y() - 50
        x = x // config.blockLength
        y = y // config.blockLength
        if x >= 0 and x < config.WIDTH and y >= 0 and y < config.HEIGHT:
            if event.button() == Qt.LeftButton:
                if (x, y) != self.startPoint and (x, y) != self.endPoint:
                    self.Map[y][x] = (1 if self.Map[y][x] == 0 else 0)
            if event.button() == Qt.RightButton:
                if self.Map[y][x] == 0:
                    if self.startPoint == None:
                        self.startPoint = (x, y)
                        self.addDisplayText('添加了一个起点:(%d,%d)' % (x, y))
                    elif self.endPoint == None and self.startPoint != (x, y):
                        self.endPoint = (x, y)
                        self.addDisplayText('添加了一个终点:(%d,%d)' % (x, y))
            self.repaint()

    def button_StartEvent(self):
        sender = self.sender()
        print(sender)
        if self.startPoint != None and self.endPoint != None:
            if self.centerTimer == None:
                self.centerTimer = QBasicTimer()
            self.button_start.setEnabled(False)
            self.button_clearSE.setEnabled(False)
            self.button_clearWall.setEnabled(False)
            self.centerTimer.start(200, self)
            self.search = A_Search(point(self.startPoint[1], self.startPoint[0]),
                                   point(self.endPoint[1], self.endPoint[0]), self.Map)
            self.yi = self.search.process()
            self.addDisplayText('开始进行搜索')

    def button_SaveMap(self):
        with open('map.txt', 'w') as f:
            f.write(json.dumps(self.Map))
            self.addDisplayText('地图保存成功-->map.txt')

    # else:
    # self.addDisplayText('地图保存失败')
    def button_LoadMap(self):
        try:
            with open('map.txt', 'r') as f:
                self.Map = json.loads(f.read())
                config.HEIGHT = len(self.Map)
                config.WIDTH = len(self.Map[0])
                self.addDisplayText('地图加载成功')
                self.repaint()
        except Exception as e:
            print('失败', e, type(e))
            if type(e) == FileNotFoundError:
                self.addDisplayText('地图加载失败:地图文件不存在')
            elif type(e) == json.decoder.JSONDecodeError:
                self.addDisplayText('地图加载失败:错误的地图文件')

    def button_Clear(self):
        sender = self.sender()
        print(self.button_clearSE, type(self.button_clearSE))
        if sender == self.button_clearSE:
            self.startPoint = None
            self.endPoint = None
            self.repaint()
            self.addDisplayText('清空起始点')
        elif sender == self.button_clearWall:
            for i in range(len(self.Map)):
                for j in range(len(self.Map[i])):
                    self.Map[i][j] = 0
            self.repaint()
            self.addDisplayText('清空所有墙壁')

    def paintEvent(self, event):
        qp = QPainter()
        qp.begin(self)
        self.drawBoard(event, qp)
        qp.end()

    def drawBoard(self, event, qp):
        self.drawMap(qp)

    def drawMap(self, qp):  # 画面绘制方法,每次地图有所改动都将重绘
        time1 = time.time()
        if self.search != None:
            if self.special != None:
                e = self.special[0]
                path = [e]
                while True:
                    e = e.father
                    if e != None:
                        path.append(e)
                    else:
                        break
            else:
                path = None
            pen = QPen(QColor(0, 0, 0), 1, Qt.SolidLine)
            qp.setPen(pen)
            for i in range(len(self.Map)):
                for j in range(len(self.Map[i])):
                    wordTag = False
                    if i == self.search.start.x and j == self.search.start.y:
                        qp.setBrush(QColor(255, 255, 0))
                    elif i == self.search.end.x and j == self.search.end.y:
                        qp.setBrush(QColor(100, 200, 50))
                    else:
                        if self.Map[i][j] == 0:
                            tagx = True
                            if path:
                                for k in path:
                                    if k.x == i and k.y == j:
                                        tagx = False
                                        qp.setBrush(QColor(0, 100, 255))
                            if tagx:
                                if self.special != None:
                                    if i == self.special[0].x and j == self.special[0].y:
                                        qp.setBrush(QColor(0, 255, 0))
                                    else:
                                        tag = True
                                        for k in self.special[1]:
                                            if k.x == i and k.y == j:
                                                tag = False
                                                wordTag = True
                                                word = str(k.F)

                                                qp.setBrush(QColor(150, 0, 0))
                                                break
                                            else:
                                                qp.setBrush(QColor(220, 220, 220))
                                        if tag:
                                            for k in self.special[2]:
                                                if k.x == i and k.y == j:
                                                    qp.setBrush(QColor(150, 150, 150))
                                                    break
                                                else:
                                                    qp.setBrush(QColor(220, 220, 220))
                                else:
                                    qp.setBrush(QColor(220, 220, 220))
                        elif self.Map[i][j] == 1:
                            qp.setBrush(QColor(0, 0, 0))
                        else:
                            qp.setBrush(QColor(255, 0, 0))
                    qp.drawRect(50 + j * config.blockLength, 50 + i * config.blockLength, config.blockLength,
                                config.blockLength)
                    if wordTag:
                        qp.setFont(QFont('楷体', 5, QFont.Light))
                        qp.drawText(50 + 10 + j * config.blockLength, 50 + 10 + i * config.blockLength, word)
                        wordTag = False
        # time.sleep(20)
        else:
            for i in range(len(self.Map)):
                for j in range(len(self.Map[i])):
                    if (j, i) == self.startPoint:
                        qp.setBrush(QColor(255, 255, 0))
                    elif (j, i) == self.endPoint:
                        qp.setBrush(QColor(100, 200, 50))
                    else:
                        if self.Map[i][j] == 0:
                            qp.setBrush(QColor(220, 220, 220))
                        elif self.Map[i][j] == 1:
                            qp.setBrush(QColor(0, 0, 0))
                        else:
                            qp.setBrush(QColor(255, 0, 0))

                    qp.drawRect(50 + j * config.blockLength, 50 + i * config.blockLength, config.blockLength,
                                config.blockLength)
        time2 = time.time()

    # time.sleep(20)
    # print('绘制时间:',time2-time1)
    def timerEvent(self, e):
        try:
            data = next(self.yi)
        except Exception as e:
            self.addDisplayText('搜索结束:')
            print('搜索结束!')
            if self.search.result == None:
                self.addDisplayText('未找到可行路径')
                print('搜索结束!')
            else:
                self.addDisplayText('总计搜索节点数:%d' % self.search.count)
                self.addDisplayText('最终路径长度:%d' % len(self.search.result))
            self.centerTimer.stop()
            self.search = None
            self.yi = None
            self.special = None
            point.clear()
            self.button_start.setEnabled(True)
            self.button_clearSE.setEnabled(True)
            self.button_clearWall.setEnabled(True)
            self.displayFlush = True
        else:
            self.special = data
            self.repaint()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = GameBoard()
    sys.exit(app.exec_())

五、运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/904882.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

和鲸科技 CEO 范向伟受邀揭牌启动南京大学 2024 级大学生人工智能素养大赛

2024 年 10 月 26 日&#xff0c;南京大学第十九届读书节在仙林校区图书馆举行开幕仪式。中国科学院院士、南京大学校长谈哲敏&#xff0c;校党委常委、副校长索文斌&#xff0c;原副校长、关工委主任闵铁军出席仪式&#xff0c;南京大学相关学院和职能部处负责人&#xff0c;以…

有序序列合并(c语言)

代码实例 int main() {int n 0;int m 0;scanf("%d %d", &n, &m);//n输入第一个升序数组中的元素个数//m输入第二个升序数组中的元素个数//创建数组//arr1为n对应的数组int arr1[1000];//arr2为m对应的数组int arr2[1000];//arr3为数组1与数组2结合后的数组…

Java审计对比工具JaVers使用

最近有个需求&#xff0c;需要将页面的内容生成excel或者word文档&#xff0c;而且每次的修改都需要生成新的版本&#xff0c;同时需要记录每次修改变化的内容。我们会把每次的修改的内容提交赋值给一个java对象&#xff0c;同时存储到数据库一条新数据&#xff0c;对应数据表一…

知识图谱:连接实体与关系的语义网络

知识图谱作为人工智能领域的核心技术之一&#xff0c;是一种通过三元组&#xff08;实体关系属性&#xff09;形式&#xff0c;结构化表达实体间关系的语义网络。这种网络不仅嵌入了丰富的语义和逻辑&#xff0c;还遵循一定的规则&#xff0c;使其成为人类进行推理、预测和分类…

免费PDF页面提取小工具

下载地址 https://download.csdn.net/download/woshichenpi/89922797 使用说明&#xff1a;PDF页面提取工具 1. 启动应用程序 双击程序的启动图标或者通过命令行运行程序。 2. 选择PDF文件 在应用程序窗口中找到“选择PDF”按钮并点击它。在弹出的文件选择对话框中&#x…

法律智能助手:开源NLP系统助力法律文件高效审查与检索

一、系统概述 思通数科AI平台是一款融合了自然语言处理和多标签分类技术的开源智能文档分类工具&#xff0c;特别适用于法律行业。平台采用深度学习的BERT模型来进行特征提取与关系抽取&#xff0c;实现了精准的文档分类和检索。用户可以在线训练和标注数据&#xff0c;使系统…

-XSS-

链接 https://github.com/do0dl3/xss-labs 搭建过程非常容易的 搭建好之后&#xff0c;就可以点击图片开始闯关了 第一关--JS弹窗函数alert() 显示payload的长度是4 level1.php?nametest level1.php?nametest1 发现只要改变name的值就显示什么在页面上 没有什么过滤的 …

Python | Leetcode Python题解之第522题最长特殊序列II

题目&#xff1a; 题解&#xff1a; class Solution:def findLUSlength(self, strs: List[str]) -> int:def is_subseq(s: str, t: str) -> bool:pt_s pt_t 0while pt_s < len(s) and pt_t < len(t):if s[pt_s] t[pt_t]:pt_s 1pt_t 1return pt_s len(s)ans …

VBto Converter是一款功能强大的工具,可让您快速轻松地将Microsoft Visual Basic 6.0项目转换

VBto Converter是一款功能强大的工具&#xff0c;可让您快速轻松地将Microsoft Visual Basic 6.0项目转换 1、简介2、官方网站3、本站下载&#xff08;已汉化&#xff09; 1、简介 VBto Converter V2.90 版本&#xff0c;是一款功能强大的工具&#xff0c;可让您快速轻松地将M…

勒索软件通过易受攻击的 Cyber​​Panel 实例攻击网络托管服务器

一个威胁行为者&#xff08;或可能多个&#xff09;使用 PSAUX 和其他勒索软件攻击了大约 22,000 个易受攻击的 Cyber​​Panel 实例以及运行该实例的服务器上的加密文件。 PSAUX 赎金记录&#xff08;来源&#xff1a;LeakIX&#xff09; Cyber​​Panel 漏洞 Cyber​​Pane…

创新业态下金融头部机构在 FICC 平台建设上的思考与实践

近年来&#xff0c;FICC 投资交易呈现活跃多元态势&#xff0c;创新转型稳步推进。FICC 平台电子化方兴未艾&#xff0c;是机构提升服务效率和质量的一大着力点。因此&#xff0c;在 FICC 平台建设上&#xff0c;许多机构都进行了深入研究&#xff0c;积累了丰富的实践经验。 …

RedisIO多路复用

一、多路复用要解决的问题: 并发多客户端连接&#xff0c;在多路复用之前的处理方案是同步阻塞网络IO模型&#xff0c;这种模型的特点就是用一个进程来处理一个网络连接。优点在于比较简单&#xff0c;缺点在于性能较差&#xff0c;每个用户请求到来都得占用一个进程来处理&am…

XML解析小坑记录[正则表达式解析]

一、问题描述 在做 SSO 单点登录时( 认证中为CAS服务对接 )。在完成对用户ticket票根校验后&#xff0c;返回了用户信息有关 XML 数据片段&#xff0c;例如下&#xff1a; <cas:serviceResponse xmlns:cas"http://www.xxx.xx/xx/cas"><cas:authentication…

人工智能与伦理:我们应该如何平衡科技与人性?

内容概要 在这个瞬息万变的时代&#xff0c;人工智能的迅猛发展让我们面对前所未有的伦理困境。科技进步带来了便利&#xff0c;但同时也亟需我们反思如何对待人性。尤其是在实现算法透明性时&#xff0c;我们要确保每一个决策背后都能被理解与追溯&#xff0c;这不仅是对技术…

electron展示下载进度条

我们使用electron下载文件时&#xff0c;会发现不像浏览器一样会有地方展示下载进度&#xff0c;这导致下载一些大文件时不知道下载进度到哪里了 下面我们通过electron提供的will-download监听和element-plus中的ElNotification和ElProgress组件实现这一功能 实现逻辑 触发…

【算法】(Python)回溯算法

回溯算法&#xff1a; 回溯算法是一种算法思想。采用“深度优先搜索&#xff08;dfs&#xff0c;depth first search&#xff09;”。采用“尝试”和“回溯”的策略。尝试搜索所有可能的解决方案&#xff0c;遇到不满足条件的撤销选择、回退到回溯点&#xff08;满足回溯条件的…

音视频入门基础:FLV专题(18)——Audio Tag简介

一、引言 根据《video_file_format_spec_v10_1.pdf》第75页&#xff0c;如果某个Tag的Tag header中的TagType值为8&#xff0c;表示该Tag为Audio Tag&#xff1a; 这时StreamID之后紧接着的就是AudioTagHeader&#xff0c;也就是说这时Tag header之后的就是AudioTagHeader&…

探索Python终端美化的终极利器:Rich库

文章目录 &#x1f680; 探索Python终端美化的终极利器&#xff1a;Rich库第一部分&#xff1a;背景介绍第二部分&#xff1a;Rich库是什么&#xff1f;第三部分&#xff1a;如何安装Rich库&#xff1f;第四部分&#xff1a;Rich库的简单函数使用方法第五部分&#xff1a;结合场…

【Java笔记】1-JDK/JRE/JVM是个啥?

JDK、JRE、JVM可以说是入门必须了解的三个词汇 先说全称 JDK&#xff1a;Java Development Kit&#xff0c;Java开发工具包 JRE&#xff1a;Java Runtime Environment&#xff0c;Java运行环境 JVM&#xff1a;Java Virtual Machine&#xff0c;Java虚拟机 再说关系 JVM⊆J…

视觉目标检测标注xml格式文件解析可视化 - python 实现

视觉目标检测任务&#xff0c;通常用 labelimage标注&#xff0c;对应的标注文件为xml。 该示例来源于开源项目&#xff1a;https://gitcode.com/DataBall/DataBall-detections-100s/overview 读取 xml 标注文件&#xff0c;并进行可视化示例如下&#xff1a; #-*-coding:ut…