欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Java如何實(shí)現(xiàn)FP-Growth算法

 更新時間:2021年06月22日 14:32:37   作者:Kidca  
學(xué)校里的實(shí)驗(yàn),要求實(shí)現(xiàn)FP-Growth算法.FP-Growth算法比Apriori算法快很多(但是卻比不上時間)在網(wǎng)上搜索后發(fā)現(xiàn)Java實(shí)現(xiàn)的FP-Growth算法很少,且大多數(shù)不太能理解):太菜.所以就自己實(shí)現(xiàn)了一下.這篇文章重點(diǎn)介紹一下我的Java實(shí)現(xiàn) ,需要的朋友可以參考下

FP-Growth算法的Java實(shí)現(xiàn)

這篇文章重點(diǎn)講一下實(shí)現(xiàn)。需要兩次掃描來構(gòu)建FP樹

第一次掃描

第一次掃描,過濾掉所有不滿足最小支持度的項(xiàng);對于滿足最小支持度的項(xiàng),按照全局支持度降序排序。

按照這個需求,可能的難點(diǎn)為如何按照全局支持度對每個事務(wù)中的item排序。

我的實(shí)現(xiàn)思路

  • 掃描原數(shù)據(jù)集將其保存在二維列表sourceData中
  • 維護(hù)一個Table,使其保存每個item的全局支持度TotalSup
  • 在Table中過濾掉低于閾值minSup的項(xiàng)
  • 將Table轉(zhuǎn)換為List,并使其按照TotalSup降序排序
  • 新建一個二維列表freqSource,其保留sourceData中的頻繁項(xiàng),并將每個事務(wù)按全局支持度降序排序

代碼

/**
     * 掃描原數(shù)據(jù)集,生成事務(wù)集
     * @param path 數(shù)據(jù)集路徑
     * @throws IOException
     */

    private void scanDataSet(String path) throws IOException {
        if(path.equals("")){
            path = filePath;
        }
        FileReader fr = new FileReader(path);
        BufferedReader bufferedReader = new BufferedReader(fr);
        String str;
//        int maxLength = 0;
        while ( (str = bufferedReader.readLine())!=null){
            ArrayList<Integer> transaction = new ArrayList<>();
            String[] tempEntry ;
            tempEntry = str.split(" ");
            for(int i =0;i< tempEntry.length;i++){
                if(!tempEntry[i].equals("")){
                    int itemValue = Integer.parseInt(tempEntry[i]);
                    transaction.add(itemValue);
                    if(!similarSingleItemLinkedListHeadsTable.containsKey(itemValue)){
                        similarSingleItemLinkedListHeadsTable.put(itemValue, new SimilarSingleItemLinkedListHead(itemValue,null,1));
                    }else{
                        //將該項(xiàng)的全局支持度+1
                        similarSingleItemLinkedListHeadsTable.get(itemValue).addSupTotal();
                    }
                }
            }
//            if(tempEntry.length>maxLength){
//                maxLength = tempEntry.length;
//            }

            sourceDataSet.add(transaction);

        }
//        System.out.println(maxLength);
        deleteNonFreqInSSILLHTAndSort();
        deleteNonFreqInSDSAndSort();
        bufferedReader.close();
        fr.close();
    }
        /**
     * 去除相似項(xiàng)表(similarSingleItemLinkedListHeadsTable)的非頻繁項(xiàng),并按全局支持度對similarSingleItemLinkedListHeads降序排序
     */
    private void deleteNonFreqInSSILLHTAndSort() {
        Hashtable<Integer,SimilarSingleItemLinkedListHead> copyOfSSILLHT =
                (Hashtable<Integer, SimilarSingleItemLinkedListHead>) similarSingleItemLinkedListHeadsTable.clone();
        Set<Integer> keySet = copyOfSSILLHT.keySet();
        //刪除非頻繁項(xiàng)
        for(int key: keySet){
            if(similarSingleItemLinkedListHeadsTable.get(key).getSupTotal()<minSupCnt){//低于支持度閾值
                similarSingleItemLinkedListHeadsTable.remove(key);
            }
        }
        //按全局支持度排序
        similarSingleItemLinkedListHeadList = new ArrayList<>(similarSingleItemLinkedListHeadsTable.values());
        similarSingleItemLinkedListHeadList.sort(new Comparator<SimilarSingleItemLinkedListHead>() {
            @Override
            public int compare(SimilarSingleItemLinkedListHead o1, SimilarSingleItemLinkedListHead o2) {
                return o2.getSupTotal() - o1.getSupTotal();
            }
        });

    }
        /**
     * 去除事務(wù)集(sourceDataSet)的非頻繁項(xiàng),并且按全局支持度對每個事務(wù)的item進(jìn)行降序排序
     * 其結(jié)果保存在freqSourceSortedDataSet
     */
    private void deleteNonFreqInSDSAndSort(){
        freqSourceSortedDataSet = (ArrayList<ArrayList<Integer>>) sourceDataSet.clone();
        for(int i =0;i<sourceDataSet.size();i++){
            for(int j = 0;j<sourceDataSet.get(i).size();j++){
                int item = sourceDataSet.get(i).get(j);
                // 由于此時SSILLHT里的項(xiàng)都是頻繁項(xiàng),只需要確定item是否存在在其中即可,存在即代表頻繁.
                if(visitSupTotal(item)==-1){
                    //將非頻繁項(xiàng)標(biāo)記為最小整數(shù)值
                    freqSourceSortedDataSet.get(i).set(j,Integer.MIN_VALUE);
                }
            }
            //將標(biāo)記的項(xiàng)移除.
            freqSourceSortedDataSet.get(i).removeIf(e->e == Integer.MIN_VALUE);
            insertSort(freqSourceSortedDataSet.get(i));
        }
        freqSourceSortedDataSet.removeIf(e->e.size() == 0);

    }

