[大邻域算法](MD)VRPTW常见求解算法--代码解析

news2024/11/17 11:27:39

相关链接:

  • 【路径分割】序列分隔和路径提取的案例
  • 【算法】LNS(大邻域算法)和ALNS(自适应大邻域算法)(持更)
  • Python实现(MD)VRPTW常见求解算法——自适应大邻域搜索算法(ALNS),本文也是该篇的解析
  • 干货 | 自适应大邻域搜索(Adaptive Large Neighborhood Search)入门到精通超详细解析-概念篇
  • 自适应大邻域搜索(Adaptive Large Neighborhood Search)-详解
  • 自适应大邻域 | 用ALNS框架求解一个TSP问题 - 代码详解

文章目录

    • 0. 简介
    • 一、Destory
      • 1.1 随机破坏
      • 1.2 最坏值破坏
      • 1. 3 执行破坏算子
    • 二、Repair
      • 2.1 随机修改
      • 2.2 贪婪修复
        • 搜索贪婪修复位置
      • 2.3 最大贡献值修复
        • 搜索最大贡献值
      • 2.4 执行修复算子
    • 三。选择--破坏and修复的类型
      • 更新算子权重
        • 重置得分

0. 简介

ALNS是从LNS发展扩展而来的,在了解了LNS以后,我们现在来看看ALNS。ALNS在LSN的基础上,允许在同一个搜索中使用多个destroy和repair方法来获得当前解的邻域。

ALNS会为每个destroy和repair方法分配一个权重,通过该权重从而控制每个destroy和repair方法在搜索期间使用的频率。 在搜索的过程中,ALNS会对各个destroy和repair方法的权重进行动态调整,以便获得更好的邻域和解。简单点解释,ALNS和LNS不同的是,ALNS通过使用多种destroy和repair方法,然后再根据这些destroy和repair方法生成的解的质量,选择那些表现好的destroy和repair方法,再次生成邻域进行搜索

solution的邻域
一个解x经过destroy和repair方法以后,实则是相当于经过了一个邻域动作的变换.
在这里插入图片描述
上图是三个CVRP问题的解,上左表示的是当前解,上右则是经过了destroy方法以后的解(移除了6个customers),下面表示由上右的解经过repair方法以后最终形成的解(重新插入了一些customers)。

上面展示的只是生成邻域中一个解的过程而已,实际整个邻域还有很多其他可能的解。比如在一个CVRP问题中,将destroy方法定义为:移除当前解x中15%的customers。假如当前的解x中有100名customers,那么就有C(100,15)= 100!/(15!×85!) =2.5×10的17次方 种移除的方式。并且,根据每一种移除方式,又可以有很多种修复的方法。这样下来,一对destroy和repair方法能生成非常多的邻居解,而这些邻居解的集合,就是邻域了.
destroy和repair的形象
在这里插入图片描述

伪代码
在这里插入图片描述


代码的主要函数

# 数据结构:解
class Sol()
# 数据结构:需求节点
Class Node()
# 数据结构:车场节点
Class Depot()
# 数据结构:全局参数
Class Model()
# 读取csv文件
Def readCSVFile()
# 初始化参数:计算’距离矩阵、时间矩阵、初始信息素’
Def calDistanceTimeMatrix()
# 计算路径费用
Def calTravelCost()
# 根据Split结果,提取路径
Def splitRoutes()
# 计算目标函数
Def calObj()
# 随机构造初始解
Def genInitialSol()
# 随机破坏
Def createRandomDestory()
# 最值破坏
Def crateWorseDestory()
# 随机修复
Def createRandomRepair()
# 贪婪修复
Def createGreadyRepair()
# 搜索贪婪修复位置
Def findGreedyInsert()
# 最大贡献值修复
Def createRegretRepair()
# 搜索最大贡献值
Def  find RegretInsert()
# 选择修复破坏算子
Def selectDestoryRepair()
# 执行破坏算子
Def doDestory()
# 执行修复算子
Def doRepair()
# 重置得分
Def resetScore()
# 更新算子权重
def updateWeight()
# 绘制目标函数收敛曲线
Def plotObj()
# 输出优化结果
Def outPut()
# 绘制优化车辆路径,以3个车场为例
Def plotRoutes()
# 运行函数
Def run()

