CGAL and the Boost Graph Library

news2025/1/17 13:48:40

CGAL and the Boost Graph Library

许多几何数据结构都可以解释为图,因为它们由顶点和边组成。对于halfedge数据结构、多面体曲面、arrangement以及二维三角剖分类来说,情况都是如此。利用对偶性,人们也可以将面解释为顶点,相邻面之间的边解释为对偶图的边。
CGAL的研究范围是几何算法而不是图算法。尽管如此,这个包提供了必要的类和函数,使Boost Graph Library(简称BGL)的算法能够使用CGAL数据结构。
此外,该软件包扩展了BGL,引入了 HalfedgeGraphFaceGraph 等概念,允许处理半边和面。这些概念反映了第6章中描述的半边数据结构的设计,即对偶半边和围绕顶点和面的圆形半边序列。

属性映射

BGL中广泛使用的另一个特性是Boost Property Map Library提供的属性映射。属性映射是一个通用的接口,用于将键对象映射到对应的值对象。
BGL使用属性映射将信息与顶点和边关联起来。该机制使用traits类( boost::property_traits )和自由函数来读取( get )和写入( put )信息,包括顶点、边以及半边和面。例如,BGL Dijksta的最短路径算法在这样的属性映射中写入每个顶点的前驱节点以及到源节点的距离。
一些默认的属性映射与图形类型相关联。它们被称为内部属性映射(internal property map),可以通过 get() 函数的重载来获取。

Surface_mesh类作为Boost Graph 概念的模型

Surface_mesh 类是BGLCGAL提供的大多数图概念的一个模型。完整的列表可以在boost::graph_traits的文档中找到。这些例子展示了如何使用 Surface_mesh 的一些BGL算法,以及如何使用CGAL提供的概念来实现一个简单的算法。

Surface_mesh的最小生成树MST

下面的示例程序计算曲面网格上的最小生成树。更多的例子可以在三角网格简化、三角网格分割和三角网格变形章节中找到。
surface mesh类使用整数索引来寻址顶点和边,它还带有一个内置的属性机制,可以很好地映射到BGL上。

最小生成树算法在属性映射中写入每个顶点的前驱节点。

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>

#include <iostream>
#include <fstream>

#include <CGAL/boost/graph/prim_minimum_spanning_tree.h>

typedef CGAL::Simple_cartesian<double>                       Kernel;
typedef Kernel::Point_3                                      Point;
typedef CGAL::Surface_mesh<Point>                            Mesh;

typedef boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;

int main(int argc, char* argv[])
{
  const std::string filename = (argc>1) ? argv[1] : CGAL::data_file_path("meshes/prim.off");

  Mesh P;
  if(!CGAL::IO::read_polygon_mesh(filename, P))
  {
    std::cerr << "Invalid input." << std::endl;
    return 1;
  }
 //
  Mesh::Property_map<vertex_descriptor,vertex_descriptor> predecessor;
  predecessor = P.add_property_map<vertex_descriptor,vertex_descriptor>("v:predecessor").first;

  boost::prim_minimum_spanning_tree(P, predecessor, boost::root_vertex(*vertices(P).first));

  std::cout << "#VRML V2.0 utf8\n"
    "DirectionalLight {\n"
    "direction 0 -1 0\n"
    "}\n"
    "Shape {\n"
    "  appearance Appearance {\n"
    "    material Material { emissiveColor 1 0 0}}\n"
    "    geometry\n"
    "    IndexedLineSet {\n"
    "      coord Coordinate {\n"
    "        point [ \n";

  for(vertex_descriptor vd : vertices(P)){
    std::cout <<  "        " << P.point(vd) << "\n";
  }

  std::cout << "        ]\n"
    "     }\n"
    "      coordIndex [\n";
  for(vertex_descriptor vd : vertices(P)){
    if(predecessor[vd]!=vd){
      std::cout << "      " << std::size_t(vd) << ", " << std::size_t(predecessor[vd]) <<  ", -1\n";
    }
  }

  std::cout << "]\n"
    "  }#IndexedLineSet\n"
    "}# Shape\n";

  P.remove_property_map(predecessor);
  return 0;
}