第二次掃描

第二次掃描,構(gòu)造FP樹。
參與掃描的是過濾后的數(shù)據(jù),如果某個數(shù)據(jù)項(xiàng)是第一次遇到,則創(chuàng)建該節(jié)點(diǎn),并在headTable中添加一個指向該節(jié)點(diǎn)的指針;否則按路徑找到該項(xiàng)對應(yīng)的節(jié)點(diǎn),修改節(jié)點(diǎn)信息

這里比較簡單,因?yàn)橐呀?jīng)有過濾、排序好的數(shù)據(jù)freqSourceSortedDataSet。我們只需要

  • 遍歷freqSourceSortedDataSet的每一個事務(wù)trans,遍歷trans中的每一個item構(gòu)建FP樹和相似項(xiàng)鏈表
  • 如果某item第一次遇到,則需要創(chuàng)建該節(jié)點(diǎn)并在相似項(xiàng)鏈表中鏈接它。
  • 鏈表不用多說。
  • 這里的FP樹的子節(jié)點(diǎn)是不定個數(shù)的,需要用特殊的數(shù)據(jù)結(jié)構(gòu)。我這里使用了HashTable
  /**
     * 構(gòu)建FP樹
     */
    private void buildFPTree(){
        for(ArrayList<Integer>trans:freqSourceSortedDataSet){
            Node curTreeNode = fpTree.root;
            for(int item :trans){
                if(!curTreeNode.children.containsKey(item)){
                    Node node = new Node(item,1);
                    curTreeNode.children.put(item,node);
                    node.father = curTreeNode;
                    buildSimilarSingleItemLinkedList(item,curTreeNode);
                }else{
                    curTreeNode.children.get(item).sup++;
                }
                curTreeNode=curTreeNode.children.get(item);
            }
        }
    }
    /**
     * 構(gòu)建相似項(xiàng)鏈表
     */
    private void buildSimilarSingleItemLinkedList(int item,Node curTreeNode){
        //找到該item在相似項(xiàng)鏈表中的位置

        int index = searchForItemInHeadsList(item,
                (ArrayList<SimilarSingleItemLinkedListHead>) similarSingleItemLinkedListHeadList);
        if(similarSingleItemLinkedListHeadList.get(index).next == null){
            similarSingleItemLinkedListHeadList.get(index).next = curTreeNode.children.get(item);
        }else{
            Node visitNode = similarSingleItemLinkedListHeadList.get(index).next;
            while (visitNode.nextSimilar!=null){

                visitNode = visitNode.nextSimilar;
            }
            if(visitNode != curTreeNode.children.get(item))
                visitNode.nextSimilar = curTreeNode.children.get(item);
        }
    }
    /**
     * 在HeadList中搜索某項(xiàng)的位置
     * @param item 項(xiàng)
     * @param similarSingleItemLinkedListHeads 頭結(jié)點(diǎn)鏈表
     * @return 位置,-1表示未找到
     */
    private int searchForItemInHeadsList(int item, ArrayList<SimilarSingleItemLinkedListHead> similarSingleItemLinkedListHeads) {
        for(int i =0;i<similarSingleItemLinkedListHeads.size();i++){
            if(similarSingleItemLinkedListHeads.get(i).getItemValue() == item){
                return i;
            }
        }
        return -1;
    }
    

