在数据可视化的过程中,绘制网络拓扑图是很重要的,它能清晰呈现一个复杂网络的结构,节点的重要性和关系。比如下面几张图:

下面这张图是我的软件绘制的:

这些都有一个共同的问题,就是如何让图绘制的更加美观? 复杂的图结构,已经没法人工布局了。所以计算机自动布局,就成了一个有趣而重要的话题。我们将其称为布点算法。

基本原理

一个好的布点算法应该能尽量满足以下四个特点:

  • 对称性:绘制网络对应将具有相同结构的子图围绕绘图中心平衡布局
  • 连接角最大化原则,使同一节点任意两条边形成的角度尽量大
    边交叉数量最小原则(最重要):绘图时应尽量减少相互交叉边的数量。
  • 直线边原则:边尽量直线,避免曲线,同时线尽可能短。其中,尽量避免交叉是最重要的,然而,并不是所有的图结构都能满足这个要求。同时,在性能上,还应当满足如下需求:

  • 算法应当能处理尽可能复杂和庞大的图,比如上万甚至上百万的节点

  • 运算速度还要尽量快,使其能够在短时间内完成操作
  • 可交互性,当用户调整了某一个点的位置,或改变了一些参数,算法是否能够动态调节?

如何表示一个网络

图分为有向图和无向图,有权图和无权图。当图有权时,权重反映了节点间的关系,通常值越大,节点关系越紧密。

  • 二维矩阵 最简单也最方便的表示方法,当网络结构巨大时,存储空间浪费是惊人的,而且如果图是无向图,则矩阵是对称的。但存取效率最高
  • 临接表, 即将边表达为一个数组,(x,y,dist) *N 对于稀疏图来说,存储效率很高,但读写效率低。一种简单的优化方法是对x和y 分别进行排序,排序后按二分查找,即可实现快速存取。
  • 实时计算 当网络图巨大,而计算关系本身的运算并不复杂时,可以采用,此时dist[x][y] 被转换为一个函数 GetDistance(x,y), 实时完成计算。这是一种很有趣且比较实用的方法,尤其是当网络图稀疏时。
    为了简化,我们采用二维矩阵作为存取结构,相信读者可以很容易的将其改写为其他形式。

算法介绍

力导引算法

力导引布局的方法可以产生相当优美的网络布局,并充分展现网络的整体结构及其自同构特征。该方法最早由Eades在1984年提出。
基本思想是将网络看成一个顶点为钢环,边为弹簧的物理系统,系统被赋予某个初始状态以后,弹簧弹力(引力和斥力)的作用会导致钢环移动,这种运动直到系统总能量减少到最小值停止。每次循环都要计算任意一对点的斥力和相邻两个点的引力,计算复杂度为O(N2)。

基本上绝大多数算法都遵循着这样的原则,即:

  1. 将网络看成一个顶点为钢环,边为弹簧的物理系统
  2. 不断迭代,使整个系统的总能量达到最小

为了简化,弹簧的受力并不遵循胡克定律,而是自己定义的受力公式,同时决定引力只在相邻节点间。因此,最基本的弹簧算法的伪代码如下:

随机分布G的节点
重复m次
{
计算作用在各个节点上的力
按照力移动节点的位置
}
根据节点位置绘图


衡量一个布点算法的美学标准,可以用下面几个公式来计算:

其中:

最后:


改进算法

FR算法改进了弹簧算法,是现在用途最为广泛的布点算法,很多算法都是在这个算法上改进的。

FR受到了天体重力系统的启发,使用力来计算每个节点的速度,而不是加速度,从而得到每个节点应当移动的距离。它的每次迭代分为三个步骤:

  • 计算节点之间的排斥力
  • 计算相邻节点之间的吸引力
  • 综合吸引力和排斥力,通过最大位移限制移动的距离

使用模拟退火算法,使得在图变得越来越稳定时,温度变得更低,节点每次移动的距离就变得更小。其主要原因是防止震荡。

KK算法使得能量最小化,在图的布局上减少了边的交叉,除了需要计算所有节点对之间的最短路径,并不需要其他理论知识。它虽然每一步的计算复杂度高于FR算法,但迭代次数较少,使其执行速度和效果都比FR好。

实现和性能

上一部分是两个月前写的,今天发现了,想把剩下的补足。

本来想把之前的布点算法的代码抽出来,做成一个独立的工程,做成附件发布出来,奈何之前学弟写的代码实在太乱乱乱了,我改了老半天,依然还有一大堆的问题,渐渐地我失去了耐心。觉得这篇文章要太监了。

