社区发现算法 - Fast Unfolding(Louvian)算法初探
1. 社团划分
0x1:社区是什么
在社交网络中,用户相当于每一个点,用户之间通过互相的关注关系构成了整个网络的结构。
在这样的网络中,有的用户之间的连接较为紧密,有的用户之间的连接关系较为稀疏。其中连接较为紧密的部分可以被看成一个社区,其内部的节点之间有较为紧密的连接,而在两个社区间则相对连接较为稀疏。
整个整体的结构被称为社团结构。如下图,红色的黑色的点集呈现出社区的结构,
用红色的点和黑色的点对其进行标注,整个网络被划分成了两个部分,其中,这两个部分的内部连接较为紧密,而这两个社区之间的连接则较为稀疏。
如何去划分上述的社区便称为社区划分的问题。
0x2:社区划分的出发点和意图
直观地说,community detection的一般目标是要探测网络中的“块”cluster或是“社团”community。
这么做的目的和效果有许多,比如说机房里机器的连接方式,这里形成了网络结构,那么,哪些机器可以视作一个“块”?进一步地,什么样的连接方式才有比较高的稳定性呢?如果我们想要让这组服务瘫痪,选择什么样的目标呢?
我们再看一个例子,word association network。即词的联想/搭配构成的网络:
我们用不同的颜色对community进行标记,可以看到这种detection得到的结果很有意思。
这个网络从词bright开始进行演化,到后面分别形成了4个组:Colors, Light, Astronomy & Intelligence。
可以说以上这4个词可以较好地概括其所在community的特点(有点聚类的感觉);另外,community中心的词,比如color, Sun, Smart也有很好的代表性(自动提取摘要)。
同时我们注意到,那些处在交叠位置的词呢,比如Bright、light等词,他们是同义项比较多的词。这个图也揭示出了这一层含义。
0x3:节点间存在连接的抽象本质 - 逻辑拓朴结构
社区的节点间是网络拓朴结构,即节点间是存在拓朴连接结构的,我们不能将其和欧式空间或者P空间中的点向量集合空间混为一谈。
以欧式空间为例,不同的节点向量存在于不同的空间位置中,向量夹角近的点向量彼此距离近,而向量夹角远的向量彼此距离远。但是即使是欧式距离很近的向量点,也不一定就代表这它们之间存在拓朴连接关系,只能说在一定的度量下(例如欧式距离度量),这两个节点很相近。
但是在社区结构中,节点之间没有什么空间位置的概念。相对的,节点间存在的是一种逻辑拓朴结构,即存在一种共有关系。
存在共有关系的节点在逻辑上会聚集为一个社区,而社区之前不存在或者存在很弱的共有关系,则呈现分离的逻辑拓朴结构。
读者朋友一定要注意不要用空间结构的概念来试图理解社区结构,不然会陷入理解的困境,社区中的节点只是因为逻辑上的共有关系而聚集在一起而已,彼此之间的位置也没有实际意义,而社区族群之间的分离也是表达一种逻辑上的弱共有关系。
举一些实际的例子:
. 节点代表消费者:节点间的连接代表了它们共同购买了一批书籍,weight代表共同购买的书籍数;
. 节点代表DNS域名:节点间的连接代表了它们拥有一批共同的src client ip(客户端),weight代表了共同的src_ip数量;
0x4:什么时候可以使用社区发现算法
下面这句话很明确地说明了在什么业务场景下可以使用社区发现算法:
(Newman and Gievan 2004) A community is a subgraph containing nodes which are more densely linked to each other than to the rest of the graph or equivalently, a graph has a community structure if the number of links into any subgraph is higher than the number of links between those subgraphs.
即我们需要先确定要解决的业务场景中,存在明显的聚集规律,节点(可以是抽象的)之间形成一定的族群结构,而不是呈现无规律的随机分散。同时另一方面,这种聚集的结构是“有意义的”,这里所谓的有意义是指这种聚集本身可以翻译为一定的上层业务场景的表现。
但是很多时候,我们业务场景中的数据集之间的共有关系并不是表现的很明显,即节点之间互相都或多或少存在一些共有关系,这样直接进行社区发现效果肯定是不好的。
所以一个很重要点是,我们在进行社区发现之前,一定要进行数据降噪。
理想情况下,降噪后得到的数据集已经是社区完全内聚,社区间完全零连接,这样pylouvain只要一轮运行就直接得到结果。当然实际场景中不可能有这么好的情况,数据源质量,专家经验的丰富程度等等都会影响降噪的效果,一般情况下,降噪只要能cutoff 90%以上的噪音,pylouvain就基本能通过几轮的迭代完成整体的社区发现过程。
0x5:社区划分的思路概要
什么样的结构能成为团?一种很直观的想法是,同一团内的节点连接更紧密,即具有更大的density。
接下来的问题是,什么样的metrics可以用来描述这种density?Louvian 定义了一个数值上的概念(本质上就是一个目标函数),有了这个目标函数,就可以引出接下来要讨论的 method based on modularity optimization
要注意的,社区划分有很多不同的算法,本文讨论的 Fast Unfolding(Louvian)只是其中一种,而这种所谓的density密度评估方法也其实其中一种思想,不要固话地认为社区划分就只有这一种方法。
Relevant Link:
https://stackoverflow.com/questions/21814235/how-can-modularity-help-in-network-analysis
http://iopscience.iop.org/article/10.1088/1742-5468/2008/10/P10008/fulltext/
https://www.researchgate.net/publication/1913681_Fast_Unfolding_of_Communities_in_Large_Networks?enrichId=rgreq-d403e26a5cb211b7053c36946c71acb3-XXX&enrichSource=Y292ZXJQYWdlOzE5MTM2ODE7QVM6MTAxOTUyNjc5NTc5NjY3QDE0MDEzMTg4MjE3ODA%3D&el=1_x_3&_esc=publicationCoverPdf
https://www.jianshu.com/p/4ebe42dfa8ec
https://blog.csdn.net/u011089523/article/details/79090453
https://blog.csdn.net/google19890102/article/details/48660239
《Fast Unfolding of Communities in Large Networks》
2. LOUVAIN算法模型
0x1:Modularity的定义 - 描述社区内紧密程度的值Q
模块度是评估一个社区网络划分好坏的度量方法,它的物理含义是社区内节点的连边数与随机情况下的边数只差,它的取值范围是 [−1/2,1),其定义如下:
A为邻接矩阵,Aij代表了节点 i 和节点 j 之间 边的权重,网络不是带权图时,所有边的权重可以看做是 1;
是所有与节点 i 相连的 边的权重之和(度数),kj也是同样;
表示所有边的权重之和(边的数目),充当归一化的作用;
是节点 i 的社区, 函数表示若节点 i 和节点 j 在同一个社区内,则返回 1,否则返回 0;
模块度的公式定义可以作如下简化:
其中 Σin 表示社区 C 内的边的权重之和;Σtot 表示与社区 C 内的节点相连的所有边的权重之和。
上面的公式还可以进一步简化成:
这样模块度也可以理解是:
首先modularity是针对一个社区的所有节点进行了累加计算。
modularity Q的计算公式背后体现了这种思想:社区内部边的权重减去所有与社区节点相连的边的权重和,对无向图更好理解,即社区内部边的度数减去社区内节点的总度数。
可以直观去想象一下,如果一个社区节点完全是“封闭的(即所有节点都互相内部连接,但是不和社区外部其他节点有连接,则modularity公式的计算结果为1)”
基于模块度的社区发现算法,都是以最大化模块度Q为目标。可以看到,这种模型可以支持我们通过策略优化,去不断地构造出一个内部聚集,外部稀疏连接的社区结构
在一轮迭代后,若整个 Q 没有变化,则停止迭代,否则继续迭代,直至收敛。
0x2:模块度增量 delta Q
模块增益度是评价本次迭代效果好坏的数值化指标,这是一种启发式的优化过程。类似决策树中的熵增益启发式评价。
代表由节点 i 入射集群 C 的权重之和;代表入射集群 C 的总权重;ki 代表入射节点 i 的总权重;
在算法的first phase,判断一个节点加入到哪个社区,需要找到一个delta Q最大的节点 i,具体的算法我们后面会详细讨论,这里只需要记住 delta Q的作用类似决策树中的信息增益评估的作用,它帮助整个模型向着Modularity不断增大的方向去靠拢。
3. LOUVAIN算法策略
Louvain算法是基于模块度的社区发现算法,该算法在效率和效果上都表现较好,并且能够发现层次性的社区结构,其优化目标是:最大化整个社区网络的模块度。
即让整个社区网络呈现出一种模块聚集的结构。
0x1:算法思想的联想
1. 两台主机拥有类似的网络对外发包模式
2. 两台主机间拥有累计的event log序列
3. 两个攻击payload拥有类似的词频特征,可以认为是同一组漏洞利用方式
4. 在netword gateway上发现了类似的网络raw流量,也可以反过来用一直的label流量特征进行有监督的聚类
..
社区发现可能可以提供一种更高层的视角来看待整体的大盘情况,具体的应用场景还需要不断的摸索。
0x2:关于启发式/贪婪思想的社区发现的进一步思考
社区发现算法,或者说在社区发现的项目中,很容易遇到的一个问题就是:“社区过大,将过多的outerlier包括到了社区中”,换句话说,社区聚类的过程中没有能及时收敛。
我们来看下面这张图:
如果按照启发式/贪婪思想进行”one-step one node“的社区聚类,O9、O10、O11会被先加入到社区D中,因为在每次这样的迭代中,D社区内部的紧密度(不管基于node密度还是edge得modularity评估)都是不断提高,符合算法的check条件,因此,O9、O10、O11会被加入到社区D中。
随后,O1 ~ O8也会被逐个被加入到社区D中,加入的原因和O9、O10、O11被加入是一样的。
从局部上来看,这些步骤是合理的,但是如果从上帝视角的全局来看,这种做法导致outerlier被错误的聚类到的社区中,导致precision下降。
解决这种问题的一个办法我觉得可以从CNN/DNN的做法中得到灵感,即设置一个Delta增益的阈值,即在每轮的迭代中(社区扩增后紧密度提升的度量)如果不能超过这个阈值,则判定为收敛成功,立即停止算法迭代。
0x3:社区发现(聚类)的效果非常依赖于weight权重计算的策略和方法
louvain社区发现是将一个无向权重图,转化为多个节点集合,每个节点集合代表了一个社区。
社区发现的思想是非常直接简单的,因为聚类的效果非常依赖于weight权重计算的方法,我们选择的权重计算方法必须要能够“很好地”在值域空间中分离开来,否则会导致overcluster。
举个例子,假设我们有一组节点间权重,我们按列的形式写出来:
A - B:
A - C:
B - C:
D - E:(明显和2不一样)
D - F:
E - F:
F - G:
A - D:(社区间存在弱关系)
B - F:
上述社区中,我们很容易理解,应该分为两个社区:
A/B/C、D/E/F
因为这2个社区满足社区内内聚,社区间相异的社区拓朴结构特性。
反过来可以想象,如果weight权重的计算公式不够合理,社区间的关系(A-D、B-F)和各自的社区weight很接近,则pylouvain就无法很好的将两个社区区分开来了。因为在pylouvain看来,它们也属于社区的一部分。
4. LOUVAIN算法流程
0x1:算法形式化描述
1)初始化:
将图中的每个节点看成一个独立的社区,社区的数目与节点个数相同;
2)开始first phase迭代 - 社区间节点转移:
对每个节点i,依次尝试把节点 i 分配到其每个邻居节点所在的社区,计算分配前与分配后的模块度变化ΔQ,并记录ΔQ最大的那个邻居节点,如果maxΔQ>0,则把节点 i 分配ΔQ最大的那个邻居节点所在的社区,否则保持不变;
3)重复2)- 继续进行社区间节点转移评估:
直到所有节点的所属社区不再变化,即社区间的节点转移结束,可以理解为本轮迭代的 Local Maximization 已达到;
4)second phase - Rebuilding Graph:
因为在这轮的first phase中,社区 C 中新增了一个新的节点 i,而 i 所在的旧的社区少了一个节点,因此需要对整个图进行一个rebuild。
对图进行重构,将所有在同一个社区的节点重构成一个新社区,社区内节点之间的边的权重更新为新节点的环的权重,社区间的边权重更新为新节点间的边权重;
5)重复2)- 继续开始下一轮的first/second phase:
直到整个图的模块度不再发生变化。
0x2:算法时间复杂度
DeltaQ 分了两部分,前面部分表示把节点i加入到社区c后的模块度,后一部分是节点i作为一个独立社区和社区c的模块度
https://blog.csdn.net/xuanyuansen/article/details/68941507
https://www.cnblogs.com/fengfenggirl/p/louvain.html
http://www.cnblogs.com/allanspark/p/4197980.html
https://github.com/gephi/gephi/wiki
https://blog.csdn.net/qq547276542/article/details/70175157
5. A Python implementation of the Louvain method to find communities in large networks
#!/usr/bin/env python3
# -*- coding: utf- -*- '''
Implements the Louvain method.
Input: a weighted undirected graph
Ouput: a (partition, modularity) pair where modularity is maximum
'''
class PyLouvain: '''
Builds a graph from _path.
_path: a path to a file containing "node_from node_to" edges (one per line)
'''
@classmethod
def from_file(cls, path):
f = open(path, 'r')
lines = f.readlines()
f.close()
nodes = {}
edges = []
for line in lines:
n = line.split()
if not n:
break
nodes[n[]] =
nodes[n[]] =
w =
if len(n) == :
w = int(n[])
edges.append(((n[], n[]), w))
# rebuild graph with successive identifiers
nodes_, edges_ = in_order(nodes, edges)
print("%d nodes, %d edges" % (len(nodes_), len(edges_)))
return cls(nodes_, edges_) '''
Builds a graph from _path.
_path: a path to a file following the Graph Modeling Language specification
'''
@classmethod
def from_gml_file(cls, path):
f = open(path, 'r')
lines = f.readlines()
f.close()
nodes = {}
edges = []
current_edge = (-, -, )
in_edge =
for line in lines:
words = line.split()
if not words:
break
if words[] == 'id':
nodes[int(words[])] =
elif words[] == 'source':
in_edge =
current_edge = (int(words[]), current_edge[], current_edge[])
elif words[] == 'target' and in_edge:
current_edge = (current_edge[], int(words[]), current_edge[])
elif words[] == 'value' and in_edge:
current_edge = (current_edge[], current_edge[], int(words[]))
elif words[] == ']' and in_edge:
edges.append(((current_edge[], current_edge[]), ))
current_edge = (-, -, )
in_edge =
nodes, edges = in_order(nodes, edges)
print("%d nodes, %d edges" % (len(nodes), len(edges)))
return cls(nodes, edges) '''
Initializes the method.
_nodes: a list of ints
_edges: a list of ((int, int), weight) pairs
'''
def __init__(self, nodes, edges):
self.nodes = nodes
self.edges = edges
# precompute m (sum of the weights of all links in network)
# k_i (sum of the weights of the links incident to node i)
self.m =
self.k_i = [ for n in nodes]
self.edges_of_node = {}
self.w = [ for n in nodes] for e in edges:
self.m += e[]
self.k_i[e[][]] += e[]
self.k_i[e[][]] += e[] # there's no self-loop initially
# save edges by node
if e[][] not in self.edges_of_node:
self.edges_of_node[e[][]] = [e]
else:
self.edges_of_node[e[][]].append(e)
if e[][] not in self.edges_of_node:
self.edges_of_node[e[][]] = [e]
elif e[][] != e[][]:
self.edges_of_node[e[][]].append(e)
# access community of a node in O() time
self.communities = [n for n in nodes]
self.actual_partition = [] '''
Applies the Louvain method.
'''
def apply_method(self):
network = (self.nodes, self.edges)
best_partition = [[node] for node in network[]]
best_q = -
i =
while :
i +=
partition = self.first_phase(network)
q = self.compute_modularity(partition)
partition = [c for c in partition if c]
# clustering initial nodes with partition
if self.actual_partition:
actual = []
for p in partition:
part = []
for n in p:
part.extend(self.actual_partition[n])
actual.append(part)
self.actual_partition = actual
else:
self.actual_partition = partition
if q == best_q: # 如果本轮迭代modularity没有改变,则认为收敛,停止
break
network = self.second_phase(network, partition)
best_partition = partition
best_q = q
return (self.actual_partition, best_q) '''
Computes the modularity of the current network.
_partition: a list of lists of nodes
'''
def compute_modularity(self, partition):
q =
m2 = self.m *
for i in range(len(partition)):
q += self.s_in[i] / m2 - (self.s_tot[i] / m2) **
return q '''
Computes the modularity gain of having node in community _c.
_node: an int
_c: an int
_k_i_in: the sum of the weights of the links from _node to nodes in _c
'''
def compute_modularity_gain(self, node, c, k_i_in):
return * k_i_in - self.s_tot[c] * self.k_i[node] / self.m '''
Performs the first phase of the method.
_network: a (nodes, edges) pair
'''
def first_phase(self, network):
# make initial partition
best_partition = self.make_initial_partition(network)
while :
improvement =
for node in network[]:
node_community = self.communities[node]
# default best community is its own
best_community = node_community
best_gain =
# remove _node from its community
best_partition[node_community].remove(node)
best_shared_links =
for e in self.edges_of_node[node]:
if e[][] == e[][]:
continue
if e[][] == node and self.communities[e[][]] == node_community or e[][] == node and self.communities[e[][]] == node_community:
best_shared_links += e[]
self.s_in[node_community] -= * (best_shared_links + self.w[node])
self.s_tot[node_community] -= self.k_i[node]
self.communities[node] = -
communities = {} # only consider neighbors of different communities
for neighbor in self.get_neighbors(node):
community = self.communities[neighbor]
if community in communities:
continue
communities[community] =
shared_links =
for e in self.edges_of_node[node]:
if e[][] == e[][]:
continue
if e[][] == node and self.communities[e[][]] == community or e[][] == node and self.communities[e[][]] == community:
shared_links += e[]
# compute modularity gain obtained by moving _node to the community of _neighbor
gain = self.compute_modularity_gain(node, community, shared_links)
if gain > best_gain:
best_community = community
best_gain = gain
best_shared_links = shared_links
# insert _node into the community maximizing the modularity gain
best_partition[best_community].append(node)
self.communities[node] = best_community
self.s_in[best_community] += * (best_shared_links + self.w[node])
self.s_tot[best_community] += self.k_i[node]
if node_community != best_community:
improvement =
if not improvement:
break
return best_partition '''
Yields the nodes adjacent to _node.
_node: an int
'''
def get_neighbors(self, node):
for e in self.edges_of_node[node]:
if e[][] == e[][]: # a node is not neighbor with itself
continue
if e[][] == node:
yield e[][]
if e[][] == node:
yield e[][] '''
Builds the initial partition from _network.
_network: a (nodes, edges) pair
'''
def make_initial_partition(self, network):
partition = [[node] for node in network[]]
self.s_in = [ for node in network[]]
self.s_tot = [self.k_i[node] for node in network[]]
for e in network[]:
if e[][] == e[][]: # only self-loops
self.s_in[e[][]] += e[]
self.s_in[e[][]] += e[]
return partition '''
Performs the second phase of the method.
_network: a (nodes, edges) pair
_partition: a list of lists of nodes
'''
def second_phase(self, network, partition):
nodes_ = [i for i in range(len(partition))]
# relabelling communities
communities_ = []
d = {}
i =
for community in self.communities:
if community in d:
communities_.append(d[community])
else:
d[community] = i
communities_.append(i)
i +=
self.communities = communities_
# building relabelled edges
edges_ = {}
for e in network[]:
ci = self.communities[e[][]]
cj = self.communities[e[][]]
try:
edges_[(ci, cj)] += e[]
except KeyError:
edges_[(ci, cj)] = e[]
edges_ = [(k, v) for k, v in edges_.items()]
# recomputing k_i vector and storing edges by node
self.k_i = [ for n in nodes_]
self.edges_of_node = {}
self.w = [ for n in nodes_]
for e in edges_:
self.k_i[e[][]] += e[]
self.k_i[e[][]] += e[]
if e[][] == e[][]:
self.w[e[][]] += e[]
if e[][] not in self.edges_of_node:
self.edges_of_node[e[][]] = [e]
else:
self.edges_of_node[e[][]].append(e)
if e[][] not in self.edges_of_node:
self.edges_of_node[e[][]] = [e]
elif e[][] != e[][]:
self.edges_of_node[e[][]].append(e)
# resetting communities
self.communities = [n for n in nodes_]
return (nodes_, edges_) '''
Rebuilds a graph with successive nodes' ids.
_nodes: a dict of int
_edges: a list of ((int, int), weight) pairs
'''
def in_order(nodes, edges):
# rebuild graph with successive identifiers
nodes = list(nodes.keys())
nodes.sort()
i =
nodes_ = []
d = {}
for n in nodes:
nodes_.append(i)
d[n] = i
i +=
edges_ = []
for e in edges:
edges_.append(((d[e[][]], d[e[][]]), e[]))
return (nodes_, edges_)
社区发现的最后一轮结果为:
以polbooks.gml为例,16、17、18、19被归类为同一个社区:
node
[
id
label "Betrayal"
value "c"
]
node
[
id
label "Shut Up and Sing"
value "c"
]
node
[
id
label "Meant To Be"
value "n"
]
node
[
id
label "The Right Man"
value "c"
]
从读者的共同购买情况作为权重评估,社区发现的结果暗示了这几本书可能属于同一类的书籍
利用louvain进行社区发现的核心在于,我们需要对我们的业务场景进行抽象,提取出node1_src/node_dst的节点概念,同时要通过专家领域经验,进行节点间weight的计算,得到了weight后,就可以通过louvain这种迭代算法进行社区拓朴的发现了。
Relevant Link:
http://www.cnblogs.com/allanspark/p/4197980.html
https://arxiv.org/pdf/0803.0476.pdf
https://github.com/LittleHann/pylouvain
http://www.cnblogs.com/allanspark/p/4197980.html
https://www.jianshu.com/p/e543dc63454f
6. 其他社区发现算法
Relevant Link:
http://blog.sina.com.cn/s/blog_63891e610101722t.html
https://www.zhihu.com/question/29042018
https://wenku.baidu.com/view/36fa145a3169a4517623a313.html
社区发现算法 - Fast Unfolding(Louvian)算法初探的更多相关文章
- barnes-hut算法 && Fast Multipole Methods算法
barnes-hut算法 http://arborjs.org/docs/barnes-hut Fast Multipole Methods算法 http://www.umiacs.umd.edu/~ ...
- 社区发现(Community Detection)算法 [转]
作者: peghoty 出处: http://blog.csdn.net/peghoty/article/details/9286905 社区发现(Community Detection)算法用来发现 ...
- 社区发现(Community Detection)算法(转)
作者: peghoty 出处: http://blog.csdn.net/peghoty/article/details/9286905 社区发现(Community Detection)算法用来发现 ...
- 社区发现(Community Detection)算法
作者: peghoty 出处: http://blog.csdn.net/peghoty/article/details/9286905 社区发现(Community Detection)算法用来发现 ...
- 模块度与Louvain社区发现算法
Louvain算法是基于模块度的社区发现算法,该算法在效率和效果上都表现较好,并且能够发现层次性的社区结构,其优化目标是最大化整个社区网络的模块度. 模块度(Modularity) 模块度是评估一个社 ...
- 社区发现算法问题&&NetworkX&&Gephi
在做东西的时候用到了社区发现,因此了解了一下有关社区发现的一些问题 1,社区发现算法 (1)SCAN:一种基于密度的社团发现算法 Paper: <SCAN: A Structural Clust ...
- Top Leaders社区发现算法(top leaders community detection approach in information networks)
一.概念 复杂网络:现实生活中各种系统都可以看做成复杂网络,复杂网络构成包括节点和边,节点是网络中的基本组成单元,节点之间的联系或者关系是网络中的边.例如 电力网络:基站代表节点,基站之间是否互通表示 ...
- SLAP(Speaker-Listener Label Propagation Algorithm)社区发现算法
其中部分转载的社区发现SLPA算法文章 一.概念 社区(community)定义:同一社区内的节点与节点之间关系紧密,而社区与社区之间的关系稀疏. 设图G=G(V,E),所谓社区发现是指在图G中确定n ...
- 社区发现SLPA算法
社区(community)定义:同一社区内的节点与节点之间关系紧密,而社区与社区之间的关系稀疏. 设图G=G(V,E),所谓社区发现是指在图G中确定nc(>=1)个社区C={C1,C2,..., ...
随机推荐
- JVM内存结构,运行机制
三月十号,白天出去有事情出去了一天,晚上刚到食堂就接到阿里电话, 紧张到不行,很多基础的问题都不知道从哪里说了orz: 其中关于JVM内存结构,运行机制,自己笔记里面有总结的,可当天还是一下子说不出来 ...
- 深入理解group by 语句的执行顺序 from→where→group by→select(含聚合函数)
由于之前没有对group by 语句的执行顺序(执行原理)做深入的了解,所以导致在实际应用过程中出现了一些问题.举个简单的粟子,比如一个表testA中的所有数据如下图: 我现在想从testA中查询us ...
- Python 经典面试题汇总之数据库篇
数据库和缓存 1.列举常见的关系型数据库和非关系型都有那些? 关系型数据库(需要有表结构) mysql.oracle.splserver.postgresql.db2.sybase 非关系型数据库(是 ...
- 在Windows7中的DPI与主题的问题
测试环境Windows7x64,vb6.0 测试在XP系统下,DPI计算似乎没问题 Screen.TwipsPerPixelX=1440/DPI=1440/96=15Screen.TwipsPerPi ...
- LeetCode算法题-Self Dividing Numbers(Java实现)
这是悦乐书的第305次更新,第324篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第173题(顺位题号是728).自分割数是一个可被其包含的每个数字整除的数字.例如,12 ...
- ubuntu安装Nginx
什么都不说了 直接干 一.安装Nginx 首先从Nginx的官网下载最新的稳定版本1.14.0:nginx 1.解压安装包 1.root@ubuntu:tar -zxf nginx-1.14.0.ta ...
- Linux学习-汇总
1.基础linux学习 Linux-基础学习(一)-基本命令 Linux-基础学习(二)-基本部署 Linux-基础学习(三)-Nginx学习 Linux-基础学习(四)-部署图书管理系统项目 Lin ...
- windows 10隐藏各种文件夹
1.windows键+R打开运行,或者Ctrl+Alt+Del键调出任务管理器--文件--运行新的任务,然后出入"regedit"打开注册表: 2.按目录找到:[-HKEY_CLA ...
- Spring Cloud Netflix vs Spring Cloud Alibaba
Spring Cloud Netflixhttps://spring.io/projects/spring-cloud-netflix spring-cloud-alibaba/README-zh.m ...
- Java的常量和变量
一.标识符 如类名,数字不能作为标识符的首字母(以字母或者下划线或者$开头且不能有空格) 注意和Python的区别,Python中标识符由字母.下划线和数字组成,且数字不能开头,也是严格区分大小写(但 ...