一、Destory

1.1 随机破坏


def createRandomDestory(model):
    d=random.uniform(model.rand_d_min,model.rand_d_max)
    reomve_list=random.sample(range(model.number_of_demands),int(d*(model.number_of_demands-1)))
    return reomve_list
  • model.rand_d_max = 0.4 # 随机破坏最大破坏比例
  • model.rand_d_min = 0.1 # 随机破坏最小破坏比例
  • model.number_of_demands=len(model.demand_id_list)需求节点数目
  • int(d*(model.number_of_demands-1):破坏的节点个数
  • 在这里插入图片描述
  • return :破坏的节点(们)的ID

1.2 最坏值破坏

def createWorseDestory(model,sol):
    deta_f=[]
    for node_no in sol.node_no_seq:
        node_no_seq_=copy.deepcopy(sol.node_no_seq)
        node_no_seq_.remove(node_no)
        obj,_,_,_=calObj(node_no_seq_,model)
        deta_f.append(sol.obj-obj)
    sorted_id = sorted(range(len(deta_f)), key=lambda k: deta_f[k], reverse=True)
    d=random.randint(model.worst_d_min,model.worst_d_max)
    remove_list=sorted_id[:d]
    return remove_list
  • sol.node_no_seq: sol的整数型基因编码
  • sol.obj:sol的目标值
  • model.worst_d_min = 5 # 最坏值破坏最少破坏数量
  • model.worst_d_max = 20 # 最坏值破坏最多破坏数量
  • sorted_id:是根据(破坏后的解的)obj值从小到大排序所对应的node_id号。

在这里插入图片描述在这里插入图片描述

  • return :破坏的节点(们)的ID

1. 3 执行破坏算子

def doDestory(destory_id,model,sol):
    if destory_id==0:
        reomve_list=createRandomDestory(model)
    else:
        reomve_list=createWorseDestory(model,sol)
    return reomve_list
  • destory_id: 0-1随机数,用来选择执行 随机破坏还是最值破坏
  • 随机破坏:随机破坏几个节点
  • 最值破坏:根据某个解sol,破坏obj值降低最大的几个节点

二、Repair

2.1 随机修改

def createRandomRepair(remove_list,model,sol):
    unassigned_node_no_seq=[]
    assigned_node_no_seq = []
    # remove node from current solution
    for i in range(model.number_of_demands):
        if i in remove_list:
            unassigned_node_no_seq.append(sol.node_no_seq[i])
        else:
            assigned_node_no_seq.append(sol.node_no_seq[i])
    # insert
    for node_no in unassigned_node_no_seq:
        index=random.randint(0,len(assigned_node_no_seq)-1)
        assigned_node_no_seq.insert(index,node_no)
    new_sol=Sol()
    new_sol.node_no_seq=copy.deepcopy(assigned_node_no_seq)
    new_sol.obj,new_sol.route_list,new_sol.route_distance,new_sol.timetable_list=calObj(assigned_node_no_seq,model)
    return new_sol
  1. 按照node_no_seq的顺序,将节点分为未分配节点和已分配节点
  • unassigned_node_no_seq:未分配节点序列
  • assigned_node_no_seq:已分配节点序列
  1. 未分配节点 依次插入,插入位置的索引随机生成。
  2. 以新的node_no_seq生成一个new_sol,并计算new_sol的属性
  3. 返回new_sol

2.2 贪婪修复

def createGreedyRepair(remove_list,model,sol):
    unassigned_node_no_seq = []
    assigned_node_no_seq = []
    # remove node from current solution
    for i in range(model.number_of_demands):
        if i in remove_list:
            unassigned_node_no_seq.append(sol.node_no_seq[i])
        else:
            assigned_node_no_seq.append(sol.node_no_seq[i])
    #insert
    while len(unassigned_node_no_seq)>0:
        insert_node_no,insert_index=findGreedyInsert(unassigned_node_no_seq,assigned_node_no_seq,model)
        assigned_node_no_seq.insert(insert_index,insert_node_no)
        unassigned_node_no_seq.remove(insert_node_no)
    new_sol=Sol()
    new_sol.node_no_seq=copy.deepcopy(assigned_node_no_seq)
    new_sol.obj,new_sol.route_list,new_sol.route_distance,new_sol.timetable_list=calObj(assigned_node_no_seq,model)
    return new_sol
  1. 按照node_no_seq的顺序,将节点分为未分配节点和已分配节点
  • unassigned_node_no_seq:未分配节点序列
  • assigned_node_no_seq:已分配节点序列
  1. while :未分配的节点

    • 使用findGreedyInsert获得插入的节点insert_node_no和索引insert_index
    • assigned_node_no_seq,按照索引插入节点
    • unassigned_node_no_seq删除当前节点
  2. 以新的node_no_seq生成一个new_sol,并计算new_sol的属性

  3. 返回new_sol

搜索贪婪修复位置

def findGreedyInsert(unassigned_node_no_seq,assigned_node_no_seq,model):
    best_insert_node_no=None
    best_insert_index = None
    best_insert_cost = float('inf')
    assigned_obj,_,_,_=calObj(assigned_node_no_seq,model)
    for node_no in unassigned_node_no_seq:
        for i in range(len(assigned_node_no_seq)):
            assigned_node_no_seq_ = copy.deepcopy(assigned_node_no_seq)
            assigned_node_no_seq_.insert(i, node_no)
            obj_, _,_,_ = calObj(assigned_node_no_seq_, model)
            deta_f = obj_ - assigned_obj
            if deta_f<best_insert_cost:
                best_insert_index=i
                best_insert_node_no=node_no
                best_insert_cost=deta_f
    return best_insert_node_no,best_insert_index
  • best_insert_node_no=None ,从unassigned_node_no_seq选择最佳插入的节点
  • best_insert_index = None,插入节点的最佳位置
  • best_insert_cost = float(‘inf’),插入的成本
  • assigned_obj: assigned_node_no_seq(部分节点序列)的目标值
一. 对于未分配的节点进行循环,循环次数为:未分配的节点个数
     1. 对于插入的位置进行循环,循环次数为:未分配的节点个数+1
    		 1) assigned_node_no_seq_:已分配节点序列的 ’深复制‘
    		 2) 按照循环的索引位置,将节点插入
    		 3)计算新序列 assigned_node_no_seq_的obj值
    		 4)deta_f: 目标值的增幅
    		 5) 判断:增幅值是否小于当前记录的最大插入成本
    		 			a. 更新 最佳插入索引best_insert_index
    		 			b. 更新最佳插入节点best_insert_node_no
    		 			c. 更新最大插入成本best_insert_cost
 二、return :最佳插入节点和最佳索引

