哈希树的python实现

一、问题的背景

        给定一组商品购买信息,找到商品购买中频繁出现的商品集。比如说,我们有如下的商品交易信息:

市场购物信息
Tip Items
1 Bread, Milk
2 Bread, Diaper, Beer, Egg
3 Milk, Diaper, Beer, Coke
4 Bread, Milk, Diaper, Beer
5 Bread, Milk, Diaper, Coke

        我们定义,Itemset 为一系列item的集合,比如:{Milk, Bread, Diaper};定义 k-itemset 为包含k个items的itemset;定义support 是所有交易信息中,包含这个 itemset 的子集。比如s({Milk, Bread, Diaper}) = 2/5;定义frequent Itemset 是一组itemset,它的support 大于等于minsup,这个minsup值由我们自己给定。

        为了找出频繁项集,最直观的方法是我们罗列出所有的候选项,然后计算每个候选项的support,最后将满足要求的频繁项保存下来。比如说,我们有M个交易信息储存在数据库中,有N个候选项,那么我们必须要比较MN次,显然它的时间复杂度是非常大的。一个有效的解决途径是我们使用有效的数据结构来存储候选项和交易信息,这样我们不需要将每个候选项与每个交易都匹配一次。

        我们可以将候选集储存在哈希树中,这样我们不需要一个交易信息与每一个候选集进行比较,我们只需要比较那些哈希树中存在的候选集。

二、如何构建哈希树

        假设我们有15个候选集,长度均为3:

{1 4 5}, {1 2 4}, {4 5 7}, {1 2 5}, {4 5 8}, {1 5 9}, {1 3 6}, {2 3 4}, {5 6 7}, {3 4 5}, {3 5 6}, {3 5 7}, {6 8 9}, {3 6 7}, {3 6 8}

我们需要:1、构建一个hash函数。 2、设定叶子的最大大小(每片叶子中能够存储的最大数据),如果某片叶子中的候选集数量超过了限制,我们需要对这个叶节点做分裂处理。

比如说我们构建了如下图所示的hash函数,在第一个层,对候选集的第一项进行判断,比如说我们对{1,2,5}进行判断,由于第一项为1,因此我们将它放到root的左节点。在第二层,我们对候选集的第二项进行判断,{1,2,5}的第二项是2,根据hash函数,我们再想中间走一步。在第三层,{1,2,5}的第三项是5,因此我们往中间走一步。当然,往下走一步仅在于你当前无法存储在3个节点的情况,因为我们定义了叶子的最大size是3,如果你这个节点能够存储下3个数据,那就没必要继续往下分裂了。

由此我们可以建立如下的哈希树:

那么给定一次商品交易信息,比如[1,2,3,5,6],我们应该如何将这次交易与候选项进行比较?根据hash函数,如果我们想左走,那么我们的选择一定是1开头,然后从[2,3,5,6]选择长度为2的子序列,继续往左走的话,由于[2,3,5,6]与哈希函数中的[1,4,7]无法找到共同项,因此往左走是行不通的,这样我们就避免了这次交易与候选集[1,4,5]的比较。同理我们往中间走,根据哈希函数得到了12[3,5,6]的集合,这样也就是有123,125,126三种可能性,因此与哈希树中对应叶节点的125,458,因此我们知道,在这次交易信息中,125这个候选集出现过一次。因此利用哈希树,我们避免了一部分的比较,减少了比较次数。

 

三、哈希树的Python实现

class Node:
    def __init__(self,val=[]):
        self.val = val
        self.children = {}

class HashTree:
    def __init__(self):
        self.root = Node()#建立根节点
        
    def insert(self,val):#给定一个值val,创建一个值为val的节点并将其插入到树中
        root = self.root
        i = 0
        while root.children:#if root has children
            if (val[i]-1) % 3 in root.children.keys():#如果想找的孩子存在
                root = root.children[(val[i]-1)%3]
                i += 1
            else:#if the child doesn't exist
                root.children[(val[i]-1)%3] = Node([val])
                return
                
        
        #so the root doesn't have child
        if len(root.val) < 3:#the value of root is smaller than 3
            root.val.append(val)
            return
        else:#root的值的个数已经等于3了,那么我们必须split root
            root.val.append(val)#先把val加入到root的值当中
            for item in root.val:#对于root值中的的每一项
                j = (item[i]-1) % 3#表示余数是多少
                if j in root.children.keys():#如果余数已经在其中了
                    root.children[j].val.append(item)#将item加入到列表中
                else:
                    root.children[j] = Node()#首先创建该节点
                    root.children[j].val = [item]#如果没有,则创建新列表
                root.val = []                

                if len(root.children[j].val) == 4:#分裂节点后,仍然出现了大于3的情况,那么我们需要继续分裂节点
                    i += 1
                    root = root.children[j]
                    for item in root.val:#对于root值中的的每一项
                        j = (item[i]-1) % 3#表示余数是多少
                        if j in root.children.keys():#如果余数已经在其中了                            
                            root.children[j].val.append(item)#将item加入到列表中
                        else: 
                            root.children[j] = Node()
                            root.children[j].val = [item]#如果没有,则创建新列表
                    root.val = []
                        
    def PrintTree(self,node):#如何 层次的输出树
        if not node.val:#如果这个节点的值不存在
            if not node.children: return#如果孩子也没有
            else:#有孩子了
                res = []
                for item in node.children.values():
                    res += [self.PrintTree(item)]
        else:#这个节点的值存在
            return node.val
        return res
                
                
            
        

        
obj = HashTree()
values = '[{1 2 4}, {1 2 9}, {1 3 5}, {1 3 9}, {1 4 7}, {1 5 8}, {1 6 7}, {1 7 9}, {1 8 9}, {2 3 5}, {2 4 7}, {2 5 6}, {2 5 7}, {2 5 8}, {2 6 7}, {2 6 8}, {2 6 9}, {2 7 8}, {3 4 5}, {3 4 7}, {3 5 7}, {3 5 8}, {3 6 8}, {3 7 9}, {3 8 9}, {4 5 7}, {4 5 8}, {4 6 7}, {4 6 9}, {4 7 8}, {5 6 7}, {5 7 9}, {5 8 9}, {6 7 8}, {6 7 9}]' 
values = values.replace('{','[').replace('}',']').replace(' ',',').replace(',,',',')
values = 'values = ' + values

exec(values)

for item in values:
    obj.insert(item)


print(obj.PrintTree(obj.root))

 

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值