meshes/prim.off网格曲面:
prim
网格曲面的最小生成树,写入VRML格式的:
在这里插入图片描述
VRML结果文件使用three.js可视化:
在这里插入图片描述

Polyhedron_3类作为Boost Graph概念的模型

Polyhedron_3 类是BGLCGAL提供的大多数图概念的一个模型。完整的列表可以在boost::graph_traits的文档中找到。这些例子展示了如何使用 Polyhedron_3的一些BGL算法,以及如何使用CGAL提供的概念来实现一个简单的算法。

多面体表面Polyhedral Surface的最小生成树MST

下面的示例程序计算多面体表面上的最小生成树。更多的例子可以在“三角网格简化”一章中找到。

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <iostream>
#include <list>
#include <CGAL/boost/graph/kruskal_min_spanning_tree.h>
typedef CGAL::Simple_cartesian<double>                       Kernel;
//typedef Kernel::Vector_3                                     Vector;
typedef Kernel::Point_3                                      Point;
typedef CGAL::Polyhedron_3<Kernel>                           Polyhedron;

typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator   vertex_iterator;
typedef boost::graph_traits<Polyhedron>::edge_descriptor   edge_descriptor;

// The BGL makes heavy use of indices associated to the vertices
// We use a std::map to store the index
typedef std::map<vertex_descriptor,int> Vertex_index_map;
Vertex_index_map vertex_index_map;
// A std::map is not a property map, because it is not lightweight
typedef boost::associative_property_map<Vertex_index_map> Vertex_index_pmap;
Vertex_index_pmap vertex_index_pmap(vertex_index_map);
void
kruskal(const Polyhedron& P)
{
  // associate indices to the vertices
  vertex_iterator vb, ve;
  int index = 0;
  // boost::tie assigns the first and second element of the std::pair
  // returned by boost::vertices to the variables vb and ve
  for(boost::tie(vb, ve)=vertices(P); vb!=ve; ++vb){
    vertex_index_pmap[*vb]= index++;
  }
  // We use the default edge weight which is the length of the edge
  // This property map is defined in graph_traits_Polyhedron_3.h
  // In the function call you can see a named parameter: vertex_index_map
  std::list<edge_descriptor> mst;
  boost::kruskal_minimum_spanning_tree(P,
                                       std::back_inserter(mst),
                                       boost::vertex_index_map(vertex_index_pmap));
  std::cout << "#VRML V2.0 utf8\n"
    "Shape {\n"
    "  appearance Appearance {\n"
    "    material Material { emissiveColor 1 0 0}}\n"
    "    geometry\n"
    "    IndexedLineSet {\n"
    "      coord Coordinate {\n"
    "        point [ \n";
  for(boost::tie(vb, ve) = vertices(P); vb!=ve; ++vb){
    std::cout <<  "        " << (*vb)->point() << "\n";
  }
  std::cout << "        ]\n"
               "     }\n"
    "      coordIndex [\n";
  for(std::list<edge_descriptor>::iterator it = mst.begin(); it != mst.end(); ++it)
  {
    edge_descriptor e = *it ;
    vertex_descriptor s = source(e,P);
    vertex_descriptor t = target(e,P);
    std::cout << "      " << vertex_index_pmap[s] << ", " << vertex_index_pmap[t] <<  ", -1\n";
  }
  std::cout << "]\n"
    "  }#IndexedLineSet\n"
    "}# Shape\n";
}
int main() {
  Polyhedron P;
  Point a(1,0,0);
  Point b(0,1,0);
  Point c(0,0,1);
  Point d(0,0,0);
  P.make_tetrahedron(a,b,c,d);
  kruskal(P);
  return 0;
}

四面体最小生成树:
在这里插入图片描述
在这里插入图片描述

使用带有ID的顶点和边