说点感性的认识吧。布点算法很重要,因为能直观的看出复杂网络的结构和关系。但在点很多时,性能就得考虑,在时间上,几百个点的计算几乎瞬间即可完成,上万个节点的图,采用高性能的布点算法,在牺牲效果的情况下,能在两分钟内跑完,使用力导引算法,则要花费10分钟以上。这可能和学弟实现的代码不够高效率有关系。

附录给出了一个简单的力导引实现,接口应该很容易,一看就能明白。

目前,推荐SM算法,相关链接:

https://en.wikipedia.org/wiki/Stress_Majorization

不过,不建议重复造轮子,有现成的graphviz可以使用。应该把时间花在更重要的事情上。如果我当时没有对语言间互操作那么反感,就不用耽搁那么多功夫重写C#版本了。

其实原本的代码中,包含了SM,超大规模布点,力导引这三种算法的完整代码,有需要的,可以站内我。如果你看到了这里,说明你还是蛮有耐心的。

附录:C#实现

using System;
using System.Collections.Generic;
using System.Linq;namespace GraphdotNet.ForceAtlas2Algo
{internal class ForceAtlas2{#region Constructors and Destructorspublic ForceAtlas2(int nodeNum){nodes = new Node[nodeNum];for (var i = 0; i < nodes.Length; i++){nodes[i] = new Node();}Degree = new int[nodeNum];}#endregion#region Methodsprivate double GetEdgeWeight(int edgeIdx){return Adjacency[3*edgeIdx + 2];}#endregion#region Constants and Fieldsinternal double Speed;private readonly Node[] nodes;private double outboundAttCompensation = 1;private Region rootRegion;#endregion#region Propertiesinternal bool AdjustSizes { get; set; }internal bool BarnesHutOptimize { get; set; }internal double BarnesHutTheta { get; set; }internal double EdgeWeightInfluence { get; set; }internal double Gravity { get; set; }internal double JitterTolerance { get; set; }internal bool LinLogMode { get; set; }internal bool OutboundAttractionDistribution { get; set; }internal double ScalingRatio { get; set; }internal bool StrongGravityMode { get; set; }private double[] Adjacency { get; set; }private int[] Degree { get; }private int[] EdgeAdjacency { get; set; }#endregion#region Public Methodspublic bool CanAlgo(){return (nodes.Length != 0);}public void EndAlgo(){}public IEnumerable<double> GetX(){return nodes.Select(node => node.X);}public IEnumerable<double> GetY(){return nodes.Select(node => node.Y);}public void GoAlgo(){if (nodes.Length == 0){return;}initialize node datavar nodeNum = nodes.Length;foreach (var n in nodes){n.Olddx = n.Dx;n.Olddy = n.Dy;n.Dx = 0;n.Dy = 0;}// If Barnes Hut active, initialize root RegionImplif (BarnesHutOptimize){rootRegion = new Region(nodes);rootRegion.BuildSubRegions();}// If outboundAttractionDistribution active, compensate.if (OutboundAttractionDistribution){outboundAttCompensation = 0;foreach (var n in nodes){outboundAttCompensation += n.Mass;}outboundAttCompensation /= nodeNum;}// Repulsion (and gravity)var repulsion = ForceFactory.Builder.BuildRepulsion(AdjustSizes, ScalingRatio);const int @from = 0;var to = nodeNum;var rgCalculator = new RepulsionGravityCalculate(nodes,from,to,BarnesHutOptimize,BarnesHutTheta,Gravity,StrongGravityMode ? (ForceFactory.Builder.GetStrongGravity(ScalingRatio)) : (repulsion),ScalingRatio,rootRegion,repulsion);rgCalculator.Run();// Attractionvar attraction = ForceFactory.Builder.BuildAttraction(LinLogMode,OutboundAttractionDistribution,AdjustSizes,1*(OutboundAttractionDistribution ? (outboundAttCompensation) : (1)));var edgeNum = EdgeAdjacency.Length/2;const double epsilon = 1e-6;if (Math.Abs(EdgeWeightInfluence - 0) < epsilon){for (var i = 0; i < edgeNum; i++){attraction.Apply(nodes[EdgeAdjacency[2*i]], nodes[EdgeAdjacency[2*i + 1]], 1);}}else if (Math.Abs(EdgeWeightInfluence - 1) < epsilon){for (var i = 0; i < edgeNum; i++){attraction.Apply(nodes[EdgeAdjacency[2*i]],nodes[EdgeAdjacency[2*i + 1]],GetEdgeWeight(i));}}else{for (var i = 0; i < edgeNum; i++){attraction.Apply(nodes[EdgeAdjacency[2*i]],nodes[EdgeAdjacency[2*i + 1]],Math.Pow(GetEdgeWeight(i), EdgeWeightInfluence));}}// Auto adjust speedvar totalSwinging = 0d; // How much irregular movementvar totalEffectiveTraction = 0d; // Hom much useful movementforeach (var n in nodes){var swinging = Math.Sqrt((n.Olddx - n.Dx)*(n.Olddx - n.Dx) + (n.Olddy - n.Dy)*(n.Olddy - n.Dy));totalSwinging += n.Mass*swinging;// If the node has a burst change of direction, then it's not converging.totalEffectiveTraction += n.Mass*0.5*Math.Sqrt((n.Olddx + n.Dx)*(n.Olddx + n.Dx) + (n.Olddy + n.Dy)*(n.Olddy + n.Dy));}// We want that swingingMovement < tolerance * convergenceMovementvar targetSpeed = JitterTolerance*JitterTolerance*totalEffectiveTraction/totalSwinging;// But the speed shoudn't rise too much too quickly, since it would make the convergence drop dramatically.const double maxRise = 0.5; // Start rise: 50%Speed = Speed + Math.Min(targetSpeed - Speed, maxRise*Speed);// Apply forcesforeach (var n in nodes){// Adaptive auto-speed: the speed of each node is lowered// when the node swings.var swinging = Math.Sqrt((n.Olddx - n.Dx)*(n.Olddx - n.Dx) + (n.Olddy - n.Dy)*(n.Olddy - n.Dy));//double factor = speed / (1f + Math.sqrt(speed * swinging));var factor = Speed/(1 + Speed*Math.Sqrt(swinging));n.X += n.Dx*factor;n.Y += n.Dy*factor;}}public void InitAlgo(double[] adjacencyInput){foreach (var n in nodes){n.ResetNode();}var ran = new Random();foreach (var n in nodes){n.X = (0.01 + ran.NextDouble())*1000 - 500;n.Y = (0.01 + ran.NextDouble())*1000 - 500;}Speed = 1.0;Adjacency = adjacencyInput;var len = Adjacency.Length;var edgeNum = len/3;EdgeAdjacency = new int[edgeNum*2];for (var i = 0; i < edgeNum; i++){EdgeAdjacency[2*i] = (int) Adjacency[3*i];EdgeAdjacency[2*i + 1] = (int) Adjacency[3*i + 1];}for (var i = 0; i < 2*edgeNum; i++){Degree[EdgeAdjacency[i]]++;}var nodeNum = nodes.Length;for (var i = 0; i < nodeNum; i++){nodes[i].Mass = 1 + Degree[i];}}public void OutlierProcess(){var maxRadius = double.NegativeInfinity;foreach (var n in nodes){if (n.Mass >= 2){maxRadius = Math.Max(maxRadius, Math.Sqrt(n.X*n.X + n.Y*n.Y));}}maxRadius += 10;var maxRadiusCeil = (int) Math.Ceiling(maxRadius);var ran = new Random();foreach (var n in nodes){if (n.Mass <= 1){n.X = ran.Next(-maxRadiusCeil*20, maxRadiusCeil*20)/20.0;n.Y = Math.Pow(-1.0, Math.Floor(n.X))*Math.Sqrt(maxRadius*maxRadius - n.X*n.X);}}}public void ResetPropertiesValues(){var nodeNum = nodes.Length;// TuningScalingRatio = nodeNum >= 100 ? 2.0 : 10.0;StrongGravityMode = false;Gravity = 1.0;// BehaviorOutboundAttractionDistribution = false;LinLogMode = false;EdgeWeightInfluence = 1.0;// Performanceif (nodeNum >= 50000){JitterTolerance = 10.0;}else if (nodeNum >= 5000){JitterTolerance = 1.0;}else{JitterTolerance = 0.1;}BarnesHutOptimize = nodeNum >= 1000;BarnesHutTheta = 1.2;}#endregion}
}

布点算法的原理和实现相关推荐

  1. deeplearning算法优化原理

    deeplearning算法优化原理 目录 • 量化原理介绍 • 剪裁原理介绍 • 蒸馏原理介绍 • 轻量级模型结构搜索原理介绍 Quantization Aware Training量化介绍 1.1 ...

  2. 【机器学习入门】(1) K近邻算法:原理、实例应用(红酒分类预测)附python完整代码及数据集

    各位同学好,今天我向大家介绍一下python机器学习中的K近邻算法.内容有:K近邻算法的原理解析:实战案例--红酒分类预测.红酒数据集.完整代码在文章最下面. 案例简介:有178个红酒样本,每一款红酒 ...

  3. 前景背景分割——ostu算法的原理及实现 OpenCV (八)

    OpenCV [八]--前景背景分割--ostu算法的原理及实现 实验结果 代码实现 实现原理 参考资料 实验结果 代码实现 #include<opencv2/opencv.hpp> #i ...

  4. 视觉SLAM开源算法ORB-SLAM3 原理与代码解析

    来源:深蓝学院,文稿整理者:何常鑫,审核&修改:刘国庆 本文总结于上交感知与导航研究所科研助理--刘国庆关于[视觉SLAM开源算法ORB-SLAM3 原理与代码解析]的公开课. ORB-SLA ...

  5. 神经网络感知器算法调整原理是什么

    算法调整原理 如果点分类正确,则什么也不做. 如果点分类为正,但是标签为负,则分别减去 αp,αq, 和 α 至 w_1, w_2,w1​,w2​, 和 bb 如果点分类为负,但是标签为正,则分别将α ...

  6. Adaboost 算法的原理与推导

    Adaboost 算法的原理与推导 0 引言 一直想写Adaboost来着,但迟迟未能动笔.其算法思想虽然简单:听取多人意见,最后综合决策,但一般书上对其算法的流程描述实在是过于晦涩.昨日11月1日下 ...

  7. layer output 激活函数_一文彻底搞懂BP算法:原理推导+数据演示+项目实战(下篇)...

    在"一文彻底搞懂BP算法:原理推导+数据演示+项目实战(上篇)"中我们详细介绍了BP算法的原理和推导过程,并且用实际的数据进行了计算演练.在下篇中,我们将自己实现BP算法(不使用第 ...

  8. TCP协议、算法和原理

    TCP是一个巨复杂的协议,因为他要解决很多问题,而这些问题又带出了很多子问题和阴暗面.所以学习TCP本身是个比较痛苦的过程,但对于学习的过程却能让人有很多收获. 关于TCP这个协议的细节,我还是推荐你 ...

  9. PageRank算法--从原理到实现

    本文将介绍PageRank算法的相关内容,具体如下: 1.算法来源 2.算法原理 3.算法证明 4.PR值计算方法 4.1 幂迭代法 4.2 特征值法 4.3 代数法 5.算法实现 5.1 基于迭代法 ...

最新文章

  1. 微服务平台的发展趋势
  2. linux下默认有哪些语言支持,修改 Linux操作系统下的显示默认支持语言
  3. python真的超过java了吗-Python 的开发效率真的比 Java高吗?
  4. 评估指标:混淆矩阵、PR、mAP、ROC、AUC
  5. c++将int转换成string_Integer与int的区别 (== 与 equal)
  6. 地址栏 输入 参数 刷新参数丢失_小米11 Pro屏幕参数曝光:2K屏幕+120Hz刷新率
  7. 如何给FLV文件加字幕
  8. 实验吧web-中-忘记密码了
  9. 随想录(如何学习内核)
  10. DataLoader worker (pid 2287) is killed by signal: Killed. pytorch训练解决方法
  11. batch size自适应log(1)
  12. mysql清除内存不足_MySQL内存不足怎么办
  13. android 手机关机代码非root,Android手机获取root权限并实现关机重启功能的方法
  14. 人脸对齐中的相似性变换
  15. 基于GSM远程短信防盗报警系统
  16. JSP页面只显示源代码不显示网页内容
  17. html导出excel
  18. 6000字长文,带你用Python完成 “Excel合并(拆分)” 的各种操作!
  19. Win10 盘符更改后需要修改的
  20. Linux如何安装rpm文件

热门文章

  1. 获取授时时间_gps时间同步服务器在通信行业的解决方案
  2. virsh 关机_KVM virsh常用命令篇
  3. hanlp是开源的吗_自然语言处理之:搭建基于HanLP的开发环境(转)
  4. 新建notebook时发现在notebook里的sys.path与电脑上不一样
  5. c语言有分数的怎么编,用C语言编程平均分数
  6. 循环神经网络matlab程序设计,神经网络及深度学习(包含matlab代码).pdf
  7. 卡尔曼滤波与组合导航原理_卫星知识科普:一种基于卫星共视的卡尔曼滤波算法!...
  8. 动态规划之状态机模型
  9. Java反射之从对象获取值
  10. 信息系统项目管理师十大常见问题汇总