2.3 最大贡献值修复

def createRegretRepair(remove_list,model,sol):
    unassigned_node_no_seq = []
    assigned_node_no_seq = []
    # remove node from current solution
    for i in range(model.number_of_demands):
        if i in remove_list:
            unassigned_node_no_seq.append(sol.node_no_seq[i])
        else:
            assigned_node_no_seq.append(sol.node_no_seq[i])
    # insert
    while len(unassigned_node_no_seq)>0:
        insert_node_no,insert_index=findRegretInsert(unassigned_node_no_seq,assigned_node_no_seq,model)
        assigned_node_no_seq.insert(insert_index,insert_node_no)
        unassigned_node_no_seq.remove(insert_node_no)
    new_sol = Sol()
    new_sol.node_no_seq = copy.deepcopy(assigned_node_no_seq)
    new_sol.obj, new_sol.route_list,new_sol.route_distance,new_sol.timetable_list = calObj(assigned_node_no_seq, model)
    return new_sol
  1. 按照node_no_seq的顺序,将节点分为未分配节点和已分配节点
  • unassigned_node_no_seq:未分配节点序列
  • assigned_node_no_seq:已分配节点序列
  1. while :未分配的节点

    • 使用findRegretInsert获得插入的节点insert_node_no和索引insert_index
    • assigned_node_no_seq,按照索引插入节点
    • unassigned_node_no_seq删除当前节点
  2. 以新的node_no_seq生成一个new_sol,并计算new_sol的属性

  3. 返回new_sol

搜索最大贡献值