下面的示例程序调用了BGL Kruskal的最小生成树算法,访问了存储在多面体顶点中的 id() 字段。

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <iostream>
#include <list>
#include <CGAL/boost/graph/kruskal_min_spanning_tree.h>
typedef CGAL::Simple_cartesian<double>                               Kernel;
typedef Kernel::Point_3                                              Point;
typedef CGAL::Polyhedron_3<Kernel,CGAL::Polyhedron_items_with_id_3>  Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator   vertex_iterator;
typedef boost::graph_traits<Polyhedron>::edge_descriptor   edge_descriptor;
void
kruskal( const Polyhedron& P)
{
  // We use the default edge weight which is the length of the edge
  // This property map is defined in graph_traits_Polyhedron_3.h
  // This function call requires a vertex_index_map named parameter which
  // when  omitted defaults to "get(vertex_index,graph)".
  // That default works here because the vertex type has an "id()" method
  // field which is used by the vertex_index internal property.
  std::list<edge_descriptor> mst;
  boost::kruskal_minimum_spanning_tree(P,std::back_inserter(mst));
  std::cout << "#VRML V2.0 utf8\n"
    "Shape {\n"
    "appearance Appearance {\n"
    "material Material { emissiveColor 1 0 0}}\n"
    "geometry\n"
    "IndexedLineSet {\n"
    "coord Coordinate {\n"
    "point [ \n";
  vertex_iterator vb, ve;
  for(boost::tie(vb,ve) = vertices(P); vb!=ve; ++vb){
    std::cout << (*vb)->point() << "\n";
  }
  std::cout << "]\n"
    "}\n"
    "coordIndex [\n";
  for(std::list<edge_descriptor>::iterator it = mst.begin(); it != mst.end(); ++it){
    std::cout << source(*it,P)->id()
              << ", " << target(*it,P)->id() <<  ", -1\n";
  }
  std::cout << "]\n"
    "}#IndexedLineSet\n"
    "}# Shape\n";
}
int main() {
  Polyhedron P;
  Point a(1,0,0);
  Point b(0,1,0);
  Point c(0,0,1);
  Point d(0,0,0);
  P.make_tetrahedron(a,b,c,d);
  // associate indices to the vertices using the "id()" field of the vertex.
  vertex_iterator vb, ve;
  int index = 0;
  // boost::tie assigns the first and second element of the std::pair
  // returned by boost::vertices to the variables vit and ve
  for(boost::tie(vb,ve)=vertices(P); vb!=ve; ++vb ){
    vertex_descriptor  vd = *vb;
    vd->id() = index++;
  }
  kruskal(P);
  return 0;
}

在这里插入图片描述

三角剖分Triangulations 作为Boost Graph概念的模型

三角剖分有顶点和面,允许直接转换为图形。半边被定义为一对面柄和边缘的指数。完整的列表可以在boost::graph_traits的文档中找到。
结合计算几何和图论的一个经典例子是平面上点集的欧氏最小生成树。它可以通过在点集的Delaunay三角剖分上运行最小生成树算法来计算。

欧氏最小生成树

在下面的例子中,我们创建一个Delaunay三角网,并在其上运行Kruskal的最小生成树算法。因为三角网的顶点句柄不是数组中的索引,我们必须提供一个属性映射,将顶点句柄映射到 [0, t.number_of_vertices()) 范围内的整数。

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>

#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/boost/graph/graph_traits_Delaunay_triangulation_2.h>

#include <CGAL/boost/graph/kruskal_min_spanning_tree.h>

#include <fstream>
#include <iostream>
#include <map>

typedef CGAL::Exact_predicates_inexact_constructions_kernel         K;
typedef K::Point_2                                                  Point;

typedef CGAL::Delaunay_triangulation_2<K>                           Triangulation;

typedef boost::graph_traits<Triangulation>::vertex_descriptor       vertex_descriptor;
typedef boost::graph_traits<Triangulation>::vertex_iterator         vertex_iterator;
typedef boost::graph_traits<Triangulation>::edge_descriptor         edge_descriptor;

// The BGL makes use of indices associated to the vertices
// We use a std::map to store the index
typedef std::map<vertex_descriptor,int>                             VertexIndexMap;

// A std::map is not a property map, because it is not lightweight
typedef boost::associative_property_map<VertexIndexMap>             VertexIdPropertyMap;