挖掘頻繁項(xiàng)集

這一部分個人覺得是實(shí)現(xiàn)上最困難的部分。但是我在B站或其他地方一涉及到這個地方都講得很快(B站也沒兩個視頻講這玩意兒,吐)。還有不同的概念,比如在黑皮書上講的是前綴路徑,在其他地方有條件模式基等概念。接下來的代碼均按照前綴路徑的說法來實(shí)現(xiàn)。

我們來捋一捋思路,挖掘頻繁項(xiàng)集需要干什么。

首先需要從后向前遍歷相似項(xiàng)鏈表的列表(這一列表已經(jīng)在第一次掃描中按全局支持度排過序了)的每一項(xiàng)。

對每一項(xiàng)遞歸地進(jìn)行如下步驟:

①記錄前綴路徑。我使用的方法是用一個HashSet記錄前綴路徑中出現(xiàn)的所有節(jié)點(diǎn)。

②記錄該FP樹的每一item的支持度。類似于前面的第一次掃描。

③根據(jù)記錄的支持度,如果item頻繁,則該item和當(dāng)前的后綴為頻繁項(xiàng)集。

④再根據(jù)record構(gòu)建該FP樹的相似項(xiàng)鏈表列表,去除掉非頻繁項(xiàng)(類似第一次掃描)和當(dāng)前item構(gòu)成條件FP樹。這里并不需要重新建立一個FP樹的結(jié)構(gòu)來構(gòu)成條件FP樹,因?yàn)橛涗浨熬Y路徑只需要訪問相似項(xiàng)和父項(xiàng)。

⑤對相似項(xiàng)鏈表列表的剩余項(xiàng)再進(jìn)行①步驟,直到相似項(xiàng)鏈表列表中沒有項(xiàng),為終止。

/**
     * 算法執(zhí)行函數(shù)
     * @param minSupCnt 最小支持度計數(shù)
     * @param path 文件路徑
     * @param pT 輸出結(jié)果的項(xiàng)集大小閾值
     */
    public void run(int minSupCnt,String path,int pT) throws IOException {
        this.printThreshold = pT;
        this.minSupCnt = minSupCnt;
        scanDataSet(path);
        buildFPTree();
        for(int i = similarSingleItemLinkedListHeadList.size()-1;i>=0;i--){
            genFreqItemSet(similarSingleItemLinkedListHeadList.get(i).getItemValue()
                    ,fpTree,similarSingleItemLinkedListHeadList,new TreeSet<>());
        }
        //genFreqItemSet(14,fpTree,similarSingleItemLinkedListHeadList,new TreeSet<>());
        System.out.println("頻繁項(xiàng)集個數(shù):\t"+cntOfFreqSet);
    }
