A*算法学习笔记

news2024/11/20 18:43:27

1 算法思路

1、Dijkstra算法与A*算法

(1)Dijkstra算法(贪心策略 + 优先队列):

  • 集合S:已确定的顶点集合,初始只含源点s。

  • 集合T:尚未确定的顶点集合。

  • 算法反复从集合T中选择当前到源点s最近的顶点u,将u加入集合S,然后对所有从u发出的边进行松弛操作。

  • 算法步骤:维护一个优先队列,将集合T中的顶点到源点s的距离,作为这些点的优先级,距离越低,优先级越高。那么只要从优先队列中取出队首元素,即可获得当前离源点s最近的顶点。

附注:松弛操作:若d(u) + w(u, v) < d(v),就更新d(v) = d(u) + w(u, v),其中w(u, v)表示边的权重,d(u)表示从顶点u到达源点s的最短距离(目前已知)

(2)A*算法(启发式搜索):

  • F = G + H,G表示从起点到某中间节点的移动代价,启发函数H(Heuristic)表示从某中间节点到终点的估算移动代价,当H始终为0,算法就退化为Dijkstra算法。

    • 曼哈顿距离

    • 欧式距离

  • 数据结构设计:

节点数据类型:

class Node
{
 public:
  Node(int x, int y, bool c, Node* fa = nullptr) : _x(x), _y(y), _father(fa), _is_closed(c){};
  Node() = default;
  ~Node() = default;

  int get_f() { return _f; }
  void set_f(int f) { _f = f; }
  int get_g() { return _g; }
  void set_g(int g) { _g = g; }
  int get_h() { return _h; }
  void set_h(int h) { _h = h; }
  int get_x() { return _x; }
  int get_y() { return _y; }
  Node* get_father() { return _father; }
  void set_father(Node* father) { _father = father; }
  int get_state() { return _state; }
  void set_state(int state) { _state = state; }
  void set_is_closed(bool flag) { _is_closed = flag; }
  bool get_is_closed() { return _is_closed; }
  void set_is_in_openlist(bool flag) { _is_in_openlist = flag; }
  bool get_is_in_openlist() { return _is_in_openlist; }

 private:
  int _x;                ///< Node x coordinate
  int _y;                ///< Node y coordinate
  Node* _father;         ///< Previous node of the node
  int _g;                ///< Cost g
  int _h;                ///< Cost h
  int _f;                ///< Cost f
  int _state;            ///< Node state(OBSTACLE / ACCESS / RESULT_NODE)
  bool _is_closed;       ///< Whether the node is closed
  bool _is_in_openlist;  ///< Whether the node is in the openlist
};

网格数据类型:

class GridMap
{
 public:
  GridMap() = default;
  ~GridMap() = default;

  void constructMap();  ///< Construct map and node information
  void printMap();      < Print map information
  std::vector<std::vector<Node>> get_map() { return _map; }

 private:
  std::vector<std::vector<Node>> _map;
};
  • 算法步骤:

图1 算法流程步骤

2 实验结果

(1)测试用例1:起点固定为左下角,终点固定为右上角。

图2 测试用例1整体运行结果截图

原始Map(*为可访问的节点,$为障碍节点),图大小为25 x 25(由MAP_SIZE参数进行控制),其中有30%的节点随机作为障碍节点(由OBSTACLE_RATIO参数进行控制):

图2 测试用例1中随机生成的25*25个节点大小的初始网格