int main(int argc,char* argv[])
{
  const char* filename = (argc > 1) ? argv[1] : "data/points.xy";
  std::ifstream input(filename);
  Triangulation tr;

  Point p;
  while(input >> p)
    tr.insert(p);

  // Associate indices to the vertices
  VertexIndexMap vertex_id_map;
  VertexIdPropertyMap vertex_index_pmap(vertex_id_map);
  int index = 0;

  for(vertex_descriptor vd : vertices(tr))
    vertex_id_map[vd] = index++;

  // We use the default edge weight which is the squared length of the edge
  // This property map is defined in graph_traits_Triangulation_2.h

  // In the function call you can see a named parameter: vertex_index_map
  std::list<edge_descriptor> mst;
  boost::kruskal_minimum_spanning_tree(tr, std::back_inserter(mst),
                                       vertex_index_map(vertex_index_pmap));

  std::cout << "The edges of the Euclidean minimum spanning tree:" << std::endl;
  for(edge_descriptor ed : mst)
  {
    vertex_descriptor svd = source(ed, tr);
    vertex_descriptor tvd = target(ed, tr);
    Triangulation::Vertex_handle sv = svd;
    Triangulation::Vertex_handle tv = tvd;
    std::cout << "[ " << sv->point() << "  |  " << tv->point() << " ] " << std::endl;
  }

  return EXIT_SUCCESS;
}

在这里插入图片描述

在顶点中存储顶点ID

BGL算法广泛地使用了顶点索引。在前面的例子中,我们将索引存储在 std::map 中,并将该映射转换为属性映射。然后,这个属性映射被作为参数传递给最短路径函数。
如果用户没有显式传递一个属性映射,图算法会使用 get(boost::vertex_index,ft) 返回的属性映射。这个属性映射假定顶点有一个成员函数 id() ,该函数返回一个int类型的引用。因此,CGAL提供了一类 Triangulation_vertex_base_with_id_2 。正确设置索引是用户的责任。
实例进一步说明,图的特征也适用于Delaunay三角剖分。

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>

#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/Triangulation_vertex_base_with_id_2.h>

#include <CGAL/boost/graph/graph_traits_Delaunay_triangulation_2.h>
#include <CGAL/boost/graph/dijkstra_shortest_paths.h>

#include <fstream>

typedef CGAL::Exact_predicates_inexact_constructions_kernel             K;
typedef K::Point_2                                                      Point;

typedef CGAL::Triangulation_vertex_base_with_id_2<K>                    Tvb;
typedef CGAL::Triangulation_face_base_2<K>                              Tfb;
typedef CGAL::Triangulation_data_structure_2<Tvb, Tfb>                  Tds;
typedef CGAL::Delaunay_triangulation_2<K, Tds>                          Triangulation;

typedef boost::graph_traits<Triangulation>::vertex_descriptor           vertex_descriptor;
typedef boost::graph_traits<Triangulation>::vertex_iterator             vertex_iterator;

typedef boost::property_map<Triangulation, boost::vertex_index_t>::type VertexIdPropertyMap;

int main(int argc,char* argv[])
{
  const char* filename = (argc > 1) ? argv[1] : "data/points.xy";
  std::ifstream input(filename);
  Triangulation tr;

  Point p;
  while(input >> p)
    tr.insert(p);

  // associate indices to the vertices
  int index = 0;
  for(vertex_descriptor vd : vertices(tr))
    vd->id()= index++;

  VertexIdPropertyMap vertex_index_pmap = get(boost::vertex_index, tr);

  // Dijkstra's shortest path needs property maps for the predecessor and distance
  std::vector<vertex_descriptor> predecessor(num_vertices(tr));
  boost::iterator_property_map<std::vector<vertex_descriptor>::iterator, VertexIdPropertyMap>
    predecessor_pmap(predecessor.begin(), vertex_index_pmap);

  std::vector<double> distance(num_vertices(tr));
  boost::iterator_property_map<std::vector<double>::iterator, VertexIdPropertyMap>
    distance_pmap(distance.begin(), vertex_index_pmap);

  vertex_descriptor source = *vertices(tr).first;
  std::cout << "\nStart dijkstra_shortest_paths at " << source->point() << std::endl;

  boost::dijkstra_shortest_paths(tr, source, distance_map(distance_pmap)
                                            .predecessor_map(predecessor_pmap));

  for(vertex_descriptor vd : vertices(tr))
  {
    std::cout << vd->point() << " [" << vd->id() << "] ";
    std::cout << " has distance = "  << get(distance_pmap,vd) << " and predecessor ";

    vd = get(predecessor_pmap,vd);
    std::cout << vd->point() << " [" << vd->id() << "]\n";
  }

  return EXIT_SUCCESS;
}