def findRegretInsert(unassigned_node_no_seq,assigned_node_no_seq,model):
    opt_insert_node_no = None
    opt_insert_index = None
    opt_insert_cost = -float('inf')
    for node_no in unassigned_node_no_seq:
        n_insert_cost=np.zeros((len(assigned_node_no_seq),3))
        for i in range(len(assigned_node_no_seq)):
            assigned_node_no_seq_=copy.deepcopy(assigned_node_no_seq)
            assigned_node_no_seq_.insert(i,node_no)
            obj_,_,_,_=calObj(assigned_node_no_seq_,model)
            n_insert_cost[i,0]=node_no
            n_insert_cost[i,1]=i
            n_insert_cost[i,2]=obj_
        n_insert_cost= n_insert_cost[n_insert_cost[:, 2].argsort()]
        deta_f=0
        for i in range(1,model.regret_n):
            deta_f=deta_f+n_insert_cost[i,2]-n_insert_cost[0,2]
        if deta_f>opt_insert_cost:
            opt_insert_node_no = int(n_insert_cost[0, 0])
            opt_insert_index=int(n_insert_cost[0,1])
            opt_insert_cost=deta_f
    return opt_insert_node_no,opt_insert_index
  • model.regret_n = 5 # 后悔值破坏数量
  • opt_insert_node_no = None ,最优插入节点
  • opt_insert_index = None ,最优插入索引
  • opt_insert_cost = -float(‘inf’),最优插入成本

在这里插入图片描述

在这里插入图片描述

一. 对于未分配的节点进行循环,节点:node_no,   循环次数为:未分配的节点个数
		1. n_insert_cost, 插入成本空矩阵,shape=(已分配节点个数,3)
		2. 对于插入的位置进行循环,索引:i,   循环次数为:未分配的节点个数+1
        	 1) assigned_node_no_seq_:已分配节点序列的 ’深复制‘
    		 2) 按照循环的索引位置,将节点插入
    		 3)计算新序列 assigned_node_no_seq_的obj_值
    		 4) n_insert_cost中填入(i,0)--node_no ,(i,1)--i,  (i,2)--obj_
		3. n_insert_cost,计算最小插入成本
		4. deta_f: 目标值的增幅,初始设为0
		5. i:1->5的循环
    		i=1: deta_f=0+n_insert_cost[1,2]-n_insert_cost[0,2]
    		i=2:deta_f=n_insert_cost[1,2]-n_insert_cost[0,2]+  n_insert_cost[2,2]-n_insert_cost[0,2]
    		i=3: deta_f=n_insert_cost[1,2]-n_insert_cost[0,2]
    				 +  n_insert_cost[2,2]-n_insert_cost[0,2]
    				 +  n_insert_cost[3,2]-n_insert_cost[0,2]
    		i=4: deta_f =n_insert_cost[1,2]-n_insert_cost[0,2]
    				 +  n_insert_cost[2,2]-n_insert_cost[0,2]
    				 +  n_insert_cost[3,2]-n_insert_cost[0,2]
    				 +  n_insert_cost[4,2]-n_insert_cost[0,2]
		6. Dudge: regret_n个增幅值的和> 最优插入成本
					更新最优插入节点id
					更新最优插入节点的索引
					更新最优插入节点的obj
二、return : opt_insert_node_no,opt_insert_index

对于一个待插入的序列和一个需要插入的节点。
我们计算假设在每个索引插入后的新序列(们)的obj值。根据obj值从小到大获得插入索引的排序。选中较小的obj值的前regret_n=5个,累计后4个与最小obj的增幅值求和,为deta_f.并将deta_f与最优插入成本n_insert_cost作比较,然后选择是否更新n_insert_cost.
注意:这里做比较不是选择的是最小的obj值,为什么如此设计的原因未知!!!

2.4 执行修复算子

def doRepair(repair_id,reomve_list,model,sol):
    if repair_id==0:
        new_sol=createRandomRepair(reomve_list,model,sol)
    elif repair_id==1:
        new_sol=createGreedyRepair(reomve_list,model,sol)
    else:
        new_sol=createRegretRepair(reomve_list,model,sol)
    return new_sol

  • repair_id: 0-1-2随机数,当前有3种修复方式
    随机修复:基于解sol,随机产生插入索引,随机插入节点
    贪婪修复:对于选择的节点和插入的位置,使得obj增幅最高即可的一组索引和节点
    最值修复:对于选择的节点和插入的位置,使得后续插入的obj增长最高的一组索引和节点

