NSGA-II算法实战:从理论到多目标优化问题求解
1. 从“既要又要”说起多目标优化到底在解决什么问题我们做项目、搞设计甚至日常买东西经常会遇到“既要又要”的难题。比如买车你既想要动力强、加速快又希望油耗低、保养便宜比如设计一个产品你希望它性能最强同时又要求成本最低、生产周期最短。这些目标往往是相互矛盾的提升一个另一个就可能变差。这种需要同时考虑多个、且经常冲突的目标的问题就是多目标优化问题。传统的单目标优化比如找函数的最小值答案通常是一个点或者一个明确的数值。但多目标优化没有唯一的“最优解”因为不存在一个解能在所有目标上都比其他解更好。取而代之的是一组“旗鼓相当”的解我们称之为Pareto最优解集也叫非支配解集。在这个集合里任何一个解的改进都必然以牺牲至少一个其他目标为代价。想象一下你面前有一排手机有的拍照顶尖但续航一般有的续航无敌但性能中庸有的性能强悍但价格昂贵。你无法说哪一部是“最好”的因为它们各有所长选择哪一部取决于你更看重拍照、续航、性能还是价格。这排手机就构成了一个Pareto前沿。那么计算机如何帮我们找到这“一排手机”呢这就是多目标进化算法的用武之地。在众多算法中NSGA-II无疑是一个经典中的经典它就像多目标优化领域的“瑞士军刀”稳定、高效、易于理解。我最早接触这个算法是在读研期间用它来优化一个通信基站的参数配置目标是同时最大化覆盖范围和最小化干扰。当时试了好几种方法最后还是NSGA-II跑出来的结果最让人满意前沿面分布均匀收敛速度也快。今天我就把自己这些年用NSGA-II的实战经验从核心思想到代码细节掰开揉碎了讲给你听目标是让你看完就能上手解决自己的“既要又要”难题。2. NSGA-II的核心思想为什么是它脱颖而出在NSGA-II之前其实已经有了第一代NSGA算法。但就像初代产品总有改进空间一样NSGA有几个明显的痛点计算速度慢时间复杂度高、没有采用精英保留策略好的解可能被丢掉、还需要手动调一个共享参数来维持解集的分布性用起来有点麻烦。NSGA-II的提出就是为了解决这三个问题。它的核心设计非常巧妙主要靠三把“刷子”快速非支配排序、拥挤度比较算子和精英保留策略。这三者结合实现了速度快、解集质量高、分布性好的目标。快速非支配排序是给种群里的所有个体“排座次”的。它快速地把大家分成不同的“前沿面”。第一前沿面里的个体不被任何其他个体支配即它们是最优的那一批第二前沿面里的个体只被第一前沿面的个体支配以此类推。这个过程是分层的帮助我们理解解的优劣等级。我理解它的过程有点像筛选简历先把所有明显不符合要求的被支配的放到后面把最亮眼的非支配的放到最前面的一摞。拥挤度比较算子则是解决“好中选优”时的选择问题。在同一前沿面内大家“非支配”的等级是一样的那选谁进入下一代呢NSGA-II引入了一个叫“拥挤度”的概念。简单说就是计算某个解在目标空间里它周围其他解的密集程度。拥挤度小的解说明它周围很“空旷”代表了该区域的多样性。优先保留这些解能让最终找到的Pareto前沿在目标空间里分布得更均匀、更宽广避免所有解都挤在一个小角落里。你可以把它想象成在教室里选座位我们更倾向于选择周围空位多的座位这样整体布局更舒服信息覆盖也更全面。精英保留策略是保证算法不会“开倒车”的关键。它将父代种群和子代种群合并在一起然后从这个更大的集合里按照非支配等级和拥挤度择优选出下一代种群。这意味着每一代中最优秀的个体都会被保留下来不会因为交叉变异而丢失。这在实际应用中非常重要能显著加快收敛速度并确保解的质量稳步提升。把这三点结合起来NSGA-II的工作流程就清晰了初始化一个随机种群 - 通过交叉变异产生子代 - 合并父子两代 - 快速非支配排序分层 - 计算同一层内的拥挤度 - 优先选择排名靠前的层在同层内优先选择拥挤度大的个体组成新的父代 - 循环往复直到满足终止条件。这个流程逻辑清晰实现起来也相对模块化是我喜欢它的一个重要原因。3. 手把手实现用Python从零搭建NSGA-II理论说得再多不如动手写一行代码。这里我将用一个经典的测试函数ZDT1来演示NSGA-II的完整实现过程。ZDT1是一个双目标最小化问题它的Pareto前沿是凸的并且有已知的理论最优解非常适合用来验证算法是否正确。首先我们需要安装必要的库主要是用于科学计算的NumPy和用于结果可视化的Matplotlib。如果你还没安装在命令行里 pip install numpy matplotlib 一下就好。import numpy as np import matplotlib.pyplot as plt3.1 问题定义与种群初始化我们首先定义ZDT1问题的两个目标函数。决策变量是一个30维的向量每个变量都在[0,1]之间。def zdt1(individual): 计算ZDT1问题的两个目标函数值 f1 individual[0] # 第一个目标就是第一个决策变量 g 1 9.0 * np.sum(individual[1:]) / (len(individual) - 1) # 辅助函数g(x) f2 g * (1 - np.sqrt(f1 / g)) # 第二个目标 return np.array([f1, f2])接下来是初始化种群。我们随机生成一定数量的个体每个个体就是一个30维的向量。def initialize_population(pop_size, num_variables, lower_bound0.0, upper_bound1.0): 初始化种群 population np.random.uniform(lowlower_bound, highupper_bound, size(pop_size, num_variables)) return population3.2 算法的三大核心组件实现这是整个算法的灵魂部分我们一步步来。首先是快速非支配排序。这里我严格按照原始论文的算法来实现。我们需要为每个个体计算两个属性支配它的个体数量n_p以及它支配的个体集合S_p。def fast_non_dominated_sort(population, fitness_values): 快速非支配排序 :param population: 种群二维数组 :param fitness_values: 每个个体对应的目标函数值二维数组 :return: 前沿面列表每个元素是该前沿面中个体的索引列表 pop_size len(population) # 初始化数据结构 S [[] for _ in range(pop_size)] # 个体p支配的解集合 n np.zeros(pop_size, dtypeint) # 支配个体p的解的数量 rank np.zeros(pop_size, dtypeint) # 每个个体的前沿面等级 fronts [[]] # 存储每一层前沿面fronts[0]是第一层 # 第一遍遍历计算S和n for p in range(pop_size): S[p] [] for q in range(pop_size): if p q: continue # 判断支配关系 if dominates(fitness_values[p], fitness_values[q]): S[p].append(q) elif dominates(fitness_values[q], fitness_values[p]): n[p] 1 if n[p] 0: # 如果没有个体支配p则p属于第一前沿面 rank[p] 0 fronts[0].append(p) # 分层计算后续前沿面 i 0 while fronts[i]: # 当前前沿面不为空 next_front [] for p in fronts[i]: # 遍历当前前沿面的每个个体 for q in S[p]: # 遍历被p支配的个体 n[q] - 1 # 因为支配者p已被移出所以q的被支配计数减1 if n[q] 0: # 如果q不再被任何当前前沿面之外的个体支配 rank[q] i 1 next_front.append(q) i 1 if next_front: fronts.append(next_front) else: break return fronts, rank def dominates(a, b): 判断解a是否支配解b。对于最小化问题a支配b当且仅当a在所有目标上都不比b差且至少在一个目标上比b好。 return np.all(a b) and np.any(a b)然后是拥挤度计算。拥挤度衡量的是同一个前沿面内个体周围的“拥挤”程度。计算时需要对每个目标函数分别排序处理。def crowding_distance_assignment(front, fitness_values): 计算指定前沿面内所有个体的拥挤度 :param front: 一个前沿面包含个体索引的列表 :param fitness_values: 所有个体的适应度值 :return: 该前沿面内每个个体的拥挤度列表与front顺序对应 l len(front) distances np.zeros(l) if l 0: return distances num_objectives fitness_values.shape[1] for m in range(num_objectives): # 根据第m个目标函数值对前沿面个体排序 sorted_indices np.argsort([fitness_values[i, m] for i in front]) # 边界个体的拥挤度设为无穷大确保它们被选中 distances[sorted_indices[0]] np.inf distances[sorted_indices[-1]] np.inf # 计算中间个体的拥挤度 f_max fitness_values[front[sorted_indices[-1]], m] f_min fitness_values[front[sorted_indices[0]], m] if f_max f_min: # 防止除零 continue for i in range(1, l-1): idx_current sorted_indices[i] idx_next sorted_indices[i1] idx_prev sorted_indices[i-1] distances[idx_current] (fitness_values[front[idx_next], m] - fitness_values[front[idx_prev], m]) / (f_max - f_min) return distances最后是选择算子。我们需要根据非支配等级和拥挤度从合并的种群中选出下一代。这里采用锦标赛选择。def selection(population, fitness_values, fronts, crowding_distances, pop_size): 根据非支配等级和拥挤度选择下一代种群 selected_indices [] current_front_idx 0 # 优先选择非支配等级高的前沿面直到放满或超过种群大小 while len(selected_indices) len(fronts[current_front_idx]) pop_size: # 将该前沿面的所有个体加入 selected_indices.extend(fronts[current_front_idx]) current_front_idx 1 # 如果最后一个前沿面不能全部放入则根据拥挤度挑选 if len(selected_indices) pop_size: last_front fronts[current_front_idx] # 计算该前沿面需要挑选的个体数 remaining pop_size - len(selected_indices) # 根据拥挤度降序排序挑选前remaining个 sorted_last_front sorted(last_front, keylambda i: crowding_distances[i], reverseTrue) selected_indices.extend(sorted_last_front[:remaining]) # 根据选中的索引返回新的种群和适应度 new_population population[selected_indices] new_fitness fitness_values[selected_indices] return new_population, new_fitness3.3 遗传操作交叉与变异有了选择我们还需要创造新个体的方法这就是遗传算法的“探索”能力所在。这里我采用模拟二进制交叉和多项式变异这是NSGA-II原文中推荐的方法效果比较稳定。def simulated_binary_crossover(parent1, parent2, eta_c20): 模拟二进制交叉 (SBX) u np.random.rand(len(parent1)) beta np.empty_like(u) mask u 0.5 beta[mask] (2 * u[mask]) ** (1.0 / (eta_c 1)) beta[~mask] (1.0 / (2 * (1 - u[~mask]))) ** (1.0 / (eta_c 1)) child1 0.5 * ((1 beta) * parent1 (1 - beta) * parent2) child2 0.5 * ((1 - beta) * parent1 (1 beta) * parent2) return child1, child2 def polynomial_mutation(individual, lower_bound, upper_bound, eta_m20): 多项式变异 mutated individual.copy() for i in range(len(individual)): if np.random.rand() 1.0 / len(individual): # 变异概率 u np.random.rand() delta 0.0 if u 0.5: delta (2 * u) ** (1.0 / (eta_m 1)) - 1 else: delta 1 - (2 * (1 - u)) ** (1.0 / (eta_m 1)) mutated[i] delta * (upper_bound - lower_bound) # 边界处理 mutated[i] np.clip(mutated[i], lower_bound, upper_bound) return mutated3.4 主循环把所有模块组装起来现在我们把上面的所有零件组装成一台可以运行的“机器”。def nsga2(pop_size100, num_variables30, generations250, crossover_prob0.9, mutation_prob1.0/30): NSGA-II主函数 # 1. 初始化 population initialize_population(pop_size, num_variables) fitness np.array([zdt1(ind) for ind in population]) for gen in range(generations): # 2. 生成子代 offspring [] offspring_fitness [] # 使用二元锦标赛选择父代 for _ in range(pop_size // 2): # 随机选择两个个体进行锦标赛 idx1, idx2 np.random.choice(pop_size, size2, replaceFalse) # 比较非支配等级和拥挤度这里简化直接随机选 parent1 population[idx1] parent2 population[idx2] # 交叉 if np.random.rand() crossover_prob: child1, child2 simulated_binary_crossover(parent1, parent2) else: child1, child2 parent1.copy(), parent2.copy() # 变异 child1 polynomial_mutation(child1, 0.0, 1.0) child2 polynomial_mutation(child2, 0.0, 1.0) offspring.extend([child1, child2]) offspring_fitness.extend([zdt1(child1), zdt1(child2)]) offspring np.array(offspring) offspring_fitness np.array(offspring_fitness) # 3. 合并父子代 combined_pop np.vstack([population, offspring]) combined_fit np.vstack([fitness, offspring_fitness]) # 4. 快速非支配排序 fronts, rank fast_non_dominated_sort(combined_pop, combined_fit) # 5. 计算拥挤度为最后一层需要参与选择的个体计算 crowding_distances np.zeros(len(combined_pop)) for front in fronts: if len(front) 0: front_distances crowding_distance_assignment(front, combined_fit) for i, idx in enumerate(front): crowding_distances[idx] front_distances[i] # 6. 环境选择选出下一代 population, fitness selection(combined_pop, combined_fit, fronts, crowding_distances, pop_size) # 可选每50代打印一次进度 if gen % 50 0: # 找出第一前沿面 first_front_indices [i for i, r in enumerate(rank) if r 0] first_front_fitness combined_fit[first_front_indices] print(fGeneration {gen}: First front size {len(first_front_fitness)}) # 最终从最后一代中提取第一前沿面Pareto最优近似解 final_fronts, _ fast_non_dominated_sort(population, fitness) pareto_front fitness[final_fronts[0]] pareto_solutions population[final_fronts[0]] return pareto_front, pareto_solutions, population, fitness运行这个主函数我们就可以得到优化后的Pareto前沿近似解了。# 运行算法 pareto_front, pareto_solutions, final_pop, final_fit nsga2(pop_size100, generations250) # 可视化结果 plt.figure(figsize(10, 6)) plt.scatter(final_fit[:, 0], final_fit[:, 1], cgray, alpha0.5, s20, labelFinal Population) plt.scatter(pareto_front[:, 0], pareto_front[:, 1], cred, s50, labelPareto Front (Approx.)) # 绘制ZDT1的真实Pareto前沿已知理论解 x np.linspace(0, 1, 100) y_true 1 - np.sqrt(x) plt.plot(x, y_true, b--, linewidth2, labelTrue Pareto Front (ZDT1)) plt.xlabel(Objective 1 (f1)) plt.ylabel(Objective 2 (f2)) plt.title(NSGA-II Optimization Result on ZDT1 Problem) plt.legend() plt.grid(True, alpha0.3) plt.show()当你运行这段代码应该能看到红色的点算法找到的Pareto前沿紧密地贴合在蓝色的虚线真实前沿附近而灰色的点最终种群则分布在前沿的后面。这说明我们的NSGA-II实现是成功的它有效地找到了一个分布均匀、逼近真实前沿的解集。4. 调参与实战让NSGA-II在你的问题上发挥威力代码跑通了但直接套用到你自己的问题上效果可能不尽如人意。别急这就像拿到了好厨具还得掌握火候和调料。NSGA-II有几个关键参数直接影响它的“性能”和“口味”。种群大小pop_size这是最重要的参数之一。种群太小搜索能力不足容易陷入局部前沿种群太大计算开销会成倍增加。我的经验是对于决策变量在10-30个的中等问题种群大小设置在100到200之间是个不错的起点。如果问题非常复杂变量多目标多可能需要增加到300甚至500。你可以先从小种群开始观察收敛情况再逐步调整。迭代代数generations要跑多少代这没有定论。一个实用的方法是观察算法的收敛情况。我通常会在代码里记录每一代第一前沿面的超体积或间距指标的变化。当这些指标在连续几十代内不再有显著改善时就可以停止了。对于ZDT1这种标准问题250代通常能得到很好的结果对于实际问题可能需要上千代。交叉概率crossover_prob和变异概率mutation_prob交叉是“利用”现有好基因变异是“探索”新可能。通常交叉概率设得较高如0.8-0.9变异概率设得较低如1/变量数。eta_c和eta_m是分布指数控制着交叉和变异算子的“侵略性”。值越大产生的子代离父代越近搜索更精细值越小子代可能与父代差异更大探索更广泛。通常eta_c设为10-30eta_m设为15-20。我个人的习惯是从默认值20开始如果发现算法收敛太快、多样性下降就适当调小eta_c或调高变异概率。处理实际问题时你还需要注意这些坑目标函数尺度不一致如果你的两个目标一个范围是[0, 1000]另一个是[0, 1]那么第二个目标在拥挤度计算中几乎会被忽略。务必在计算拥挤度之前对目标函数值进行归一化处理。可以用每一代中该目标的最大最小值进行缩放。约束处理NSGA-II原始版本是针对无约束问题的。如果你的问题有约束比如某个变量之和必须为1常用的方法是罚函数法或约束支配原则。罚函数法简单但罚因子难调约束支配原则更优雅它修改了支配的定义首先比较约束违反程度违反程度小的个体支配违反程度大的只有都不违反或违反程度相同时才比较目标函数值。我在处理工程约束时更偏爱约束支配原则。性能评估怎么知道跑出来的结果好不好除了肉眼观察前沿的分布还可以用定量指标超体积衡量解集所支配的目标空间体积越大越好。这是最综合的指标。间距衡量解集中个体分布的均匀程度越小越好。世代距离衡量找到的解集与真实Pareto前沿如果已知的平均距离越小越好。 在论文或报告中用这些指标说话会更有说服力。5. 超越ZDT1NSGA-II能解决哪些真实问题学以致用我们来看看NSGA-II在更广阔天地里的身影。它绝不仅仅是个玩具算法。在工程设计与优化领域这是NSGA-II的传统强项。比如天线设计我们需要在增益、带宽、尺寸等多个指标间权衡。我曾参与过一个滤波器设计项目目标是在满足通带、阻带衰减要求的前提下最小化插入损耗和电路尺寸。将滤波器的结构参数如微带线长度、宽度作为决策变量NSGA-II帮助我们找到了一系列折中设计方案供工程师根据实际PCB板空间和性能要求进行最终选择大大缩短了设计周期。在机器学习与人工智能领域模型调参本身就是一个多目标问题。我们不仅关心模型的准确率还关心模型的复杂度影响推理速度、泛化能力、甚至训练能耗。使用NSGA-II来自动搜索神经网络的架构层数、每层神经元数和超参数学习率、正则化系数可以得到一个帕累托最优模型集合。你可以从中选择一个高精度但复杂的模型部署在服务器上同时选择一个轻量级但精度稍逊的模型部署在移动设备上。这种自动化的多目标模型搜索正成为AutoML中的重要组成部分。在资源调度与路径规划领域问题天然就是多目标的。例如数据中心的任务调度既要最小化总完成时间最大完工时间又要最小化总能耗。每个任务在不同机器上的运行时间和能耗不同NSGA-II可以用来搜索调度方案为管理员提供从“最快但最耗电”到“最省电但较慢”的一系列可选方案。在物流配送的车辆路径问题中同时优化总行驶距离和所有客户的平均等待时间也是NSGA-II的典型应用场景。在金融投资组合优化中经典的马科维茨模型就是双目标最大化预期收益最小化风险方差。NSGA-II可以用来求解这个模型得到一条从“高风险高收益”到“低风险低收益”的有效前沿。投资者可以根据自己的风险偏好在这条前沿上选择最适合自己的投资组合点这比传统的单目标优化如给定风险下求最大收益提供了更全面的决策视角。从我自己的项目经验来看将一个问题成功建模为NSGA-II能求解的形式往往比调参本身更重要。你需要清晰地定义决策变量什么可以变、目标函数要优化什么注意量纲以及约束条件必须满足什么。这个过程本身就是对你所研究问题的深刻再思考。当算法跑起来并输出那一组代表着不同权衡方案的点时那种“一览众山小”的感觉是单目标优化无法给予的。它提供的不是唯一答案而是一张清晰的“权衡地图”把选择的权力和依据交还给了决策者。

