本文介绍HITS算法的相关内容。

1.算法来源

2.算法原理

3.算法证明

4.算法实现

4.1 基于迭代法的简单实现

4.2 MapReduce实现

5.HITS算法的缺点

6.写在最后

参考资料


1. 算法来源

1999年,Jon Kleinberg 提出了HITS算法。作为几乎是与PageRank同一时期被提出的算法,HITS同样以更精确的搜索为目的,并到今天仍然是一个优秀的算法。

HITS算法的全称是Hyperlink-Induced Topic Search。在HITS算法中,每个页面被赋予两个属性:hub属性和authority属性。同时,网页被分为两种:hub页面和authority页面。hub,中心的意思,所以hub页面指那些包含了很多指向authority页面的链接的网页,比如国内的一些门户网站;authority页面则指那些包含有实质性内容的网页。HITS算法的目的是:当用户查询时,返回给用户高质量的authority页面。


2. 算法原理

很多算法都是建立在一些假设之上的,HITS算法也不例外。HITS算法基于下面两个假设[^ref_1]:

  • 一个高质量的authority页面会被很多高质量的hub页面所指向。
  • 一个高质量的hub页面会指向很多高质量的authority页面。

什么叫“高质量”,这由每个页面的hub值和authority值确定。其确定方法为:

  • 页面hub值等于所有它指向的页面的authority值之和。
  • 页面authority值等于所有指向它的页面的hub值之和。

为了让大家快速理解HITS算法,先举一个简单的例子[^ref_2]。

图中共有3个网页,它们构成了一个有向图。我们设每个网页的初始hub值和authority值都为1。记\(h(p)\)为页面\(p\)的hub值,\(a(p)\)为页面\(p\)的authority值。则有\(h(1)=h(2)=h(3)=1\),\(a(1)=a(2)=a(3)=1\)。

HITS算法的计算过程也是一个迭代的过程。在第一次迭代中,有:

\[a(1) = 0, a(2) = 0, a(3) = h(1) + h(2) = 2 (没有页面指向网页1和网页2)\\
h(1) = a(3) = 2, h(2) = a(3) = 2, h(3) = 0 (网页3没有指向任何页面)
\]

这里就已经可以看出网页3是一个相对好的authority页面,而网页1和网页2是相对好的hub页面。其实到这里迭代也可以结束了,因为再迭代下去无非是\(a(3)\),\(h(1)\)与\(h(2)\)的值不断增大,而哪个是hub页面,哪个是authority页面并不会改变。

上面的简单例子只是为了帮助理解,省略掉了很多步骤和细节。下面将详细地介绍HITS算法[^ref_3]:。

与PageRank算法不同,HITS算法是在用户搜索后运行的,所以HITS算法的处理对象集合肯定得小很多。

首先,我们需要确定这个集合。整个互联网中的网页之间的关系可以抽象为一个有向图\(G = (V,E)\),当有一个搜索请求产生时(不妨设关键字为\(\sigma\)),我们可以取所有包含关键字\(\sigma\)的网页组成的集合\(Q_\sigma\)为初始集合,并在这个集合上运行我们的HITS算法。然而,这个集合却有着明显的缺陷:这个集合可能非常之大,大到包含了数百万个网页,而这显然不是理想的集合大小。于是,我们进而想找到一个更小的集合\(S_\sigma\),满足以下条件:

  1. \(S_\sigma\)确实足够小。
  2. \(S_\sigma\)包含很多与查询相关的页面。
  3. \(S_\sigma\)包含很多高质量的authority页面。

如何找到这个\(S_\sigma\)集合?我们假设用户输入关键字搜索,搜索引擎使用一个基于文本的引擎进行搜索。然后我们取排名(按照相关度排名)最靠前的t(t一般取200左右)个网页作为初始集合,记为根集合\(R_\sigma\)。这个集合满足我们上面提到的前两个条件,但是还远远不能满足第三个条件。

于是,我们需要扩展\(R_\sigma\)。一般认为,一个与关键字相关的高质量的网页即使不在\(R_\sigma\)中,那也很可能在\(R_\sigma\)中有某些网页指向它。基于此,我们扩展\(R_\sigma\)的过程如下(摘自Jon Kleinberg 的论文):

