想象你是一位大型派对的策划师。三百位客人陆续进场,他们之间有着错综复杂的“认识”关系——有人是大学同学,有人是生意伙伴,有人刚在酒会上交换过名片。现在,你要把这份“谁认识谁”的关系图画成一张挂在墙上的巨型海报。如果你按报名顺序把所有人的名字摆上去,再把每一对认识的人之间拉一条线,得到的将不是一张“关系图”,而是一团连你自己都认不出来的毛线球。 “好看”的可视化,本质上是一场“关系派对”的座位管理学:把熟络的朋友安排在同一桌(聚类着色),让朋友之间坐得近、陌生人隔得远(力导向布局),人多的时候只展示每桌桌长(节点聚合),帮某位客人找到引荐对方的最短介绍链(路径高亮),甚至按“朋友的朋友”这种社交模板扫描全场(模式匹配)。而 Neo4j Bloom,就是主办方手里那个“派对管理终极工具箱”。 这篇文章会用一场派对从头到尾的筹备过程,带你看懂图数据库可视化的七个关键环节,并学会用 Neo4j Bloom 这类工具把复杂关系画出条理、画出美感。
图数据库可视化是知识图谱、社交网络与风控反欺诈领域的“关系翻译官”——掌握它,你就能把藏在数据里的连接故事讲给任何人听,是 AI 应用工程师搭建可读可查知识系统的必备技能。
目录
可视化演示
动态演示图数据库怎么可视化才好看的核心机制,通过源头/管道/节点/终端四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
图数据库怎么可视化才好看:图数据库 图数据库可视化与Bloom定位 力导向布局与层级布局算法 核心机制可视化 图数据库怎么可视化才好看整体架构与关键组件
图布局算法:先给客人安排座位
派对海报的第一张草图,往往是个灾难:名字按签到顺序随便撒在纸上,结果认识的人相距十万八千里,连线交错成蜘蛛网。作为主人,你急需一套“座位规则”——让每对朋友之间有一根“友谊弹簧”把他们往一处拉,同时给每个客人一个“私人距离气泡”,逼开那些靠得太近的陌生人。
这套规则在计算机里有个正式名字:力导向布局(Force-Directed Layout)。它把每个节点当成带电小球(互相排斥),把每条边当成弹簧(互相吸引)。算法不断迭代,直到整个系统的“张力”降到最低。
Fruchterman-Reingold 是这个流派最经典的算法。它先按画布大小算出一个“理想距离”:把场地面积均分给每位客人再开方,得到邻居间最舒适的间距。随后反复施加两种力:
除了力导向,还有层级布局(树状组织从根往下层层排开)和环形布局(适合环状依赖)。选好布局后还要评估可读性:边交叉数、节点重叠率、同社区节点的平均距离——交叉越少,图越好读。等迭代收敛,你会惊喜地发现:好友自然坐在了一起,派对的社交结构一目了然。
- 画布面积海报(画布)的宽 W 乘以高 H,即“派对场地”的像素总面积。
- 客人总数N 是节点总数,也就是到场的客人人数。
- 理想距离 k把场地均分给每位客人后开方,得到正方形边长——邻居间最舒适的坐席间距。
- 排斥力距离 d 越小,推开的力越大,防止客人挤成一团;负号表示方向向外。
- 吸引力距离 d 越大,弹簧拉回越用力,保证朋友不会走散。
节点聚类:给朋友圈插上小旗
座位排好了,可墙上的海报依旧是几十个大小相同的黑点。作为主人,你需要一眼识别出“这是大学同学那桌,那是投资人那桌”。最自然的做法是:把互相熟悉的小圈子圈出来,给每桌插上不同颜色的小旗。
这在图分析中叫社区发现(Community Detection)。“社区”不是按标签硬分,而是从连接模式里“长”出来的——一群人内部联系紧密、对外联系稀疏,就是一个天然社区。就好比派对里有些人虽然来自不同公司,却因为共同爱好聊成了一桌。
如何衡量一个分组的质量?社区发现领域的“金牌标准”是模块度 Q。它比较“真实连接”与“随机重连的期望连接”之间的差距:
算出社区后,给每个社区分配一种颜色、一个分组标签,海报立刻从“一锅粥”变成“一幅画”。聚类着色还能叠加属性分组:比如把“人”画成圆、“公司”画成方,再用色块背景托底社区,层次感立刻出来。
- 边总数m 是图中边的总数,即全场“认识关系”的总条数。
- 真实连接A_ij 是邻接矩阵元素——i 和 j 是否真的认识(认识记 1,否则记 0)。
- 两人度数k_i 和 k_j 分别是节点 i、j 的度,即两人各自的“熟人数量”。
- 随机期望保持每个人的熟人数量不变、把所有关系打乱重连,i 与 j 之间“恰好认识”的期望次数。
- 超额联系真实值减去随机期望,大于 0 说明这对关系“超出偶然”,是圈子里的真友谊。
- 同组才计入δ(c_i, c_j) 保证只有被分到同一社区的节点对才参与累加,最后除以 2m 归一化。
交互式探索:主人的三件套
海报挂上墙后,派对的互动才刚刚开始。主人不能只站在一个位置看,他需要全场走动的能力:站上二楼看全局格局,走到某桌前看每个人的表情,觉得某个角落喧闹无序,还能暂时遮住它不让自己分心。
图探索工具也提供同样的三件套:平移(拖动画布漫步)、缩放(拉近看细节、拉远看全貌)、筛选(过滤掉次要节点)。值得强调的是“语义缩放”——放大倍数不够时只画空心点,放大到一定程度才显示名字,再放大显示属性详情,信息密度始终适配人眼。
筛选中最常用的一招是“按度数过滤”。度数指一个节点连接了多少条边,是节点的“社交活跃度”:派对里有些人认识半个会场,有些人只认识同桌两三位。设定一个阈值 θ,只保留熟人数量不少于 θ 的节点:
交互式探索的精髓在于“永远给用户正确的信息量”。主人不会在谈生意时盯着三百人的名字发呆,他只会聚焦当前关心的那群人——探索工具让这种聚焦成本降到最低。
- 全集 V海报上所有客人的集合。
- 节点度数deg(v) 是 v 直接认识的客人数量。
- 阈值 θ主人设定的“活跃门槛”,例如“只保留熟人超过 10 个的社交达人”。
- 保留子集通过筛选保留下来的节点集合——边缘人物淡出,核心关系网浮现。
大规模渲染:派对人数爆棚时
预约的派对忽然变成了全城盛会:五万人涌入会场。这时海报策略必须彻底改变——总不可能把五万个名字全画上去。作为主人,你会分层招待:大厅里只放“桌长”立牌,真正要找人时再去翻厚厚的花名册。
图可视化的渲染原理与之完全一致。第一层是“画布加速”:用 Canvas/WebGL 交给 GPU 批量绘制,而不是用 DOM 元素逐个摆放——五万张 DOM 卡片会直接拖垮浏览器。第二层是“节点聚合”:当几十个节点在屏幕上重叠成一团时,合并为一个“超级节点”,旁边标个数字徽标,点开才展开。
为什么要聚合?因为人眼能同时辨认的节点数量有一个物理上限——每个节点在屏幕上至少需要一小块“专属圆形区域”才不至于和邻居糊在一起。于是可渲染节点数有了如下公式:
解决了渲染容量,还需“分页加载”:优先渲染当前视野内的节点,拖到边缘时动态加载新区域。就像主人站在大厅,永远只细看眼前那几桌,但全场概况始终在他脑中。
- 屏幕面积W × H 是画布的像素面积,即“屏幕场地”的总面积。
- 单点占地A_node 是单个节点可辨认所需的最小屏幕面积——一个直径约 20 至 50 像素的圆。
- 可容纳数两者相除,得出这块屏幕最多能放下多少个互不遮挡的可辨认节点。
- 取最小值当真实节点数 N 超过容量时,必须聚合或抽稀,而不是全量绘制。
路径高亮:最短的介绍链
派对进行到一半,一位客人拉住你:“我想认识张总,但没人引荐很唐突。”作为八面玲珑的主人,你马上在脑中搜索:谁认识张总?谁又认识那个人?一条“我认识 A、A 认识 B、B 认识张总”的介绍链在脑中成型。
在图论里,这就是“最短路径”问题。BFS(广度优先搜索)是无权图上求最短路径的经典方法:从起点出发,先标记所有一步能到的邻居,再标记两步能到的,逐层扩散——就像主人先喊一声“谁认识张总”,再从举手的人那里继续追问下一层。
BFS 的核心是一条简洁的“距离松弛”方程:
找到路径后,可视化要做的是“突出”:把路径上的节点和边涂成醒目标色(关系突出),其余关系淡化成灰色背景,再加一段从起点流向终点的脉冲光效(路径动效)。观众的视线会像主人的介绍辞一样,沿着这条链顺畅地流动。
- 起点距离起点 s(那位请求引荐的客人)到自己的距离是 0。
- 邻居集合N(v) 是 v 所有邻居的集合,即 v 直接认识的客人。
- 多走一步如果 u 已知距离 d(u),那么从 u 再走一步就能到 v,距离是 d(u)+1。
- 取最小值v 从所有邻居里挑一个“最近的介绍人”,选择最小的 d(u)+1。
- 逐层扩散从起点一圈一圈向外标记,第一次摸到某节点时记录的就是最短距离——因为 BFS 按层扩展,先到即最短。
模式匹配:按模板扫描全场
派对来了很多“中间人”,你忽然想揪出所有“传话链”:甲认识乙、乙认识丙、丙认识丁。与其一张张看海报,不如定义好模板,让系统自动在全场扫描——就像主人对保安说:“把这种三人串联的组合都给我标记出来。”
图数据库里这就是“模式匹配”。Neo4j 用 Cypher 语言描述模板,例如找“通过两跳认识目标”的三元链:
数学上,模式匹配是“子图同构”问题:把模板里的角色一一映射到真实节点上,且要求模板中的每条边在真实图里都存在。用公式表达:
模式匹配的威力还在于“模板可复用”。查过一次“两跳链”,可以把模板收藏起来(结果收藏)。下次想找“谁通过中间人认识张总”,一键重放,匹配结果直接在图上高亮成一组组小卡片。
- 角色集合V_P 是模板图的节点集合,即模式里的“角色位”(甲、乙、丙)。
- 角色映射f 是从角色位到真实节点的映射,相当于给每个角色安排一位“演员”。
- 每条边∀(u,v) ∈ E_P 遍历模板中的每一条“认识”关系。
- 边存在性角色 u、v 映射到的两位真实客人也必须确实存在对应关系——“对号入座”成功才算匹配。
- 匹配成功只要存在任何一个合法映射,就说模板在图中被命中。
MATCH (a:Person)-[:KNOWS]->(b:Person)-[:KNOWS]->(c:Person)RETURN a, b, c
Bloom 实战:主人的终极工具箱
前面所有功夫,最终都要落在趁手的工具上。Neo4j Bloom 就是为“不写代码也能玩转关系图”而生的“派对管理工具箱”。它不需要你背 Cypher 语法,像用搜索引擎一样输入自然语言就能探索图数据库。
Bloom 有四个核心功能。透视视图(Perspective):为当前探索限定一个“白名单”——只显示指定的标签和关系类型,如同主人决定今晚只开放“商务区”和“亲友区”。搜索检索:输入“哪些产品由张总负责”,系统把查询翻译成图谱搜索。卡片详情:点开任意节点,像翻看宾客档案一样浏览属性与关联。场景保存:把当前视角、样式、搜索命令整体存为“场景”,下次一键恢复。
搜索结果的排序暗藏玄机。Bloom 不只按文本匹配排结果,还会考虑节点在图里的“分量”——就像主人查“刘总”时,会把圈里最有话语权的那位排在最前面:
用 Bloom 探索关系图,就像请了一位随身管家:你说“想见刘总”,管家先找出所有名字含“刘总”的客人,再优先把圈里最有分量、最容易见到的那位带到你面前。你不需要懂后台的辛劳,只需享受探索的自由。
- 文本匹配Match(v, q) 是节点属性与搜索词 q 的文本匹配程度——名字与标签有多像。
- 网络重要性Importance(v) 是节点在图中的网络分量,比如连接数或 PageRank。
- 权重系数α 与 β 控制“字面对上”与“圈内分量”谁更重要。
- 加权总分两者加权求和——同名的人里,社交圈更大的那位排到最前面。
AI 应用场景
社交网络圈层分析与意见领袖挖掘
阶段:数据探索与图特征工程
解决的问题:传统方式用表格统计好友数、转发数,无法呈现“谁和谁是一个圈子”这类结构信息。AI 方式将关注与互动关系构造成图,用社区发现加聚类着色、力导向布局把圈层和关键人物“画”出来。
具体案例:某内容平台运营团队把 50 万用户的互关关系导入 Neo4j,用 Bloom 的透视视图限定“用户—关注”两类标签,按模块度聚类着色后,一眼看出 6 个兴趣圈层,进而为每个圈层定制内容策略。
企业知识图谱的一站式问答探索
阶段:知识表示与检索增强(RAG)
解决的问题:传统文档检索无法回答“张总旗下的公司持有哪些专利”这类多跳关系问题。AI 方式把实体与关系存入图数据库,用路径高亮加模式匹配把多跳答案渲染成一条清晰的关系链。
具体案例:某咨询公司用 Neo4j 构建了含 10 万实体、30 万关系的企业知识图谱。顾问在 Bloom 中搜索“某投资机构最近投资的医疗公司”,系统通过模板匹配找到目标子图,并用最短路径高亮展示“机构—投资—公司—创始人”的关系链,原本半小时的调研缩短到 2 分钟。
反欺诈团伙识别中的可视化研判
阶段:模型可解释性与人工研判
解决的问题:风控模型只给出“风险分”,却说不清风险来源;人工排查转账记录如同大海捞针。AI 方式把交易网络可视化,用社区发现揪出“黑产团伙”,用环状模式匹配识别资金回环。
具体案例:某银行风控团队将 7 天内的异常转账构造成图,在 Bloom 中运行“资金回流环”模式模板,命中 12 个可疑环形结构,路径高亮后人工核对确认 3 个欺诈团伙,成功拦截约百万元。
Python 演示
import mathimport random def force_directed_layout(edges, n_nodes, steps=200, W=100.0, H=100.0, C=1.0): # 初始位置: 随机散布(像刚进场的客人) pos = [[random.random() * W, random.random() * H] for _ in range(n_nodes)] k = C * math.sqrt(W * H / n_nodes) # 理想距离: 场地均分给每个客人 for step in range(steps): disp = [[0.0, 0.0] for _ in range(n_nodes)] # 排斥力: 所有节点两两相斥, 距离越近推得越猛 for i in range(n_nodes): for j in range(i + 1, n_nodes): dx = pos[i][0] - pos[j][0] dy = pos[i][1] - pos[j][1] dist = max(math.hypot(dx, dy), 0.01) force = k * k / dist disp[i][0] += dx / dist * force disp[i][1] += dy / dist * force disp[j][0] -= dx / dist * force disp[j][1] -= dy / dist * force # 吸引力: 每条边都是一根友谊弹簧 for (a, b) in edges: dx = pos[b][0] - pos[a][0] dy = pos[b][1] - pos[a][1] dist = max(math.hypot(dx, dy), 0.01) force = dist * dist / k disp[a][0] += dx / dist * force disp[a][1] += dy / dist * force disp[b][0] -= dx / dist * force disp[b][1] -= dy / dist * force # 按位移更新位置, 并限制在画布内 for i in range(n_nodes): d = max(math.hypot(disp[i][0], disp[i][1]), 0.01) step_size = min(0.05 * d, 5.0) pos[i][0] += disp[i][0] / d * step_size pos[i][1] += disp[i][1] / d * step_size pos[i][0] = max(5, min(W - 5, pos[i][0])) pos[i][1] = max(5, min(H - 5, pos[i][1])) return pos def main(): random.seed(42) n_nodes = 30 # 构造两个朋友圈: 0-14 号为一组, 15-29 号为另一组 edges = [] for i in range(n_nodes): for j in range(i + 1, n_nodes): same_community = (i < 15) == (j < 15) p = 0.30 if same_community else 0.02 if random.random() < p: edges.append((i, j)) pos = force_directed_layout(edges, n_nodes, steps=150, W=100, H=100) print("节点数:", n_nodes, " 边数:", len(edges)) print("前 5 个节点坐标 (x, y):") for i in range(5): print(" 节点", i, ":", round(pos[i][0], 2), ",", round(pos[i][1], 2)) # 检验两个圈子是否在空间上分开 left = sum(pos[i][0] for i in range(15)) / 15 right = sum(pos[i][0] for i in range(15, 30)) / 15 print("圈子 A 平均 x:", round(left, 2), " 圈子 B 平均 x:", round(right, 2)) if __name__ == "__main__": main()
k = C * math.sqrt(W * H / n_nodes)计算理想距离:场地面积均分给每个客人后开方。force = k * k / dist排斥力:距离越近推得越猛。force = dist * dist / k吸引力:距离越远弹簧拉回越用力。disp[i][0] += dx / dist * force把力分解到 x 方向,累积每位客人的位移。step_size = min(0.05 * d, 5.0)限制单步最大位移,防止布局震荡发散。same_community = (i < 15) == (j < 15)判断两个节点是否属于同一朋友圈。p = 0.30 if same_community else 0.02同圈子内连边概率远高于跨圈子。if random.random() < p:按概率生成“认识关系”边。pos = force_directed_layout(edges, n_nodes, steps=150, W=100, H=100)执行力导向布局主流程。left = sum(pos[i][0] for i in range(15)) / 15计算圈子 A 的平均 x 坐标,用于验证空间分离。
常见误区
误区初学者觉得一张图越完整越专业,于是把数据库中几十万条关系和节点一股脑塞进画布,认为“信息全”就等于“可视化好”。
正解全部画出来只会得到“毛线球”。人眼带宽和屏幕像素都有限,这正是“大规模渲染”中聚合与抽稀存在的原因。专业做法是分层展示:先给全局概览(聚合/采样),再让用户逐层钻取,永远保持“一屏讲清一个故事”。就像办派对,主人不会把五万人的姓名全部念一遍,而是报“桌长名单”。
为何常见:“全量等于完整”的错觉来自混淆“数据完整”与“信息可读”,工具默认全量渲染也让新人误以为这是正确用法。
误区很多人觉得布局算法只是“摆放得好看不好看”的装饰,于是所有图都套用力导向布局,结果遇到树状组织架构时画面乱作一团。
正解布局决定读者第一眼能否读懂图。力导向适合无明显层级的关系网;层级布局适合父子/上下级结构;环形布局适合环状依赖(如编译依赖)。选错布局,再准确的图也读不出结构。就像座位安排:婚宴用圆桌,会议用长桌,派对用散座——场景决定摆放。
为何常见:力导向算法“什么图都能画”的通用性掩盖了它“什么图都不是最优”的局限,且多数教程只演示这一种。
误区新手喜欢为每种标签配一种颜色,画布上五彩缤纷,觉得这样“高级”、信息量大。
正解颜色在可视化里是“语义编码”,必须让人一眼区分“同一颜色等于同一类”。人眼能稳定区分的颜色约 7±2 种,超过这个数就变成“找色差”游戏。正确做法:同社区同色系、类间差异明显,用形状区分大类(圆=人、方=公司、三角=产品),颜色只表达社区或风险级别。就像派对桌旗,红黄蓝三色足够区分三桌,弄出三十种颜色反而人人找不到座位。
为何常见:日常界面设计的多彩审美被错误迁移到数据可视化上,忽略了可视化的首要目标是“可读”而非“好看”。
误区打开百万节点图卡成幻灯片,第一反应是“该换高配电脑、买独显”。
正解卡顿的根因往往是渲染架构——如果每个节点都用 DOM 元素表示,几万个 DOM 足以拖垮任何浏览器;换成 WebGL/Canvas 批量绘制,普通笔记本也能流畅渲染数十万节点。其次还有数据策略:聚合、分页、降采样,这些软件方案比硬件升级更关键。就像餐厅接待五万人,问题不在桌子数量,而在你是“一桌一桌摆”还是“流水席加分批进场”。
为何常见:“性能等于硬件”是通用直觉,而前端渲染的圈内知识(Canvas vs DOM、GPU 批量绘制)超出新手视野,导致归因错误。
面试题
Q1. 力导向布局的核心思想是什么?
- A.朋友近、陌生人互相排斥← 正确答案
- B.按节点名称字母排序
- C.把节点随机撒在画布
- D.依照节点编号排位置
答案:A。力导向把节点当带电小球、边当弹簧:弹簧拉近有关系的节点,斥力推开陌生节点,平衡后形成清晰结构。B 是字典序排列,C 是随机布局,D 是按编号排列,都不涉及“关系驱动的平衡”,故排除。
Q2. 社区发现的分组目标是什么?
- A.组内边密集且组间边稀疏← 正确答案
- B.每组节点数量完全一样
- C.所有节点度数尽可能大
- D.分组数量越少越好
答案:A。社区发现追求“组内联系紧密、组间联系稀疏”,用模块度 Q 量化这种结构。B(数量均等)、C(度数尽量大)、D(分组数尽量少)都不是从连接结构出发的判据。
Q3. 为什么 BFS 在无权图中“先到达即最短”?
- A.逐层向外扩展先到者最近← 正确答案
- B.因为图中不存在任何环路
- C.因为每个节点都有编号
- D.随机选邻居碰巧能找到
答案:A。BFS 按“距离层”逐层扩展:起点是第 0 层,一步可达是第 1 层……第一次访问到某节点时必然经过最少层数,所以先到即最短。B 错在“无环”不是 BFS 的前提;C、D 与最短路径原理无关。
Q4. Neo4j Bloom 的“透视视图”(Perspective)的作用是?
- A.限定标签与关系类型的白名单视图← 正确答案
- B.把图放大到最大倍数的视图
- C.自动生成 3D 立体效果的视图
- D.只显示最小生成树的视图
答案:A。透视视图把探索视野限定在一组白名单标签和关系类型上,相当于主人宣布“今晚只看商务区”。B 是缩放功能,C 是 3D 渲染,D 是最小生成树,均不是标签级过滤。
Q5. 下列哪些方法能显著提升大规模图的渲染性能?
- A.用 WebGL 交给 GPU 批量绘制← 正确答案
- B.把重叠节点聚合为超级节点← 正确答案
- C.用 innerHTML 逐节点创建
- D.按视野范围分页动态加载← 正确答案
答案:ABD。WebGL 批量绘制、重叠节点聚合、按视野分页加载,都是从渲染架构或数据策略上为浏览器减负。C 用 innerHTML 逐个创建 DOM 节点恰恰是大图卡顿的根源,应排除。
Q6. 子图同构匹配成功的条件是什么?
- A.模板节点都能映射且边保持不变← 正确答案
- B.模板图与数据图节点数相等
- C.数据图包含模板图所有颜色
- D.模板边数比数据图边数更多
答案:A。模板中每个角色都能在真实图中找到“对号入座”的节点,且模板的每条边在真实图中也存在,即映射 f 保持边结构。B 只要求数量相等不够,C 颜色无关,D 边数比较不构成子图匹配。
Q7. 屏幕上能同时清晰辨认的节点数为何有限?
- A.每个节点都需要足够的像素面积← 正确答案
- B.浏览器对节点总数有硬性限制
- C.布局算法只支持少量节点
- D.色板中的可用颜色数量有限
答案:A。每个节点至少要占一个可辨认的圆形像素区域,否则邻居就会糊成一团。屏幕面积除以单节点面积就是可读上限,超出必须聚合或抽稀。B 浏览器并无硬性节点数限制,C 布局算法支持大图,D 颜色数量与可读性无关。
Q8. 路径高亮中把非路径边“弱化”的目的是什么?
- A.引导视线沿着关键路径流动← 正确答案
- B.降低浏览器渲染的资源开销
- C.让布局算法收敛得更快
- D.让所有节点的颜色保持一致
答案:A。弱化背景边是为了让观众的注意力沿着“介绍链”一眼到底,是叙事引导而非性能手段。A 才是目的;B 是可能的副产物但不是主要动机,C 与布局收敛无关,D 与配色设计无关。
Q9. 关于力导向布局,下列说法正确的有哪些?
- A.每轮需计算所有节点对之间的斥力← 正确答案
- B.理想距离与画布面积成正比
- C.迭代会收敛到能量相对低的状态← 正确答案
- D.任意数据都适合同一种布局算法
答案:AC。力导向每轮确实要计算所有节点对之间的斥力(O(n²) 复杂度);迭代趋向弹簧与斥力平衡的低能量状态。B 错误:理想距离与画布面积的平方根成正比,而非面积本身;D 错误:树状数据更适合层级布局,不能一概而论。
Q10. 模块度公式中 k_i × k_j ÷ (2m) 这一项表示什么?
- A.随机重连时两节点间的期望边数← 正确答案
- B.节点i与j实际连接数量
- C.图中所有最短路径的长度
- D.节点i和j各自度的平均值
答案:A。它是在“保持每人熟人数量不变、随机重连边”的零模型下,i 与 j 之间存在边的期望次数。真实边(1)减去期望值,才是超乎偶然的“真友谊”。B 是实际边数,C、D 都与期望边数无关。