相关新闻

UE5 NDisplay 单主机配置与打包实战指南

UE5 NDisplay 单主机配置与打包实战指南

1. 为什么选择单主机NDisplay方案? 如果你正在用虚幻引擎5做展览展示、虚拟拍摄,或者需要在一块大屏幕上分区域显示不同的3D内容,那你肯定听说过NDisplay。官方文档和很多教程一上来就讲多台电脑联网的集群渲染,配置那个SwitchBoa…

2026/5/17 12:31:52 阅读更多 →
mediapipe实战:从零搭建跨平台AI视觉开发环境

mediapipe实战:从零搭建跨平台AI视觉开发环境

1. 为什么选择MediaPipe?从零开始的跨平台环境搭建心路 最近在做一个智能交互项目,需要用到实时的人脸检测和手势识别。一开始看了几家商业方案的报价,好家伙,那价格直接劝退。转头就瞄上了Google开源的MediaPipe,这玩…

2026/7/4 9:38:02 阅读更多 →
TagSpaces vs 传统文件夹:为什么说标签化管理才是文件整理的未来?附实战配置分享

TagSpaces vs 传统文件夹:为什么说标签化管理才是文件整理的未来?附实战配置分享

TagSpaces vs 传统文件夹:为什么说标签化管理才是文件整理的未来?附实战配置分享 不知道你有没有过这样的经历:为了找一份半年前做的项目方案,你需要在“工作/2023年/第三季度/客户A/提案/最终版”这样层层嵌套的文件夹里来回翻找…