Subgraph(\(\sigma\), \(\psi\), t, d)

  \(\sigma\): a query string.

  \(\psi\): a text-based search engine.

  t, d: natural numbers.

  Let \(R_\sigma\) denote the top t results of \(\psi\) on \(\sigma\).

  Set \(S_\sigma\) := \(R_\sigma\)

  For each page p \(\in\) \(R_\sigma\)

    Let \(\Gamma^+(p)\) denote the set of all pages p points to.

    Let \(\Gamma^-(p)\) denote the set of all pages pointing to p.

    Add all pages in \(\Gamma^+(p)\) to \(S_\sigma\).

    If \(|\Gamma^-(p)| \leq d\), then

      Add all pages in \(\Gamma^-(p)\) to \(S_\sigma\).

    Else

      Add an arbitrary set of d pages from \(\Gamma^-(p)\) to \(S_\sigma\).

  End

  Return \(S_\sigma\)

一开始我们令\(S_\sigma\) = \(R_\sigma\)。然后通过上面的方法,我们将所有被\(R_\sigma\)中网页所指向的网页加入到\(S_\sigma\)中,再把一定数量的指向\(R_\sigma\)集合中网页的那些网页(每个\(R_\sigma\)中网页最多能添加d个指向它的网页)加入到\(S_\sigma\)中。为了保证\(S_\sigma\)集合的合适的大小,d不能太大,一般设置为50左右。通常情况下,扩展之后集合的大小为1000~5000个网页,满足上面的三个条件。

在计算hub值和authority值之前,我们还需要对\(S_\sigma\)进行一下处理。我们把同一个“域名”(域名指一个网站)下的网页之间的链接全部删除,因为通常这些链接只是为了让人能在这个网站下的不同网页之间进行切换,例如网站内的导航链接。在HITS算法中,这些链接与不同网站之间的链接相比,肯定是后者更能体现hub值和authority值的传递关系。所以我们在\(S_\sigma\)集合中删除这些链接,形成新集合\(G_\sigma\)。

现在,就可以开始计算hub值和authority值了[^ref_4]。我们用\(h(p)\)表示页面\(p\)的hub值,\(a(p)\)表示页面\(p\)的authority值。首先令每个页面的初始hub值\(h(p)\)为1,初始authority值\(a(p)\)也为1。然后就开始迭代计算的过程(n为\(G_\sigma\)中总的网页数):

\[\forall p, a(p) = \sum_{i = 1}^n h(i), \\
\forall p, h(p) = \sum_{i = 1}^n a(i)
\]

每一轮迭代结束,都需要进行标准化,使\(\sum_{i = 1}^n h(i)^2 = \sum_{i = 1}^n a(i)^2 = 1\)。标准化的必要性将在算法证明部分解释。

什么时候迭代结束呢?我们可以设置一个迭代次数上限k来控制,或者设定一个阈值,当变化小于阈值的时候迭代结束。然后只要返回给用户authority值靠前的十几个网页就行了。

好了,HITS算法的原理其实就这么点,十分通俗易懂。


3. 算法证明

上面说到如何控制迭代的终止,而这又有个前提条件,那就是经过不断的迭代,每个网页的hub值和authority值最终会收敛。下面我们就来证明HITS算法的收敛性。

为了证明的方便,我们用矩阵的方式来表示HITS算法。

对于初始集合\(G_\sigma\),用一个矩阵\(M\)表示\(G_\sigma\)中网页之间的关系:\(m_{ij} = 1\)表示网页\(i\)指向网页\(j\),否则为0。用向量\(H\)表示所有页面的hub值,其中第i个分量表示网页i的hub值;用向量\(A\)表示所有页面的authority值,其中第i个分量表示网页i的authority值。所有页面的hub值和authority值初始都为1。例如上面算法原理的例子中的图就可以表示为:

\[ M =
\left(
\begin{array}{ccc}
0 & 0 & 1 \\
0 & 0 & 1 \\
0 & 0 & 0 \\
\end{array}
\right)
\]

然后可以计算:

\[A = M^T H =
\left(
\begin{array}{ccc}
0 & 0 & 0 \\
0 & 0 & 0 \\
1 & 1 & 0 \\
\end{array}
\right)
\left(
\begin{array}{ccc}
1 \\
1 \\
1 \\
\end{array}
\right)
=
\left(
\begin{array}{ccc}
0 \\
0 \\
2 \\
\end{array}
\right), \\
H = M A =
\left(
\begin{array}{ccc}
0 & 0 & 1 \\
0 & 0 & 1 \\
0 & 0 & 0 \\
\end{array}
\right)
\left(
\begin{array}{ccc}
0 \\
0 \\
2 \\
\end{array}
\right)
=
\left(
\begin{array}{ccc}
2 \\
2 \\
0 \\
\end{array}
\right)
\]

一般的,我们有:

\[A_k = M^T H_{k - 1}, \\
H_k = M A_k \\
(每一轮先计算A_k,然后根据A_k计算H_k)
\]

更进一步,有:

\[A_k = (M^T M)^{k - 1} M^T Z, \\
H_k = (M M^T)^k Z \\
(其中Z的所有分量都为1)
\]

在这里我们引用一些线性代数的知识:

定理1
一个矩阵与该矩阵的转置的乘积是对称矩阵。
定理2
实对称矩阵的特征值都是实数,且若矩阵大小为n * n,则其必有n个实特征值(包含重根)。
定理3
含有n个特征值的n阶矩阵,其主对角线元素之和等于其特征值之和。
定义1
对于实数矩阵,绝对值最大的特征值称为主特征值,对应的特征向量称为主特征向量。
定理4
如果一个实对称矩阵是非负矩阵,则其主特征向量也是非负的,并且是非0向量。
定理5
令\(W\)为一个n*n实对称矩阵,\(v\)是一个n维向量且与\(W\)的主特征向量\(\omega_w\)非正交,则一个n维单位向量将沿着\(W^k v\)的方向收敛至\(\omega_w\)。

由定理1可知上面的\(M^T M\)和\(M M^T\)都是对称矩阵,且由定理2可知都有n个实特征值。

在\(H_k = (M M^T)^k Z\)中,\(Z\)与\(M^T M\)的主特征向量非正交,所以\(H\)向量最终将收敛至\(M^T M\)的主特征向量。定理5中的单位向量指的就是\(H\)向量,为了保证其每轮迭代时都是一个单位向量,我们在每次迭代之后都对其进行标准化

在\(A_k = (M^T M)^{k - 1} M^T Z\)中,\(M^T Z\)与\(M^T M\)的主特征向量非正交。证明如下(这部分证明在 Jon Kleinberg 的原论文中省略了,自行证明,仅供参考):

\[假设 M^T Z与M^T M的主特征向量\omega_w正交,则有: \\
(M^T Z)^T \omega_w = 0 \\
\Rightarrow (Z^T M) \omega_w = 0 \\
\Rightarrow Z^T (M \omega_w) = 0 \\
\because M是非负矩阵,\omega_w也是非负矩阵 \\
\therefore M \omega_w是非负矩阵
\]

我们只要再证\(M \omega_w\)不是\(0\)矩阵,就可以推翻我们的假设了。因为\(Z^T\)的所有分量都为1,所以\(Z^T\)与非负且非0矩阵的内积一定不为0。在进一步证明之前,我们先证\(M^T M 的主特征值 \lambda_w \neq 0\):

\[令m_{ij}为矩阵M第i行第j列的元素 \\
即M = \left(
\begin{array}{ccc}
m_{11} & \dots & m_{1n} \\
\vdots & m_{ij} & \vdots \\
m_{n1} & \dots & m_{nn} \\
\end{array}
\right),
M^T = \left(
\begin{array}{ccc}
m_{11} & \dots & m_{n1} \\
\vdots & m_{ji} & \vdots \\
m_{1n} & \dots & m_{nn} \\
\end{array}
\right) \\
则M^T M 的主对角线元素w_{ii} = \sum_{j = 1}^n m_{ji}^2 \\
\because M不是0矩阵 \\
\therefore w_{ii}不全为0 \\
\therefore M^T M 主对角线元素之和必不为0 \\
又 \because 由定理2和定理3可知对称矩阵的主对角线元素之和为特征值之和 \\
\therefore M^T M 的特征值之和必不为0 \\
\therefore M^T M 的主特征值 \lambda_w \neq 0
\]