搜索到的路径为: (24, 0) (24, 1) (23, 2) (24, 3) (23, 4) (24, 5) (24, 6) (24, 7) (24, 8) (23, 9) (22, 10) (21, 11) (20, 12) (19, 13) (18, 14) (17, 14) (16, 15) (15, 16) (14, 16) (13, 17) (12, 18) (11, 19) (10, 20) (9, 21) (8, 21) (7, 22) (6, 22) (5, 22) (4, 22) (3, 23) (2, 23) (1, 24) (0, 24) 结果Map(*为可访问的节点,$为障碍节点,#为结果路径节点):

图3 测试用例1中红色线为A*算法搜索到的路径

(2)测试用例2: 原始图(*为可访问的节点,$为障碍节点),图大小为25 x 25,其中有30%的节点随机作为障碍节点:

图4 测试用例2中随机生成的25*25个节点大小的初始网格

 结果路径:(24, 0) (24, 1) (24, 2) (24, 3) (24, 4) (23, 5) (23, 6) (24, 7) (23, 8) (24, 9) (24, 10) (23, 11) (22, 12) (21, 13) (20, 14) (21, 15) (22, 16) (21, 17) (20, 18) (19, 19) (18, 20) (17, 21) (16, 22) (15, 22) (14, 22) (13, 22) (12, 22) (11, 23) (10, 23) (9, 23) (8, 23) (7, 23) (6, 23) (5, 23) (4, 23) (3, 23) (2, 23) (1, 24) (0, 24) 结果Map(*为可访问的节点,$为障碍节点,#为结果路径节点):

图5 测试用例2中红色线表示A*算法搜索到的路径

 (3)测试用例3:测试路径不存在的情况,从左下角起点无法到达右上角终点,则会输出“Path does not exists”。

图6 测试用例3中路径不存在的情况

3 代码示例

(1)include/project/AStar.hpp:A*算法步骤的主要接口和参数定义,其中节点类型有三种:ACCESS表示可通行节点,用"* "表示;OBSTACLE表示障碍节点,用"$ "表示;RESULT_NODE表示搜索到的结果节点,用"# "表示。节点间上下左右通行的代价(DIRECT_COST)初始化为10,斜向通行的代价(OBLIQUE_COST)初始化为15;网格大小(MAP_SIZE)初始化为25;随机障碍节点占所有节点的比例(OBSTACLE_RATIO)初始化为0.3。

/**
 * @file AStar.hpp
 * @author Qinyi Deng (remoa@qq.com)
 * @brief
 * @version 0.1
 * @date 2023-02-09
 *
 * @copyright Copyright (c) 2023 SCUT
 *
 */
#pragma once
#include <queue>
#include <vector>

const int OBSTACLE = 1;                  ///< Used in _state attribute in node
const int ACCESS = 2;                    ///< Used in _state attribute in node
const int RESULT_NODE = 3;               ///< Used in _state attribute in node
const int INITIALIZE_COST = 0x7fffffff;  ///< Initialize cost
const int MAP_SIZE = 25;                 ///< 25 * 25 's map
const int DIRECT_COST = 10;              ///< The path to walk up, down, left and right, costs 10
const int OBLIQUE_COST = 15;             ///< The path of walking obliquely, costs 15
const double OBSTACLE_RATIO = 0.3;       < Scale of obstacle nodes in the figure

namespace assignment2 {
/**
 * @brief Node class information
 */
class Node
{
 public:
  Node(int x, int y, Node* fa = nullptr) : _x(x), _y(y), _father(fa){};
  Node() = default;
  ~Node() = default;

  int get_f() { return _f; }
  void set_f(int f) { _f = f; }
  int get_g() { return _g; }
  void set_g(int g) { _g = g; }
  int get_h() { return _h; }
  void set_h(int h) { _h = h; }
  int get_x() { return _x; }
  int get_y() { return _y; }
  Node* get_father() { return _father; }
  void set_father(Node* father) { _father = father; }
  int get_state() { return _state; }
  void set_state(int state) { _state = state; }
  void set_is_closed(bool flag) { _is_closed = flag; }
  bool get_is_closed() { return _is_closed; }
  void set_is_in_openlist(bool flag) { _is_in_openlist = flag; }
  bool get_is_in_openlist() { return _is_in_openlist; }
  bool operator<(const Node& a) const { return this->_f < a._f; }

 private:
  int _x;                ///< Node x coordinate
  int _y;                ///< Node y coordinate
  Node* _father;         ///< Previous node of the node
  int _g;                ///< Cost g
  int _h;                ///< Cost h
  int _f;                ///< Cost f
  int _state;            ///< Node state(OBSTACLE / ACCESS / RESULT_NODE)
  bool _is_closed;       ///< Whether the node is closed
  bool _is_in_openlist;  ///< Whether the node is in the openlist
};

///
/**
 * @brief GridMap class information
 */
class GridMap
{
 public:
  GridMap() = default;
  ~GridMap() = default;
  std::vector<std::vector<Node>> _map;

  void constructMap();  ///< Construct map and node information
  void printMap();      < Print map information
};

class AStar
{
 public:
  AStar() = default;
  ~AStar() = default;

  std::priority_queue<Node*> _openlist;
  bool aStar(GridMap* grid_map);                    < A star main algorithm
  bool checkBound(int x, int y);                    < Check out of bounds
  bool addOpenlist(GridMap* grid_map, Node* node);  < Add Nodes to openlist
  bool checkInOpenList(GridMap* grid_map, Node* node, int x_offset, int y_offset,
                       int cost);     < Judge whether nodes in openlist and update cost function
  void printPath(GridMap* grid_map);  < Print path node information
};

};  // namespace assignment2

(2)src/AStar.cpp:A*算法的主要算法步骤实现。

/**
 * @file AStar.cpp
 * @author Qinyi Deng (remoa@qq.com)
 * @brief
 * @version 0.1
 * @date 2023-02-09
 *
 * @copyright Copyright (c) 2023 SCUT
 *
 */
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <random>

#include "project/AStar.hpp"

using namespace assignment2;

/**
 * @brief construct map and node information
 */
void GridMap::constructMap()
{
  std::vector<Node> tmp_list(MAP_SIZE);
  _map.resize(MAP_SIZE, tmp_list);
  std::default_random_engine e;
  std::uniform_int_distribution<int> u(0, MAP_SIZE);
  e.seed(time(0));
  for (int i = 0; i < MAP_SIZE; i++) {
    for (int j = 0; j < MAP_SIZE; j++) {
      _map[i][j] = Node(i, j);
      if (u(e) % MAP_SIZE < (int) (MAP_SIZE * OBSTACLE_RATIO)) {
        _map[i][j].set_state(OBSTACLE);
      } else {
        _map[i][j].set_state(ACCESS);
      }
      _map[i][j].set_h((i + MAP_SIZE - 1 - j) * DIRECT_COST);
      _map[i][j].set_g(INITIALIZE_COST);
      _map[i][j].set_f(INITIALIZE_COST);
      _map[i][j].set_is_in_openlist(false);
      _map[i][j].set_is_closed(false);
    }
  }
  _map[MAP_SIZE - 1][0].set_state(ACCESS);
  _map[MAP_SIZE - 1][0].set_g(0);
  _map[MAP_SIZE - 1][0].set_f(_map[MAP_SIZE - 1][0].get_h());
  _map[0][MAP_SIZE - 1].set_state(ACCESS);
}

/**
 * @brief print map information
 */
void GridMap::printMap()
{
  for (int i = 0; i < MAP_SIZE; i++) {
    for (int j = 0; j < MAP_SIZE; j++) {
      if (_map[i][j].get_state() == ACCESS) {
        std::cout << "* ";
      } else if (_map[i][j].get_state() == OBSTACLE) {
        std::cout << "$ ";
      } else if (_map[i][j].get_state() == RESULT_NODE) {
        std::cout << "# ";
      }
    }
    std::cout << std::endl;
  }
}

/**
 * @brief A star algorithm
 * @return true find path
 * @return false path does not exist.
 */
bool AStar::aStar(GridMap* grid_map)
{
  Node* start = &(grid_map->_map[MAP_SIZE - 1][0]);
  _openlist.push(start);
  while (!_openlist.empty()) {
    Node* head = _openlist.top();
    _openlist.pop();
    if (addOpenlist(grid_map, head)) {
      return true;
    }
    head->set_is_closed(true);
    head->set_is_in_openlist(false);
  }
  return false;
}

/**
 * @brief Check out of boundary
 * @param x x coordinate of node
 * @param y y coordinate of node
 * @return true Within the boundary
 * @return false Outside the boundary
 */
bool AStar::checkBound(int x, int y)
{
  if (x >= 0 && x < MAP_SIZE && y >= 0 && y < MAP_SIZE) {
    return true;
  }
  return false;
}

/**
 * @brief judge whether nodes in openlist and update cost function
 * @param grid_map grid_map
 * @param node input node
 * @param x_offset x_offset
 * @param y_offset y_offset
 * @param cost cost(DIRECT_COST / OBLIQUE_COST)
 * @return true Find path
 * @return false We haven't found the path yet
 */
bool AStar::checkInOpenList(GridMap* grid_map, Node* node, int x_offset, int y_offset, int cost)
{
  if (checkBound(node->get_x() + x_offset, node->get_y() + y_offset)) {
    Node* node_ptr = &(grid_map->_map[node->get_x() + x_offset][node->get_y() + y_offset]);
    if (node_ptr->get_state() != OBSTACLE) {
      if ((node_ptr->get_is_in_openlist() && node->get_g() + cost < node_ptr->get_g())
          || (!node_ptr->get_is_in_openlist() && !node_ptr->get_is_closed())) {
        node_ptr->set_g(node->get_g() + cost);
        node_ptr->set_f(node_ptr->get_h() + node_ptr->get_g());
        node_ptr->set_father(node);
        if (!node_ptr->get_is_in_openlist()) {
          node_ptr->set_is_in_openlist(true);
          _openlist.push(node_ptr);
          if (node_ptr->get_x() == 0 && node_ptr->get_y() == MAP_SIZE - 1) {
            return true;
          }
        }
      }
    }
  }
  return false;
}

/**
 * @brief Add Nodes to openlist
 * @param grid_map grid_map
 * @param node node information
 * @return true find path
 * @return false We haven't found the path yet
 */
bool AStar::addOpenlist(GridMap* grid_map, Node* node)
{
  bool flag1 = false;
  bool flag2 = false;
  for (int i = -1; i <= 1; i++) {
    for (int j = -1; j <= 1; j++) {
      if (i == 0 && j == 0) {
        continue;
      }
      if (abs(i) + abs(j) == 1) {
        flag1 = checkInOpenList(grid_map, node, i, j, DIRECT_COST);
      } else {
        flag2 = checkInOpenList(grid_map, node, i, j, OBLIQUE_COST);
      }
      if (flag1 || flag2) {
        return true;
      }
    }
  }
  return false;
}

/**
 * @brief print path node information
 * @param grid_map grid_map
 */
void AStar::printPath(GridMap* grid_map)
{
  Node* end_ptr = &(grid_map->_map[0][MAP_SIZE - 1]);
  std::vector<Node*> path_list;
  while (end_ptr != nullptr) {
    end_ptr->set_state(RESULT_NODE);
    path_list.push_back(end_ptr);
    end_ptr = end_ptr->get_father();
  }
  std::cout << "The result path is as follows:" << std::endl;
  for (int i = path_list.size() - 1; i >= 0; i--) {
    std::cout << "(" << path_list[i]->get_x() << ", " << path_list[i]->get_y() << ")"
              << " ";
  }
  std::cout << std::endl;
}

(3)test/src/AStar_test.cpp:测试用例生成和打印运行结果。

/**
 * @file myMap_test.cpp
 * @author Qinyi Deng (remoa@qq.com)
 * @brief
 * @version 0.1
 * @date 2023-02-09
 *
 * @copyright Copyright (c) 2023 SCUT
 *
 */
#include <gtest/gtest.h>

#include "project/AStar.hpp"

using namespace assignment2;

TEST(TmpAddTest, CheckValues)
{
  // ASSERT_EQ(tmp::add(1, 2), 3);
  EXPECT_TRUE(true);
}

int main(int argc, char** argv)
{
  AStar* astar = new AStar();
  GridMap* map = new GridMap();
  map->constructMap();
  map->printMap();
  if (astar->aStar(map)) {
    astar->printPath(map);
    std::cout << "The map and its result path are as follows:" << std::endl;
    map->printMap();
  } else {
    std::cout << "Path does not exist!\n";
  }
  return 0;
  // ::testing::InitGoogleTest(&argc, argv);
  // return RUN_ALL_TESTS();
}

(4).clang-format的配置文件,对代码进行格式化。首先在Settings.json中将Clang_format_style配置修改为file。

图7 vscode中配置clang-format

 然后通过ctrl+shift+p在VSCode中修改配置文件settings.json,加入以下代码:

"clang-format.assumeFilename": "./.clang-format",
"[cpp]": {
    "editor.defaultFormatter": "xaver.clang-format"
},

最后添加.clang-format文件:

---
Language:        Cpp
BasedOnStyle:  Google

ColumnLimit: 100
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:   
  AfterClass:            true
  AfterControlStatement: false
  AfterEnum:             true
  AfterFunction:         true
  AfterNamespace:        false
  AfterObjCDeclaration:  false
  AfterStruct:           true
  AfterUnion:            true
  AfterExternBlock:      false
  BeforeCatch:           false
  BeforeElse:            false
  IndentBraces:          false
  SplitEmptyFunction:    true
  SplitEmptyRecord:      true
  SplitEmptyNamespace:   true
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
DerivePointerAlignment: false
SpaceAfterCStyleCast: true
Standard: c++20

(5).clang-tidy的配置文件:对代码进行静态分析,检查违反代码规范的代码模式。首先,在vscode下载好插件后,select a kit clang gcc中选择clang。然后通过ctrl+shift+p在VSCode中修改配置文件settings.json,加入以下代码:

"clang-tidy.buildPath": "build/compile_commands.json"

最后构造.clang-tidy文件进行配置:

---
Checks: '*,-fuchsia-*,-google-*,-zircon-*,-abseil-*,-modernize-use-trailing-return-type,-llvm-*,-llvmlibc-*'
CheckOptions: [{ key: misc-non-private-member-variables-in-classes, value: IgnoreClassesWithAllMemberVariablesBeingPublic }]
WarningsAsErrors: '*'
HeaderFilterRegex: ''
FormatStyle: none

(6)Doxygen插件配置:下载好插件后,通过ctrl+shift+p在VSCode中修改配置文件settings.json,加入以下代码:

"doxdocgen.generic.authorEmail": "remoa@qq.com",
"doxdocgen.generic.authorName": "Qinyi Deng",
"doxdocgen.file.copyrightTag": [
    "@copyright Copyright (c) {year} SCUT"
],
"doxdocgen.file.fileOrder": [
    "file",
    "author",
    "brief",
    "version",
    "date",
    "empty",
    "copyright",
    "empty",
    "custom",
],
"doxdocgen.generic.order": [
    "brief",
    "tparam",
    "param",
    "return"
],
"doxdocgen.generic.paramTemplate": "@param{indent:0} {param}{indent:4} param doc",
"doxdocgen.generic.returnTemplate": "@return {type} ",
"doxdocgen.generic.includeTypeAtReturn": true,

然后在文件开头/函数前输入/**,然后回车,会输出对应的注释段。

(7)cmake/SourcesAndHeaders.cmake文件设置相关参数

set(sources
    src/AStar.cpp
)

set(exe_sources
		src/AStar.cpp
		${sources}
)

set(headers
    include/project/AStar.hpp
)

set(test_sources
  src/AStar_test.cpp
)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开闭架构

在《不过时的经典层架构》里&#xff0c;有朋友留言关于Manager和Engine的概念&#xff0c;虽然朋友留言把概念解释清楚了。为了避免其他人有同样的疑问&#xff0c;这里我还是再解释一下。 以上是经典的四层架构&#xff0c;在这个架构中&#xff0c;Manager和Engine(引擎)都是…

【liunx配置服务自启动】liunx系统设置net Core程序开机自启动服务 centos系统

liunx系统设置net Core程序开机自启动服务 系统版本&#xff1a;Centos7.9 我的程序部署到/www/wwwroot/AcmeBookStoreHttpApiHost.com/目录下&#xff0c; 程序名是Meowv.Blog.HttpApi.Hosting.dll 1.新建自启动配置文件 首先跳转到system目录下 cd /usr/lib/systemd/syste…

【.net core】yisha框架,实体不在同一项目下设置swagger接口及实体模型注释,授权鉴权

1.Startup.cs中ConfigureServices方法中添加&#xff1a; 授权鉴权内容 #region 授权鉴权//Bearer 的scheme定义var securityScheme new OpenApiSecurityScheme(){Description "使用JWT方案授权&#xff0c;请求时&#xff0c;在请求头部信息中加入: \"Authoriza…

分布式计算模型详解:MapReduce、数据流、P2P、RPC、Agent

前言 本文隶属于专栏《大数据理论体系》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见大数据理论体系 思维导图 MapReduce MapReduce 是一种分布式计算模…

Tomcat与Undertow容器性能对比分析

&#x1f468;‍&#x1f393;作者&#xff1a;bug菌 ✏️博客&#xff1a; CSDN、 掘金、 infoQ、 51CTO等 &#x1f389;简介&#xff1a;CSDN博客专家&#xff0c;C站历届博客之星Top50&#xff0c;掘金/InfoQ/51CTO等社区优质创作者&#xff0c;全网合计8w粉&#xff0c;对…

BufferedImage将图片切成圆形

原图 修改后 方法一 //文件路径 File imageFile new File(path); public BufferedImage changeImages(File imageFile) {BufferedImage avatarImage null;try {avatarImage ImageIO.read(imageFile); avatarImage scaleByPercentage(avatarImage, avatarImage.getWidth(…

LENOVO联想笔记本电脑ThinkBook 15 G2-ITL(20VE)原装出厂Windows10系统恢复原厂OEM设置预装系统

Lenovo联想笔记本电脑&#xff0c;ThinkBook 15 G2-ITL(20VE)出厂Windows10系统&#xff0c;出厂预装系统 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;ISO 文件大小&#xf…

最优化--凸函数--拉格朗日乘子法

目录 凸函数 凸函数定义 凸函数的判定 性质特点 拉格朗日乘子法 基本思想 有约束最优化问题 拉格朗日乘子法 凸函数 凸函数&#xff08;Convex Function&#xff09;是定义在凸集上的实值函数&#xff0c;具有以下性质&#xff1a;对于任意两个定义域内的点&#xf…

Windows11系统启动VMware Workstation 在此主机上不支持嵌套虚拟化导致无法启动虚拟机

问题复现&#xff1a; VMware Workstation中的虚拟机时启动失败&#xff0c;弹出错误弹窗&#xff1a; VMware Workstation 在此主机上不支持嵌套虚拟化。 模块“MonitorMode”启动失败。 未能启动虚拟机。 问题原因&#xff1a; 不要同时开启hyper-V和VMware虚拟机软件&…

(小程序)指定问题换一批功能实现

(小程序)指定问题换一批功能实现 vue3写法 html <view class"title"><p>推荐问题</p><view class"refresh" click"onRefresh"><text>换一批</text><image src"https://cdn.tudb.work/aios/web/im…

MongoDB的数据类型

BSON JSON作为一种轻量级的数据交换格式&#xff0c;JSON的可读性非常好&#xff0c;而且非常便于系统生成和解析&#xff0c;这些优势也让它逐渐取代了XML标准在Web领域的地位&#xff0c;当今许多流行的Web应用开发框架&#xff0c;如SpringBoot都选择了JSON作为默认的数据编…

007+limou+C语言基础排序算法(上)

0.前言 您好这里是limou3434的一篇博文&#xff0c;感兴趣可以看看我的其他内容。 排序算法简单理解就是&#xff1a;一串数组经过排序算法后得到有序的数组。排序算法在不同应用场景有不同的效果&#xff0c;因此我们有必要了解一些基础的排序算法。 而本次我给您带来的是一…

vue+leaflet实现聚合图(根据半径划分)

效果 官方示例 github地址 1. 安装leaflet.markercluster插件 npm install leaflet.markercluster -S** 2. 在项目中引入leaflet.markercluster和样式文件 ** import leaflet.markercluster/dist/MarkerCluster.css import leaflet.markercluster/dist/MarkerCluster.Def…

SqlServer定时执行存储过程

1.连接数据库后选择【SQL Server 代理】—【作业】——右键【新建作业】&#xff0c;具体操作如下图&#xff1a; 2.【新建作业】步骤如下图所示&#xff1a; 3.新建【步骤】&#xff0c;具体如下图所示&#xff1a; 4.新建【计划】&#xff0c;具体如下图所示&#xff1a; 6.配…

Ubuntu 20.04 下g++用不了,但是提示已经安装

问题描述 用sudo apt-get install g来安装&#xff0c;系统却又说g已经是最新版本了&#xff0c;但是用g -v查看又提示需要安装 g&#xff0c;如图片所示。 解决方法 未安装g&#xff0c;安装依赖只需运行命令行&#xff1a; sudo apt-get install build-essential仍然无法成…

docker部署redis

一、拉取镜像 docker search redis 我部署的是redis6.0&#xff0c;使用docker pull 拉取镜像 docker pull redis:6.0 拉取成功后使用docker image查看镜像 docker images | grep redis 二、创建挂载目录 在 /opt 目录下创建redis的 conf 和 data 目录 sudo mkdir /opt/re…

YOLOv5 环境安装

Windows11下yolov5环境配置系列教程&#xff08;基础部分&#xff09; Windows11下yolov5环境配置系列教程&#xff08;基础部分&#xff09;_wnowswolf的博客-CSDN博客 安装 Anaconda3 可以改为miniconda 占用更小 将安装目录下的Scripts和condabin文件夹的路径加入环境变…

linux重启网卡失败

[TO简单粗暴啊&#xff1a; RTNETLINK answers: File exists 这个报错&#xff0c;我看了一下网卡目录下出现了两个网卡配置文件&#xff0c;一个ens33&#xff0c;一个eth0。我本机是ens33&#xff0c;所以把eth0的删除了&#xff0c;就可以了。我这个是测试机器&#xff0c;…

人脸识别是什么?及人脸识别的流程

目录 1. 人脸识别是什么及应用场景2. 人脸识别的组成2.1 前端图像采集2.2 后端智能平台 3.人脸技术的流程3.1人脸检测3.2人脸对齐3.2.1仿射变换3.2.2对齐方法 3.3人脸编码&#xff08;提取特征向量&#xff09;3.4人脸分类 1. 人脸识别是什么及应用场景 人脸识别特指利用分析比…

什么是 frp内网穿透?快解析内网穿透如何实现远程办公?

1.什么是frp内网穿透 frp是一个开源、简洁易用、高性能的内网穿透和 反向代理软件&#xff0c;支持 tcp, udp, http, https等协议&#xff0c;虽然它体积轻量但功能很强大。它利用处于内网或防火墙后的机器&#xff0c;对外网环境提供 http 或 https 服务&#xff1b;对于 htt…