在这里插入图片描述

参考

  1. https://doc.cgal.org/latest/BGL/index.html
  2. https://juejin.cn/post/7413940909262143524

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

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

相关文章

AcWing119 袭击

目录 AcWing119 袭击题目描述背景输入输出数据范围 题解解法优化 打赏 AcWing119 袭击 题目描述 背景 特工进入据点突袭发电站&#xff0c;已知所有发电站的位置和所有特工的降落位置&#xff0c;求任意特工距离任意核电站的最短距离 输入 第一行一个整数 T T T&#xff0…

基于SpringBoot实现SpringMvc上传下载功能实现

SpringMvc上传下载功能实现 1.创建新的项目 1&#xff09;项目信息填写 Spring Initializr (单击选中)Name(填写项目名字)Language&#xff08;选择开发语言&#xff09;Type&#xff08;选择工具Maven&#xff09;Group&#xff08;&#xff09;JDK&#xff08;jdk选择17 &…

深度学习——D1(环境配置)

课程内容 W-H-W 资源 AI地图 物体检测和分割 样式迁移 人脸合成 文字生成图片 预测与训练 本地安装

【IPV6从入门到起飞】5-2 IPV6+Home Assistant(ESP32+MQTT+DHT11+BH1750)传感器采集上传监测

IPV6Home Assistant[ESP32MQTTDHT11BH1750]传感器采集上传监测 1 背景2 实现效果3 Home Assistant配置3-1 MQTT配置3-2 yaml 配置3-3 加载配置 4 ESP32搭建4-1 开发环境4-2 工程代码 5 实现效果 1 背景 在上一小节【IPV6从入门到起飞】5-1 IPV6Home Assistant(搭建基本环境)我…

luogu基础课题单 入门 上

【深基2.例5】苹果采购 题目描述 现在需要采购一些苹果&#xff0c;每名同学都可以分到固定数量的苹果&#xff0c;并且已经知道了同学的数量&#xff0c;请问需要采购多少个苹果&#xff1f; 输入格式 输入两个不超过 1 0 9 10^9 109 正整数&#xff0c;分别表示每人分到…

chapter1-项目搭建

文章目录 序章1. 项目开发基础概念1.1 企业开发中常见的web项目类型1.2 企业项目开发流程1.3 立项申请阶段 2. 需求分析2.1 首页2.2 登录注册2.3 课程列表2.4 课程详情2.5 购物车2.6 商品结算2.7 购买成功2.8 个人中心2.9 我的课程及课程学习 3. 环境搭建3.1 创建虚拟环境3.2 相…

2024.9.13 Python与图像处理新国大EE5731课程大作业,索贝尔算子计算边缘,高斯核模糊边缘,Haar小波计算边缘

1.编写一个图像二维卷积程序。它应该能够处理任何灰度输入图像&#xff0c;并使用以下内核进行操作&#xff1a; %matplotlib inline import numpy as np import matplotlib.pyplot as plt from scipy import linalg import random as rm import math import cv2# import and …

基于云端的SIEM解决方案

最近的一项市场研究爆出了一组惊人的数字&#xff0c;在2024年&#xff0c;网络攻击增加了600%&#xff01;更加令人担忧的是&#xff0c;这恐怕只是冰山一角。世界各地的组织都已经认识到了这一威胁&#xff0c;并正在采取多重措施来抵御来自线下和远程混合式办公模式带来的网…

SpringBoot项目获取统一前缀配置以及获取非确定名称配置

SpringBoot项目获取统一前缀配置以及获取非确定名称配置 在SpringBoot项目中&#xff0c;我们经常看到统一前缀的配置&#xff0c;我们该怎么统一获取 my.config.a.namexiaoming my.config.a.age18 my.config.a.addressguangdong my.config.b.namexiaomli my.config.b.age20 my…

【Unity】手写图片轮播

最近项目空闲&#xff0c;回顾了一下之前写的杂七杂八的软件&#xff0c;比较多&#xff0c;而且比较杂乱&#xff0c;代码能看明白&#xff0c;但是非常不科学&#xff0c;不符合逻辑&#xff0c;然后我就有点无奈&#xff0c;虽说是做了很多年的老程序的&#xff0c;但是遇到…

