Kendall tau距离
定义:Kendall tau距离衡量两个序列的相似性,距离越大,相似性越小。具体可用序列a调正次序变为序列b所花费的步骤数来量化。要求序列a和序列b的长度以及元素是一样的。
如 a = { 0 , 3 , 1 , 6 , 2 , 5 , 4 } a=\{0, 3, 1, 6, 2, 5, 4\} a={0,3,1,6,2,5,4}, b = { 1 , 0 , 3 , 6 , 4 , 2 , 5 } b=\{1, 0, 3, 6, 4, 2, 5\} b={1,0,3,6,4,2,5},将a调整顺序化为b的距离
首先为序列 a a a 定义一个基准,用序列 a I n d e x aIndex aIndex 存放 a a a 各元素对应的索引,如下:
a a a 和 a I n d e x aIndex aIndex 满足: $ aIndex[a[i]] = i$ ,通过索引 i i i 在 a a a 中找对应的值,通过值 a [ i ] a[i] a[i] 在 a I n d e x aIndex aIndex 中找对应的索引。
假若从 i = 0 i=0 i=0 开始去遍历 a [ i ] a[i] a[i],然后返回 a I n d e x [ a [ i ] ] aIndex[a[i]] aIndex[a[i]], 正好可以得到一个有序的序列 { 0 , 1 , 2 , 3 , 4 , 5 , 6 } \{0, 1, 2, 3, 4, 5, 6\} {0,1,2,3,4,5,6}。
如果 b = = a b == a b==a ,那么从从 i = 0 i=0 i=0 开始去遍历 b [ i ] b[i] b[i],然后返回 a I n d e x [ b [ i ] ] aIndex[b[i]] aIndex[b[i]],也应是一个有序的序列。但当 b ≠ a b \neq a b=a 时,那么 a I n d e x [ b [ i ] ] aIndex[b[i]] aIndex[b[i]]便不是一个有序的序列。
令 $ bIndex[i] = aIndex[b[i]]$ ,将 $ bIndex[i] $ 从有序变无序的距离便是Kendall tau距离,也即是 $ bIndex[i] $ 中的逆序对数量。
通过上述推导,可将求两个序列的Kendall tau距离转为求映射变换后序列的逆序对数量,可通过归并排序求逆序对。代码如下:
class KendallTau:
def __init__(self):
self.count = 0
def distance(self, a, b):
if len(a) != len(b):
return "a和b的数量不相等"
n = len(a)
aIndex = [0] * n
for i in range(n):
aIndex[a[i]] = i
print(aIndex)
bIndex = [0] * n
for j in range(n):
bIndex[j] = aIndex[b[j]]
print(bIndex)
return self.mergeCount(bIndex)
def mergeCount(self, a):
self.count = 0
self.mergeSort(a, 0, len(a) - 1)
return self.count
def merge(self, a, l, mid, r):
i = l
j = mid + 1
aux = []
while i <= mid and j <= r:
if a[i] > a[j]:
aux.append(a[j])
self.count += mid - i + 1
j += 1
else:
aux.append(a[i])
i += 1
while i <= mid:
aux.append(a[i])
i += 1
while j <= r:
aux.append(a[j])
j += 1
i = l
for num in aux:
a[i] = num
i += 1
def mergeSort(self, a, l, r):
if l >= r:
return
mid = l + r >> 1
self.mergeSort(a, l, mid)
self.mergeSort(a, mid + 1, r)
self.merge(a, l, mid, r)
if __name__ == '__main__':
kt = KendallTau()
a = [0, 3, 1, 6, 2, 5, 4]
b = [1, 0, 3, 6, 4, 2, 5]
res = kt.distance(a, b)
print(res) # 4