下面证明\(M \omega_w\)不是\(0\)矩阵:

\[\because \omega_w是M^T M的主特征向量 \\
\therefore 有 M^T M \omega_w = \lambda_w \omega_w,其中 \lambda_w 为主特征值 \\
假设M \omega_w 是0矩阵,则: \\
M^T M \omega_w = 0 \Rightarrow \lambda_w \omega_w = 0 \\
但这显然不成立 \\
\because \lambda_w \neq 0 且 \omega_w是非负矩阵 \\
\therefore \lambda_w \omega_w \neq 0 \\
\therefore 假设不成立 \\
\therefore M \omega_w 不是0矩阵
\]

再结合上面的结论:\(M \omega_w是非负矩阵\),即可得出:\(M \omega_w\)是非负矩阵且不是\(0\)矩阵。

所以上面的假设:\(假设 M^T Z与M^T M的主特征向量\omega_w 正交\) 不成立。所以\(M^T Z\)与\(M^T M\)的主特征向量非正交。也即\(A\)向量最终将收敛至\(M^T M\)的主特征向量。同样的,为了保证其每轮迭代时都是一个单位向量,我们在每次迭代之后都对其进行标准化

至此,我们便证明了HITS算法的收敛性。


4. 算法实现

下面的代码原理与我的另一篇博客PageRank相似。

4.1 基于迭代法的简单实现

用python实现,需要先安装python-graph-core。

class HITSIterator:
__doc__ = '''计算一张图中的hub,authority值''' def __init__(self, dg):
self.max_iterations = 100 # 最大迭代次数
self.min_delta = 0.0001 # 确定迭代是否结束的参数
self.graph = dg self.hub = {}
self.authority = {}
for node in self.graph.nodes():
self.hub[node] = 1
self.authority[node] = 1 def hits(self):
"""
计算每个页面的hub,authority值
:return:
"""
if not self.graph:
return flag = False
for i in range(self.max_iterations):
change = 0.0 # 记录每轮的变化值
norm = 0 # 标准化系数
tmp = {}
# 计算每个页面的authority值
tmp = self.authority.copy()
for node in self.graph.nodes():
self.authority[node] = 0
for incident_page in self.graph.incidents(node): # 遍历所有“入射”的页面
self.authority[node] += self.hub[incident_page]
norm += pow(self.authority[node], 2)
# 标准化
norm = sqrt(norm)
for node in self.graph.nodes():
self.authority[node] /= norm
change += abs(tmp[node] - self.authority[node]) # 计算每个页面的hub值
norm = 0
tmp = self.hub.copy()
for node in self.graph.nodes():
self.hub[node] = 0
for neighbor_page in self.graph.neighbors(node): # 遍历所有“出射”的页面
self.hub[node] += self.authority[neighbor_page]
norm += pow(self.hub[node], 2)
# 标准化
norm = sqrt(norm)
for node in self.graph.nodes():
self.hub[node] /= norm
change += abs(tmp[node] - self.hub[node]) print("This is NO.%s iteration" % (i + 1))
print("authority", self.authority)
print("hub", self.hub) if change < self.min_delta:
flag = True
break
if flag:
print("finished in %s iterations!" % (i + 1))
else:
print("finished out of 100 iterations!") print("The best authority page: ", max(self.authority.items(), key=lambda x: x[1]))
print("The best hub page: ", max(self.hub.items(), key=lambda x: x[1])) if __name__ == '__main__':
dg = digraph() dg.add_nodes(["A", "B", "C", "D", "E"]) dg.add_edge(("A", "C"))
dg.add_edge(("A", "D"))
dg.add_edge(("B", "D"))
dg.add_edge(("C", "E"))
dg.add_edge(("D", "E"))
dg.add_edge(("B", "E"))
dg.add_edge(("E", "A")) hits = HITSIterator(dg)
hits.hits()

程序中给出的网页之间的关系如下:

运行结果如下:

This is NO.9 iteration
authority {'E': 0.7886751345855355, 'C': 0.2113248654398108, 'B': 0.0, 'A': 7.119870133749228e-06, 'D': 0.5773502691457247}
hub {'E': 3.6855159786102477e-06, 'C': 0.40824829046663563, 'B': 0.7071067811721405, 'A': 0.40824829046663563, 'D': 0.40824829046663563}
finished in 9 iterations!
The best authority page: ('E', 0.7886751345855355)
The best hub page: ('B', 0.7071067811721405)

4.2 MapReduce实现

MapReduce是一个高效的分布式计算框架,在这里就不多做介绍了(若还不怎么了解MapReduce可以参考我另一篇博客PageRank,里面有简单的原理介绍和代码展示)。

下面是实现HITS算法的类,其中注释较为详细,就不多做解释了:

class HITSMapReduce:
__doc__ = '''计算一张图中的hub,authority值''' def __init__(self, dg):
self.max_iterations = 100 # 最大迭代次数
self.min_delta = 0.0001 # 确定迭代是否结束的参数 # graph表示整个网络图。是字典类型。
# graph[i][authority][0] 存放第i网页的authority值
# graph[i][authority][1] 存放第i网页的入链网页,是一个列表
# graph[i][hub][0] 存放第i网页的hub值
# graph[i][hub][1] 存放第i网页的出链网页,是一个列表
self.graph = {}
for node in dg.nodes():
self.graph[node] = {"authority": [1, dg.incidents(node)], "hub": [1, dg.neighbors(node)]} @staticmethod
def normalize(ah_list):
"""
标准化
:param ah_list: 一个列表,其元素为(网页名,数值)
:return: 返回一个标准化的列表,其元素为(网页名,标准化的数值)
"""
norm = 0
for ah in ah_list:
norm += pow(ah[1], 2) norm = sqrt(norm)
return [(ah[0], ah[1] / norm) for ah in ah_list] def hits_authority_mapper(self, input_key, input_value):
"""
用于计算每个页面能获得的hub值,这个hub值将传递给页面的authority值
:param input_key: 网页名,如 A
:param input_value: self.graph[input_key],即这个网页的相关信息,包含两个字典,{a...}和{h...}
:return: [(网页名, 0.0), (出链网页1, A的hub值), (出链网页2, A的hub值)...]
"""
return [(input_key, 0.0)] + \
[(out_link, input_value["hub"][0]) for out_link in input_value["hub"][1]] def hits_hub_mapper(self, input_key, input_value):
"""
用于计算每个页面能获得的authority值,这个authority值将传递给页面的hub值
:param input_key: 网页名,如 A
:param input_value: self.graph[input_key],即这个网页的相关信息,包含两个字典,{a...}和{h...}
:return: [(网页名, 0.0), (入链网页1, A的authority值), (入链网页2, A的authority值)...]
"""
return [(input_key, 0.0)] + \
[(in_link, input_value["authority"][0]) for in_link in input_value["authority"][1]] def hits_reducer(self, intermediate_key, intermediate_value_list):
"""
统计每个网页获得的authority或hub值
:param intermediate_key: 网页名,如 A
:param intermediate_value_list: A所有获得的authority值或hub值的列表:[0.0,获得的值,获得的值...]
:return: (网页名,计算所得的authority值或hub值)
"""
return intermediate_key, sum(intermediate_value_list) def hits(self):
"""
计算authority值与hub值,各需要调用一次mapreduce模块
:return: self.graph,其中的 authority值与hub值 已经计算好
"""
iteration = 1 # 迭代次数
change = 1 # 记录每轮迭代后的PR值变化情况,初始值为1保证至少有一次迭代
while change > self.min_delta:
print("Iteration: " + str(iteration)) # 计算每个页面的authority值并标准化
# new_authority为一个列表,元素为:(网页名,此轮迭代所得的authority值)
new_authority = HITSMapReduce.normalize(
MapReduce.map_reduce(self.graph, self.hits_authority_mapper, self.hits_reducer)) # 计算每个页面的hub值并标准化
# new_hub为一个列表,元素为:(网页名,此轮迭代所得的hub值)
new_hub = HITSMapReduce.normalize(
MapReduce.map_reduce(self.graph, self.hits_hub_mapper, self.hits_reducer)) # 计算此轮 authority值+hub值 的变化情况
change = sum(
[abs(new_authority[i][1] - self.graph[new_authority[i][0]]["authority"][0]) for i in range(len(self.graph))])
change += sum(
[abs(new_hub[i][1] - self.graph[new_hub[i][0]]["hub"][0]) for i in range(len(self.graph))])
print("Change: " + str(change)) # 更新authority值与hub值
for i in range(len(self.graph)):
self.graph[new_authority[i][0]]["authority"][0] = new_authority[i][1]
self.graph[new_hub[i][0]]["hub"][0] = new_hub[i][1] iteration += 1
return self.graph