2026/7/4 20:08:39 阅读更多 →

最新新闻

超棒!无需网络的离线地图应用 Organic Maps,2025 年安装量达 600 万!

超棒!无需网络的离线地图应用 Organic Maps,2025 年安装量达 600 万!

Organic Maps:隐私保护离线地图应用Organic Maps 是注重隐私保护的离线地图与 GPS 应用,适用于徒步、骑行和驾车场景。它免费、无广告、不追踪,由开源社区及创建 MapsWithMe/Maps.Me 应用的同一批开发者打造,数据源自 OpenStreetM…

2026/7/6 9:08:13 阅读更多 →
基于Go抓包+Python CNN的本地化异常流量检测工具(含GUI界面与预训练模型)

基于Go抓包+Python CNN的本地化异常流量检测工具(含GUI界面与预训练模型)

本文还有配套的精品资源,点击获取 简介:直接在本地运行的异常流量识别工具,底层用Go语言实现高效网络数据包捕获和会话解析,支持TCP/UDP/ICMP协议的实时流量提取、窗口计数、主机行为建模和服务识别;上层采用Python…

2026/7/6 9:08:13 阅读更多 →
MATLAB直接跑的7输入RBF回归工具:带数据、代码和结果图,开箱即用

MATLAB直接跑的7输入RBF回归工具:带数据、代码和结果图,开箱即用

