FP-tree算法比Apriori算法更难实现,主要是其中用的一些特殊的数据结构,对我来说都不太熟,不像Apriori只用一些数组就能简单得实现。
失败的例子
和Apriori一样,我第一次实现的时候,也是想着用的vector的存储结构来实现树,定义了一个如下的结构体
struct TreeNode
{
int Id;
int Co;
TreeNode *Parent;
TreeNode *HeadNext;
vector<TreeNode*> Children;
};
int InitTree(TreeNode &T)
{
TreeNode temp;
temp.Id=0;
temp.Co=0;
temp.Parent=NULL;
temp.HeadNext=NULL;
temp.Children.clear();
T=temp;
return 0;
}
但是在扫描数据库建树的时候,效率极其低下,扫描8万行的数据集大概需要20个小时。可能是对树的构造还是不太熟,虽然这样子能够应该能够实现建成一棵FPTree,但是不实用,所以就没继续照着这个思路寻找条件模式基。
int AddTree(TreeNode &T,int a,int next,vector<vector<int> > sim)//sim为去除了非频繁一项集且排序好的全体数据集
{
if(next>=sim[a].size()) return next;
else{
int locate=0;
TreeNode *NewNode;
NewNode=new(TreeNode);
int to=-1;
if(T.Children.size()==0)
{
NewNode->Id=sim[a][next];
next++;
NewNode->Co=1;
NewNode->Parent=&T;
NewNode->Children.clear();
T.Children.push_back(NewNode);
locate=T.Children.size()-1;
//delete NewNode;
AddTree(*T.Children[locate],a,next,sim);
}
else{
for(int i=0;i<T.Children.size();i++)
{
if(T.Children[i]->Id==sim[a][next])
{
next++;
to=i;
T.Children[i]->Co++;
//delete NewNode;
AddTree(*T.Children[i],a,next,sim);
//break;
}
}
if(to==-1)
{
NewNode->Id=sim[a][next];
next++;
NewNode->Co=1;
NewNode->Parent=&T;
NewNode->Children.clear();
T.Children.push_back(NewNode);
locate=T.Children.size()-1;
//delete NewNode;
AddTree(*T.Children[locate],a,next,sim);
}
}
}
}
void CreateTree(TreeNode &T,vector<vector<int> > sim)
{
int i=0;
while(i<10)
{
if(AddTree(T,i,0,sim)>=sim[i].size())
{
i++;
}
//cout<<i<<endl;
}
}
第二次尝试
没办法只能去网上找找如何建FP-Tree,结果大部分都是用Python,Java实现,只找到了一篇数据挖掘作业——FP Tree算法之C++实现,不过里面出现了map、Tire树等我不会的结构,接下来打算参照该算法,然后使用我唯一会一点的vector来重新实现一遍。
12.05更新
终于应该是成功实现了FPtree的创建,不写代码都不知道自己到底有多菜,稍微复杂一点的工程就写的头疼,还差个核心算法FPgrowth。(今天的生活也是同样的苦涩)
失败again
写了半天又调试不出结果,最后还是报错,应该是内存超限的问题,不知道怎么解决。
失败了也要贴源码系列
void FPGrowth(vector<FPtreeNode> FPTree,HeadNode H)
{
cout<<"OK"<<endl;
HeadNode t0=HeadNode();
vector<vector<HeadNode> > t;//条件模式基
vector<HeadNode> t1;
vector<HeadNode> t2;
for(int i=0;i<FPTree.size();i++)
{
if(H.Id==FPTree[i].Id)
{
for(int j=FPTree[i].Parent;j!=0;j=FPTree[j].Parent)
{
t0.Id=FPTree[j].Id;
t0.Co=FPTree[i].Co;
t1.push_back(t0);
}
t.push_back(t1);
t1.clear();
}
}
if(t.size()==1)
{
cout<<"OK"<<t[0][0].Id<<endl;
//return;
}
int Max=0,lo=0;
for(int i=0;i<t.size();i++)
{
if(t[i].size()>Max)
{
Max=t[i].size();
lo=i;
}
}
for(int i=t[lo].size();i>=0;i--)
{
t0.Id=t[lo][i].Id;
t0.Co=t[lo][i].Co;
t2.push_back(t0);
}
vector<int> b(Max);
for(int i=0;i<t.size();i++)
{
for(int j=0;j<t[i].size();j++)
{
int c=Change(t2,t[i][j].Id);
b[c]+=t[i][j].Co;
}
}
vector<HeadNode> NewHead=BuildHead(b);
vector<vector<HeadNode> > Newsim;
Simplify(t,Newsim,b);
vector<FPtreeNode> NewFPTree;
NewFPTree=CreateTree(Newsim,NewHead);
if(NewFPTree.size()>0)
{
for(int i=NewHead.size()-1;i>=0;i++)
{
FPGrowth(NewFPTree,NewHead[i]);
}
}
}
12.11更新(成功)
终于在截止日期前一天把FPGrowth算法运行成功了,
不过千分之五的支持度,也就是441运行就报错了,原因是存字典树的内存溢出了。
而且中间有简化数据库,也就是将非频繁一项集移除数据库并且将剩余项按出现频率大小排序的一步极其耗时,一大半时间浪费在这,因此运行时间比我的Apriori算法慢得多,还有改进的地方。
改完之后就相对正常了。
结束
今天刚把两个算法给老师检查完,Apriori算法要求计算1%和千分之五的支持度,FPGrowth要求计算千分之五,千分之二,千分之一的支持度。
emmm两个程序最多只能跑千分之五的,更高的都需要跑个几分钟。千分之五的两个运行时间都差不多十几秒,FP快一点能跑个9s。
同组检查的大神跑个千分之一都是秒出答案的,最高跑了十万分之三,九十多秒出结果,被打击的想退学。(自闭:|
源码上传在我的GitHub上。