下面是一个测试用例:

if __name__ == '__main__':
dg = digraph() dg.add_nodes(["A", "B", "C", "D", "E"]) dg.add_edge(("A", "C"))
dg.add_edge(("A", "D"))
dg.add_edge(("B", "D"))
dg.add_edge(("C", "E"))
dg.add_edge(("D", "E"))
dg.add_edge(("B", "E"))
dg.add_edge(("E", "A")) h = HITSMapReduce(dg)
hits_result = h.hits() print("The final iteration result is")
for key, value in hits_result.items():
print(key + " authority: ", value["authority"][0], " hub: ", value["hub"][0]) max_authority_page = max(hits_result.items(), key=lambda x: x[1]["authority"][0])
max_hub_page = max(hits_result.items(), key=lambda x: x[1]["hub"][0])
print("The best authority page: ", (max_authority_page[0], max_authority_page[1]["authority"][0]))
print("The best hub page: ", (max_hub_page[0], max_hub_page[1]["hub"][0]))

运行结果为:

The final iteration result is
E authority: 0.7886751345948128 hub: 8.64738646858812e-10
A authority: 7.060561487452559e-10 hub: 0.408267180858587
C authority: 0.2113248654051872 hub: 0.40823884510260666
B authority: 0.0 hub: 0.7071067809972986
D authority: 0.5773502691896258 hub: 0.40823884510260666
The best authority page: ('E', 0.7886751345948128)
The best hub page: ('B', 0.7071067809972986)

以上便是HITS算法的MapReduce实现。


5. HITS算法的缺点

  • 计算效率低

这里说的“效率低”是针对其实时计算的特点而提出的。HITS算法是在用户提出搜索请求之后才开始运行的,然而计算出结果又需要多次迭代计算,所以就这点上来说HITS算法效率仍然较低。

  • 主题漂移

在算法原理部分我们介绍了HITS算法是如何生成初始集合\(G_\sigma\)。从根集合\(R_\sigma\)我们通过链接添加网页的方法进行扩展,但这也很可能添加进与搜索主题无关的网页。若是这部分网页中又恰恰有着一些高质量的authority页面,则很有可能返回给用户,降低用户的搜索体验。

  • 作弊网页

试想我们弄一个页面指向很多高质量的authority页面,那么这个页面就成为了一个高质量的hub页面。然后再弄个链接指向自己的搓网页,按照HITS算法,将大大提升自己的搓网页的authority值。

  • 稳定性差

对于一个网页集合,若是删除其中的某条链接,就有可能造成一些网页的hub值和authority值发生巨大变化。


6. 写在最后

以后想到什么再写上来吧。


参考资料

1:《这就是搜索引擎:核心技术详解》,张俊林

2:The Mathematics of Web Search,这个网站上有HITS和PageRank的一些数学知识。

3:原论文:Authoritative Sources in a Hyperlinked Environment

4:“标准参考文献”:维基百科