本文还有配套的精品资源,点击获取 简介:这个MATLAB RBF神经网络回归工具包专为7个输入特征预测1个连续数值目标设计,不需要额外工具箱,兼容2018b及以上版本。主程序MainRBFR.m一键完成数据加载、RBF网络搭建、训练、测试和预测…

2026/7/6 9:08:13 阅读更多 →
Playwright网络请求拦截与模拟实战:从原理到六大应用场景详解

Playwright网络请求拦截与模拟实战:从原理到六大应用场景详解

1. 项目概述与核心价值 如果你正在用 Playwright 做自动化测试或者数据抓取,有没有遇到过这样的场景:测试一个下单流程,但支付接口总是不稳定,导致你的测试脚本动不动就失败;或者你想测试前端页面在网络超时或服务器返…

2026/7/6 9:04:10 阅读更多 →
PyTorch LSTM 多变量多任务预测:3种损失函数加权策略对比与代码实现

PyTorch LSTM 多变量多任务预测:3种损失函数加权策略对比与代码实现

PyTorch LSTM 多变量多任务预测:3种损失函数加权策略对比与代码实现在时间序列预测领域,多任务学习正逐渐成为提升模型性能的关键技术。当我们需要同时预测多个相关变量时,传统的单任务学习方法往往难以捕捉变量间的复杂关系。本文将深入探讨…

2026/7/6 9:04:10 阅读更多 →
Python Selenium自动化校园网登录:从环境搭建到打包部署全攻略

Python Selenium自动化校园网登录:从环境搭建到打包部署全攻略

1. 项目概述与核心价值 校园网登录,尤其是像深澜Srun这类需要跳转认证页面的系统,对于需要长时间保持网络连接的用户来说,是个不大不小的痛点。手动打开浏览器、输入账号密码、点击登录,这套流程每天重复几次,不仅繁琐…

2026/7/6 9:04:10 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