/**
     * 生成頻繁項(xiàng)集
     * @param last 最后項(xiàng)
     * @param fPTree 條件FP樹
     * @param fatherSimilarSingleItemLinkedListHeads 父樹的相似項(xiàng)頭結(jié)點(diǎn)鏈表
     * @param freqItemSet 頻繁項(xiàng)集
     */
    private void genFreqItemSet(int last,FPTree fPTree,
                                List<SimilarSingleItemLinkedListHead>fatherSimilarSingleItemLinkedListHeads,TreeSet<Integer>freqItemSet) {

        FPTree conditionalFPTree = new FPTree();
        List<SimilarSingleItemLinkedListHead>similarSingleItemLinkedListHeads = new ArrayList<>();

        TreeSet<Integer>localFreqItemSet = (TreeSet<Integer>) freqItemSet.clone();
        int index ;
        index = searchForItemInHeadsList(last,
                (ArrayList<SimilarSingleItemLinkedListHead>) fatherSimilarSingleItemLinkedListHeads);

        Node firstNode = fatherSimilarSingleItemLinkedListHeads.get(index).next;
        HashSet<Node>record = new HashSet<>();  //用于記錄前綴路徑上出現(xiàn)的節(jié)點(diǎn)
        //記錄前綴路徑
        if(firstNode!=null){
            record.add(firstNode);
            Node nodeToVisitFather = firstNode;
            Node nodeToVisitSimilar = firstNode;
            while (nodeToVisitSimilar!=null){
                nodeToVisitSimilar.supInCFP = nodeToVisitSimilar.sup;
                nodeToVisitFather = nodeToVisitSimilar;
                while (nodeToVisitFather!=null){
                    // 計算supInCFT
                    if(nodeToVisitFather!=nodeToVisitSimilar)
                        nodeToVisitFather.supInCFP += nodeToVisitSimilar.supInCFP;
                    record.add(nodeToVisitFather);
                    nodeToVisitFather = nodeToVisitFather.father;
                }
                nodeToVisitSimilar = nodeToVisitSimilar.nextSimilar;
            }

            //記錄在子樹中的支持度
            Hashtable<Integer,Integer> supRecord = new Hashtable<>();
            record.forEach(new Consumer<Node>() {
                @Override
                public void accept(Node node) {
                    int item = node.item;
                    if(item == -1 ){    //根節(jié)點(diǎn)
                        return;
                    }
                    if(supRecord.containsKey(item)){
                        supRecord.put(item,supRecord.get(item)+ node.supInCFP);
                    }else{
                        supRecord.put(item,node.supInCFP);
                    }

                }
            });
            //輸出結(jié)果
            if(supRecord.get(last)>=minSupCnt){
                localFreqItemSet.add(last);
                if(localFreqItemSet.size()>=printThreshold && !result.contains(localFreqItemSet)){
                    cntOfFreqSet++;
//                    for(int i = localFreqItemSet.size()-1;i>=0;i--){
//                        System.out.print(localFreqItemSet.get(i)+" ");
//                    }
                    localFreqItemSet.forEach(new Consumer<Integer>() {
                        @Override
                        public void accept(Integer integer) {
                            System.out.print(integer+" ");
                        }
                    });
                    result.add(localFreqItemSet);

                    System.out.println("");
                }
            }

            //構(gòu)建相似項(xiàng)鏈表
            record.forEach(new Consumer<Node>() {
                @Override
                public void accept(Node node) {
                    if(node.item == -1){    //根節(jié)點(diǎn)
                        Node visitNode = node;
                        buildConditionalFPTree(conditionalFPTree.root, visitNode,record,
                                (ArrayList<SimilarSingleItemLinkedListHead>) similarSingleItemLinkedListHeads,supRecord,last);
                    }
                }
            });
            //按支持度降序排序
            similarSingleItemLinkedListHeads.sort(new Comparator<SimilarSingleItemLinkedListHead>() {
                @Override
                public int compare(SimilarSingleItemLinkedListHead o1, SimilarSingleItemLinkedListHead o2) {
                    return o2.getSupTotal() - o1.getSupTotal();
                }
            });

            if(similarSingleItemLinkedListHeads.size()>=1){
                //遞歸搜索頻繁項(xiàng)
                for(int i =similarSingleItemLinkedListHeads.size()-1;i>=0;i--){
                    genFreqItemSet(similarSingleItemLinkedListHeads.get(i).getItemValue(),
                            conditionalFPTree,similarSingleItemLinkedListHeads,localFreqItemSet);
                    // similarSingleItemLinkedListHeads.remove(i);
                }
            }
        }
    }
/**
     * 遞歸構(gòu)建條件FP樹
     * @param rootNode 以該節(jié)點(diǎn)為根向下建立條件FP樹
     * @param originalNode  rootNode對應(yīng)在原樹中的節(jié)點(diǎn)
     * @param record    前綴路徑
     * @param similarSingleItemLinkedListHeads  相似項(xiàng)表頭鏈表
     * @param supRecord 支持度計數(shù)的記錄
     * @param last 最后項(xiàng)
     */
    private void buildConditionalFPTree(Node rootNode,Node originalNode,HashSet<Node>record
            ,ArrayList<SimilarSingleItemLinkedListHead>similarSingleItemLinkedListHeads,Hashtable<Integer,Integer>supRecord,int last){
        if(originalNode.children!=null){
            for(int key:originalNode.children.keySet()){    //遍歷originalNode的所有兒子節(jié)點(diǎn),檢查其是否在前綴路徑中
                Node tempNode = originalNode.children.get(key);
                if(record.contains(tempNode)){
                    Node addedNode = new Node(tempNode.item, tempNode.supInCFP);
                    if(last == key){    //去除last的所有節(jié)點(diǎn)
                        tempNode.supInCFP = 0;
                        continue;
                    }
                    if(supRecord.get(key)>=minSupCnt){
                        //addedNode 拷貝 tempNode除兒子節(jié)點(diǎn)外的屬性
                        addedNode.supInCFP = tempNode.supInCFP;
                        rootNode.children.put(tempNode.item, addedNode);
                        addedNode.father = rootNode;
                        //構(gòu)建相似項(xiàng)表
                        int i = searchForItemInHeadsList(tempNode.item,similarSingleItemLinkedListHeads);
                        if(i==-1){
                            similarSingleItemLinkedListHeads.add(new SimilarSingleItemLinkedListHead(key,addedNode, addedNode.supInCFP));
                        }else{
                            similarSingleItemLinkedListHeads.get(i).setSupTotal(similarSingleItemLinkedListHeads.get(i).getSupTotal()+addedNode.supInCFP);
                            Node visitNode = similarSingleItemLinkedListHeads.get(i).next;
                             while (visitNode.nextSimilar!=null){
                                visitNode = visitNode.nextSimilar;
                            }
                            if(visitNode!=addedNode){
                                visitNode.nextSimilar= addedNode;
                            }
                        }
                        buildConditionalFPTree(addedNode,originalNode.children.get(key),record,similarSingleItemLinkedListHeads,supRecord,last);
                        addedNode.supInCFP = 0; //將supInCFP重置為0;
                    }else{
                        buildConditionalFPTree(rootNode,originalNode.children.get(key),record,similarSingleItemLinkedListHeads,supRecord,last);
                    }

                }
            }
        }
    }