HITS算法--从原理到实现的更多相关文章

  1. HITS算法简介

    1.算法名称 超文本敏感标题搜索 (Hyperlink-Induced Topic Search) 2.算法背景 HITS 算法是由康奈尔大学的Jon Kleinberg 博士于1997 年首先提出的 ...

  2. 机器学习系列------1. GBDT算法的原理

    GBDT算法是一种监督学习算法.监督学习算法需要解决如下两个问题: 1.损失函数尽可能的小,这样使得目标函数能够尽可能的符合样本 2.正则化函数对训练结果进行惩罚,避免过拟合,这样在预测的时候才能够准 ...

  3. Atitit 贝叶斯算法的原理以及垃圾邮件分类的原理

    Atitit 贝叶斯算法的原理以及垃圾邮件分类的原理 1.1. 最开始的垃圾邮件判断方法,使用contain包含判断,只能一个关键词,而且100%概率判断1 1.2. 元件部件串联定律1 1.3. 垃 ...

  4. Atitit.java图片图像处理attilax总结  BufferedImage extends java.awt.Image获取图像像素点image.getRGB(i, lineIndex); 图片剪辑/AtiPlatf_cms/src/com/attilax/img/imgx.javacutImage图片处理titit 判断判断一张图片是否包含另一张小图片 atitit 图片去噪算法的原理与

    Atitit.java图片图像处理attilax总结 BufferedImage extends java.awt.Image 获取图像像素点 image.getRGB(i, lineIndex); ...

  5. mahout中kmeans算法和Canopy算法实现原理

    本文讲一下mahout中kmeans算法和Canopy算法实现原理. 一. Kmeans是一个很经典的聚类算法,我想大家都非常熟悉.虽然算法较为简单,在实际应用中却可以有不错的效果:其算法原理也决定了 ...

  6. Hits算法

    HITS(HITS(Hyperlink - Induced Topic Search) ) 算法是由康奈尔大学( Cornell University ) 的Jon Kleinberg 博士于1997 ...

  7. 转:Reddit排名算法工作原理

    http://www.aqee.net/how-reddit-ranking-algorithms-work/ 这是一篇继<Hacker News 排名算法工作原理>之后的又一篇关于排名算 ...

  8. 链接分析算法之:HITS算法

    链接分析算法之:HITS算法     HITS(HITS(Hyperlink - Induced Topic Search) ) 算法是由康奈尔大学( Cornell University ) 的Jo ...

  9. Hacker News网站的文章排名算法工作原理

    In this post I'll try to explain how Hacker News ranking algorithm works and how you can reuse it in ...

随机推荐

  1. Oracle18c OnlyExadataVersion 安装说明

    1.根据惜分飞还有盖国强老师云和恩墨的文章, 验证了下OnlyExadataVersion的Oracle18c的数据库安装过程. 2.oracle参数修改以及创建用户, 目录, 修改.bash_pro ...

  2. hive分区表

    分区表创建 row format delimited fields terminated by ',';指明以逗号作为分隔符 依靠插入表创建分区表  从表sample_table选择 满足分区条件的 ...

  3. Nginx PREACCESS阶段 如何限制每个客户端每秒处理请求数

    L:56 limit_req_zone $binary_remote_addr zone=one:10m rate=2r/m;#以用户IP作为key 开辟共享内存10M 并且限制每分钟2个请求 rat ...

  4. BZOJ2282 SDOI2011消防/NOIP2007树网的核(二分答案+树形dp)

    要求最大值最小容易想到二分答案.首先对每个点求出子树中与其最远的距离是多少,二分答案后就可以标记上一些必须在所选择路径中的点,并且这些点是不应存在祖先关系的.那么如果剩下的点数量>=3,显然该答 ...

  5. Web Scraper爬取就是这么简单

    这应该是最全的一个文档了 https://www.jianshu.com/p/e4c1561a3ea7 所以我就不介绍了,大家直接看就可以了,有问题可以提出来,我会针对问题对文章进行补充~

  6. git命令行界面

    学习目标:掌握git命令行界面的操作.掌握最基本的clone add commit push pull操作. 先下载客户端:http://github-windows.s3.amazonaws.com ...

  7. Dependency Walker使用说明[转]

    在Windows世界中,有无数块活动的大陆,它们都有一个共同的名字——动态链接库.现在就让我们走进这些神奇的活动大陆,找出它们隐藏已久的秘密吧! 初窥门径:Windows的基石 随便打开一个系统目录, ...

  8. luogu2865 路障 (dijkstra)

    求次短路,dijkstra时同时记下到某点的最短距离和次短距离即可. #include<cstdio> #include<cstring> #include<algori ...

  9. table默认的box-sizing在不同浏览器不同

    转载自:https://blog.csdn.net/csm0912/article/details/88290672

  10. 一起使用mock数据动态创建表格

    在ant-design中,我们创建一个基础table会怎么实现呢? 如下代码可视,我们会自己创建一些数据,在表格中渲染出来,如下 <Card title="基础表格"> ...