小目标检测顶会新思路!最新成果刷爆遥感SOTA,参数小了18倍

遥感领域的小目标检测一直是个具有挑战性和趣味性的研究方向&#xff0c;同时也是顶会顶刊的常客。但不得不说&#xff0c;今年关于遥感小目标检测的研究热情尤其高涨&#xff0c;已经出现了很多非常优秀的成果。 比如SuperYOLO方法&#xff0c;通过融合多模态数据并执行高分辨…

数据库安全性控制

‍ 在当今信息化时代&#xff0c;数据库安全性 对于保护数据免受非法访问和损害至关重要。无论是个人数据还是企业机密&#xff0c;数据库安全性控制都能有效地防范潜在的威胁。本文将为你深入浅出地介绍数据库安全性控制的关键方法和机制&#xff0c;帮助你轻松掌握这一重要概…

vulnhub靶机:21 LTR: Scene1

下载 下载地址&#xff1a;https://www.vulnhub.com/entry/21ltr-scene-1,3/ 导入靶机 一直按默认的来&#xff0c;一直下一步 修改网卡 修改靶机和 kali 攻击机网络模式为仅主机模式 把仅主机模式的 IP 修改为 192.168.2.0 信息收集 主机发现 arp-scan -l 靶机 IP 是 192.…

Windows系统下安装Redis

文章目录 1、下载Redis安装包2、解压压缩包3、运行Redis4、Redis连接检测5、Redis相关设置5.1设置环境变量PATH5.2Redis 配置文件修改 1、下载Redis安装包 Windows版本的Redis可以在Github中下载&#xff1a;下载Redis 2、解压压缩包 将下载的压缩包解压到某个目录下&#…

微服务CI/CD实践(五)Jenkins Docker 自动化构建部署Java微服务

微服务CI/CD实践系列&#xff1a; 微服务CI/CD实践&#xff08;一&#xff09;环境准备及虚拟机创建 微服务CI/CD实践&#xff08;二&#xff09;服务器先决准备 微服务CI/CD实践&#xff08;三&#xff09;Jenkins部署及环境配置 微服务CI/CD实践&#xff08;四&#xff09;…

c++20 std::format 格式化说明

在标头<format>定义 ()功能很强大&#xff0c;它把字符串当成一个模板&#xff0c;通过传入的参数进行格式化&#xff0c;并且使用大括号‘{}’作为特殊字符代替‘%’。 1、基本用法 &#xff08;1&#xff09;不带编号&#xff0c;即“{}”&#xff08;2&#xff09;带…

学会使用西门子博途Startdrive中的测量功能

工程师在驱动调试过程中&#xff0c;往往需要对驱动系统的性能进行分析及优化&#xff0c;比如说借助于调试软件中的驱动器测量功能&#xff0c;可以得到驱动系统的阶跃响应、波特图等&#xff0c;以此为依据工程师可以调整速度控制器、电流控制器的相关参数&#xff0c;使驱动…

今天一定要彻底卸载Windows Denfender!攻略给你了

最近有小伙伴吐槽&#xff1a;明明都已经把Windows Defender关了&#xff0c;为啥它还会时不时拦截我下载的文件&#xff1f; 小白就问&#xff1a;明明是谁&#xff1f; 嗯…… 肯定有小伙伴遇到同样的问题&#xff0c;Windows Defender已经关了&#xff0c;但好像并没有完…

利用Xinstall,轻松搭建高效App运营体系

在移动互联网时代&#xff0c;App的推广和运营成为了企业发展的关键环节。然而&#xff0c;随着流量红利的逐渐消失&#xff0c;传统的推广方式已经难以满足企业快速获客的需求。在这个背景下&#xff0c;Xinstall作为一款强大的渠道推广工具&#xff0c;凭借其独特的功能和优势…

【IP协议】IP协议报头结构(上)

IP 协议报头结构 4位版本 实际上只有两个取值 4 > IPv4&#xff08;主流&#xff09;6 > IPv6 IPv2&#xff0c;IPv5 在实际中是没有的&#xff0c;可能是理论上/实验室中存在 4位首部长度 IP 协议报头也是变长的&#xff0c;因为选项个数不确定&#xff0c;所以报头长…