完整代碼

FP-Growth

到此這篇關(guān)于詳解Java如何實(shí)現(xiàn)FP-Growth算法的文章就介紹到這了,更多相關(guān)Java實(shí)現(xiàn)FP-Growth算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 全排列算法-遞歸與字典序的實(shí)現(xiàn)方法(Java)

    全排列算法-遞歸與字典序的實(shí)現(xiàn)方法(Java)

    下面小編就為大家?guī)硪黄帕兴惴?遞歸與字典序的實(shí)現(xiàn)方法(Java) 。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • Spring MVC攔截器和跨域請求使用詳解

    Spring MVC攔截器和跨域請求使用詳解

    SpringMVC的攔截器也是AOP思想的一種實(shí)現(xiàn)方式,主要用于攔截用戶的請求并做相應(yīng)的處理,通常應(yīng)用在權(quán)限驗(yàn)證、記錄請求信息的日志、判斷用戶是否登錄等功能上,這篇文章主要介紹了Spring MVC攔截器和跨域請求,需要的朋友可以參考下
    2023-07-07
  • 詳解Java中的阻塞隊(duì)列

    詳解Java中的阻塞隊(duì)列

    在去年的面試過程中,被面試官問道“阻塞隊(duì)列”這個問題,因?yàn)楫?dāng)時并沒有對此問題進(jìn)行深入理解,只是按照自己的理解說明了該問題,最后面試結(jié)果也不太好,今天對該問題進(jìn)行簡要的面試并記錄如下;如有錯誤,歡迎指正,需要的朋友可以參考下
    2021-06-06
  • 深入理解spring的AOP機(jī)制原理

    深入理解spring的AOP機(jī)制原理

    本篇文章主要介紹了深入理解spring的AOP機(jī)制原理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • Java中內(nèi)部類的概念與分類詳解

    Java中內(nèi)部類的概念與分類詳解

    一個類的定義放在另一個類的內(nèi)部,這個類就叫做內(nèi)部類,下面這篇文章主要給大家介紹了關(guān)于Java中內(nèi)部類的概念與分類的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-09-09
  • spring mvc中直接注入的HttpServletRequst安全嗎

    spring mvc中直接注入的HttpServletRequst安全嗎

    這篇文章主要給大家介紹了關(guān)于spring mvc中直接注入的HttpServletRequst是不是安全的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2018-04-04
  • java抓取鼠標(biāo)事件和鼠標(biāo)滾輪事件示例

    java抓取鼠標(biāo)事件和鼠標(biāo)滾輪事件示例

    這篇文章主要介紹了java抓取鼠標(biāo)事件和鼠標(biāo)滾輪事件示例,需要的朋友可以參考下
    2014-05-05
  • SpringAOP實(shí)現(xiàn)日志收集管理功能(步驟詳解)

    SpringAOP實(shí)現(xiàn)日志收集管理功能(步驟詳解)

    這篇文章主要介紹了SpringAOP實(shí)現(xiàn)日志收集管理功能,本文分步驟通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • 詳解Spring boot Admin 使用eureka監(jiān)控服務(wù)

    詳解Spring boot Admin 使用eureka監(jiān)控服務(wù)

    本篇文章主要介紹了詳解Spring boot Admin 使用eureka監(jiān)控服務(wù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • Spring Cloud Feign請求添加headers的實(shí)現(xiàn)方式

    Spring Cloud Feign請求添加headers的實(shí)現(xiàn)方式

    這篇文章主要介紹了Spring Cloud Feign請求添加headers的實(shí)現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04

最新評論