三。选择–破坏and修复的类型


def selectDestoryRepair(model):
    d_weight=model.d_weight
    d_cumsumprob = (d_weight / sum(d_weight)).cumsum()
    d_cumsumprob -= np.random.rand()
    destory_id= list(d_cumsumprob > 0).index(True)

    r_weight=model.r_weight
    r_cumsumprob = (r_weight / sum(r_weight)).cumsum()
    r_cumsumprob -= np.random.rand()
    repair_id = list(r_cumsumprob > 0).index(True)
    return destory_id,repair_id
  • model.d_weight=d_weight = np.ones(2) * 10 =array([10.10]) # 破坏算子权重
  • np.random.rand() 每一个元素是服从0~1均匀分布的随机样本值,也就是返回的结果中的每一个元素值在0-1之间。
  • model.r_weight=np.ones(3) * 10 =array([10,10,10) # 修复算子权重

在这里插入图片描述
在这里插入图片描述

return:

  • 对于破坏算子会随机返回0或1;
  • 对于修复算子会随机返回0,1,2

更新算子权重

electDestoryRepair中可以看到选择破坏算子或者修复算子是被model.d_weight和model.r_weight的值影响的.所以,改变该数组值则会影响选择结果.因此,有了该函数.

def updateWeight(model):
    for i in range(model.d_weight.shape[0]):
        if model.d_select[i]>0:
            model.d_weight[i]=model.d_weight[i]*(1-model.rho)+model.rho*model.d_score[i]/model.d_select[i]
        else:
            model.d_weight[i] = model.d_weight[i] * (1 - model.rho)
    for i in range(model.r_weight.shape[0]):
        if model.r_select[i]>0:
            model.r_weight[i]=model.r_weight[i]*(1-model.rho)+model.rho*model.r_score[i]/model.r_select[i]
        else:
            model.r_weight[i] = model.r_weight[i] * (1 - model.rho)
    model.d_history_select = model.d_history_select + model.d_select
    model.d_history_score = model.d_history_score + model.d_score
    model.r_history_select = model.r_history_select + model.r_select
    model.r_history_score = model.r_history_score + model.r_score
  • model.d_weight=array([ , ]) #破坏算子的权重
  • model.d_select =array([ , ]) # 破坏算子选择次数
  • model.d_score=array([ , ]) # 破坏算子的得分
  • model.d_history_select=array([ , ]) # 破坏算子累计选择次数
  • model.d_history_score =array([ , ])# 破坏算子累计得分
  • model.rho # 权重衰减哔哩
  • model.r_weight=array([ , , ])
  • model.r_select=array([ , , ])# 修复算子选择次数
  • model.r_score==array([ , , ])# 修复算子的得分
  • model.r_history_select=array([ , , ])# 修复算子累计选择次数
  • model.r_history_score=array([ , , ])# 修复算子累计得分
    初始情况
    在这里插入图片描述
    一般情况
    在这里插入图片描述

与d_score相关.d_score的值与主函数的下面代码相关.

# model.r1=30, model.r2=20, model.r3=10
 if new_sol.obj<sol.obj:
     sol=copy.deepcopy(new_sol)
     if new_sol.obj<model.best_sol.obj:
         model.best_sol=copy.deepcopy(new_sol)
         model.d_score[destory_id]+=model.r1 #一等得分值
         model.r_score[repair_id]+=model.r1
     else:
         model.d_score[destory_id]+=model.r2 #二等得分值
         model.r_score[repair_id]+=model.r2
 elif new_sol.obj-sol.obj<T:
     sol=copy.deepcopy(new_sol)
     model.d_score[destory_id] += model.r3 #三等得分值
     model.r_score[repair_id] += model.r3

new_sol是在sol上破坏和修复后的新领域解.

  • 情况一: 新解的obj<原解的obj + 新解的obj<最优解的obj==>得30分
  • 情况2: 新解的obj<原解的obj, 但没有比最优解好==》得20分
  • 情况3:新解比原来的解差==》得10分.

重置得分

在每个epoch中重置得分的初始值.因为会出现比[1.6, 16.6]两者之间的差距跟大的值.如果这样的值一直出现,则失去了选择破坏算子和修复算子的初衷,这样就不是ALNS与LNS没啥大的区别了.

def resetScore(model):
   model.d_select = np.zeros(2)
   model.d_score = np.zeros(2)

   model.r_select = np.zeros(3)
   model.r_score = np.zeros(3)

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

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

相关文章

【每日一题Day121】LC1139最大的以 1 为边界的正方形 | 前缀和数组 + 枚举

最大的以 1 为边界的正方形【LC1139】 给你一个由若干 0 和 1 组成的二维网格 grid&#xff0c;请你找出边界全部由 1 组成的最大 正方形 子网格&#xff0c;并返回该子网格中的元素数量。如果不存在&#xff0c;则返回 0。 写了50分钟写出来了 思路是对的 但就是不够清晰 并且…

ACPI on ARMv8 Servers

文章目录前言一、Why ACPI on ARM&#xff1f;二、Kernel Compatibility三、Relationship with Device Tree四、Booting using ACPI tables五、ACPI Detection六、Device Enumeration七、Driver Recommendations参考资料前言 ARM64处理器除了可以用设备树&#xff08;DT&#…

V90伺服驱动器设置IP地址和PN设备名称的具体方法(2种)

V90伺服驱动器设置IP地址和PN设备名称的具体方法(2种) 1. 通过V-ASSISTANT软件进行配置 首先下载并安装V-ASSISTANT软件,然后将V90通过网线连接到电脑上,注意此时电脑使用的网卡,不能选择无线网卡, SINAMICS-V90伺服调试软件V-ASSISTANT_V1.07.01 打开V-ASSISTANT软件,…

Java 基础面试题——面向对象

目录1.面向对象和面向过程有什么区别&#xff1f;2.面向对象的有哪些特征?3.静态变量和实例变量有什么区别&#xff1f;4.Java 对象实例化顺序是怎样的&#xff1f;5.浅拷贝和深拷贝的区别是什么&#xff1f;5.1.浅拷贝5.2.深拷贝5.3.总结6.Java 中创建对象的方式有哪几种&…

Qt代码单元测试以及报告生成

简介 单元测试是所有测试中最底层的一类测试&#xff0c;是第一个环节&#xff0c;也是最重要的一个环节&#xff0c;是唯一一次有保证能够代码覆盖率达到100%的测试&#xff0c;是整个软件测试过程的基础和前提&#xff0c;单元测试防止了开发的后期因bug过多而失控&#xff0…

< elementUi 组件插件: el-table表格拖拽修改列宽及行高 及 使用注意事项 >

elementUi 组件插件&#xff1a; el-table拖拽修改列宽及行高 及 使用注意事项&#x1f449; 资源Js包下载及说明&#x1f449; 使用教程> 实现原理> 局部引入> 全局引入 &#xff08;在main.js中&#xff09;&#x1f449; 注意事项往期内容 &#x1f4a8;&#x1f4…

Android Studio adb命令

Android Studio在IP连接142的手机进行无线调试 adb tcpip 5555 // 端口 adb shell ifconfig wlan0 //查看ip adb connect 192.168.1.142:5555 当已经连接上142这个ip&#xff0c;还想再连接其他143这个ip 方法如下&#xff1a; adb devices -l 查看当前设备 adb …

直接下载docker镜像包

这周正在做APIcat的企业版研发&#xff0c;准备适配阿里云的计算巢提供企业直接云安装的功能&#xff0c;顺便把原来写过的一个直接下载docker镜像包的小程序挂到了Gitee上面&#xff0c;有需要的可以点击下载 https://gitee.com/extrame/dgethttps://gitee.com/extrame/dget …

安灯呼叫系统助力企业实现精益管理

在当前的生产企业管理过程当中&#xff0c;生产现场难免会出现各种异常情况影响生产&#xff0c;乳设备需要机修、品质问题、物料呼叫等各种情况&#xff0c;在以往这些问题出现过后处理的方式都是些申请单&#xff0c;由人工到各个部门去给领导签字&#xff0c;浪费大量时间沟…

【Unity VR开发】结合VRTK4.0:远距离抓取物体

语录&#xff1a; 我们都生活在阴沟里&#xff0c;但仍有人仰望星空。 前言&#xff1a; 空间场景中的一个常见问题是试图抓取一个无法触及或太远而无法到达的对象。VRTK提供了一种简单的方法来实现这种技术&#xff08;Interactions.PointerInteractors.DistanceGrabber&…

上市公司每股社会贡献值(数据+代码)(2000-2020年)

数据来源&#xff1a;自我整理 时间跨度&#xff1a;2000-2020年 区域范围&#xff1a;上市公司 指标说明&#xff1a; 每股社会贡献值( 净利润所得税费用营业税金及附加支付给职工以及为职工支付的现金本期应付职工薪酬-上期应付职工薪酬财务费用捐赠-排污费及清理费) / 期…

MySQL服务器体系架构 执行原理

前言 前边我们了解了MySQL采用客户端/服务器架构&#xff0c;用户通过客户端程序发送增删改查需求&#xff0c;服务器程序收到请求后处理&#xff0c;并且把处理结果返回给客户端。这篇文章主要看下MySQL服务端是如何处理客户端的请求&#xff0c;感兴趣的小伙伴们可以参考借鉴…

【单目标优化算法】烟花优化算法(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

定时器设计

定时器设计 定时器应用&#xff1a; 游戏的Buff实现&#xff0c;Redis中的过期任务&#xff0c;Linux中的定时任务等等心跳检测&#xff0c;如服务器接收队列满了&#xff0c;tcp客户端会定时探测是否能够发送数据 定时器数据结构选取要求&#xff1a; 需要快速找到到期任务…

防火墙设置实验(12)

预备知识防火墙基本原理 防火墙是一种访问控制技术&#xff0c;位于可信与不可信网络之间&#xff0c;通过设置一系列安全规则对两个网络之间的通信进行控制&#xff0c;检测交换的信息&#xff0c;防止对信息资源的非法存取和访问。防火墙分为软防火墙和硬防火墙&#xff0c;一…

关于分布式的一些基础知识

1、分布式锁 (ngix,zoomkeeper,raft,kafka) 在单机场景下&#xff0c;可以使用语言的内置锁来实现进程或者线程同步。但是在分布式场景下&#xff0c;需要同步的进程可能位于不同的节点上&#xff0c;那么就需要使用分布式锁。 为了保证一个方法或属性在高并发情况下的同一时间…

【面试题】前端春招第二面

不容错过的一些面试题小细节&#xff0c;话不多说&#xff0c;直接看题~大厂面试题分享 面试题库后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★地址&#xff1a;前端面试题库HTML/CSS/Javascript/ES篇&#xff08;1&#xff09;标准盒模型和怪异盒…

k8s集群概念组件简介

一、Kubernetes 集群概述1.2 Kubernetes 资源资源用途服务&#xff08;Service&#xff09;Kubernetes 使用服务在一组 pod 上公开正在运行的应用程序。ReplicaSetKubernetes 使用 ReplicaSet 来维护恒定的 pod 号。Deployment维护应用程序生命周期的资源对象。1.3 Kubernetes …

重生之我是赏金猎人-SRC漏洞挖掘(七)-看我如何从FUZZ到SRC官网XSS

0x01 前奏 本文来自&#xff1a;RGM78sec 截至发稿&#xff0c;vuln早已修复 前不久在挖掘某SRC时&#xff0c;偶然在该SRC官网的编辑器发现了一个接口。 起初以为是任意文件包含能RCE了&#xff0c;后来测试发现只是拼接读取了远程资源站的图片&#xff0c;原本都想着放弃…

TypeScript(二)基本数据类型

前言 距离上篇博客已有两年了&#xff0c;对TS也有了一定的认识&#xff0c;于是计划继续更新系列文章 基本数据类型 在JS中&#xff0c;基本数据类型有&#xff1a;Boolan&#xff08;布尔&#xff09;&#xff0c;Number&#xff08;数字&#xff09;&#xff0c;String&a…