最近刚学完FP-tree,写个简单的实现巩固一下。其中原始项目集只能以字符串的形式读入,输出结果为强规则,中间过程的条件模式基以及频繁模式都可以输出。
知识点:
1.理清楚FP-tree算法流程(这个是必须的)。
2.递归求string的所有子序列。
3.树的灵活运用。(多叉树,father指针,特定结点链表指针)
优点:
实现的还是很详细的,每个小阶段的结果都可以输出。另外使用的方法都是比较简单易懂的。
缺点:
代码技巧性不足导致效率较低。
运行截图:
#include <bits/stdc++.h>
using namespace std;
struct cmp1{
bool operator()(const pair<char,int> a,const pair<char,int> b){
if(a.second == b.second)return a.first > b.first;
return a.second < b.second;
}
};
priority_queue<pair<char,int>,vector<pair<char,int> >,cmp1>Qt;//用于数据内部排序
stack<string> St,S;//存储预处理前数据。预处理后数据
map<char,int>M,hd;//用于统计数据出现次数。记录头指针的编号。
map<string,int>FP;//用于存频繁模式
//-----------------------------------树相关结构及函数定义----------------------------------------
typedef struct Node* node;
node head[10005];
struct Node {//结点定义
char key;
int value;
node next,father;
vector<node> child;
};
node CreateNode(char _key,char _value){//创建结点
node p = new Node;
p->key = _key;
p->value = _value;
p->father = p->next = NULL;
return p;
}
node getNode(char _key,char _value,node _father){//得到处理后的结点
node p = CreateNode(_key,_value);
p->father = _father;
p->next = head[hd[_key]];
head[hd[_key]] = p;
return p;
}
void SolveDate(string s,node rt){//将数据转换到树上
node p = rt;
for(int i=0 ; i<s.length() ; ++i){
if((p->child).empty()){
(p->child).push_back(getNode(s[i],1,p));
p = (p->child).back();
}
else {
vector<node>::iterator it;
for(it=(p->child).begin() ; it!=(p->child).end() ; ++it){
if((*it)->key == s[i]){
(*it)->value++;
p = *it;
break;
}
}
if(it == (p->child).end()){
(p->child).push_back(getNode(s[i],1,p));
p = (p->child).back();
}
}
}
}
int Combination2(string s,int temp,int num,string a,string b,int ZCD,double MC){//求关联规则时求string的所有子序列
if(s.length()-temp < num)return 0;
if(temp == s.length()){//这里不能num==0就停,因为b串未全。
if(FP.find(a) == FP.end() || FP[a] < ZCD)return 0;
if(FP.find(b) == FP.end() || FP[b] < ZCD)return 0;
double de = 1.0*FP[s]/FP[a];
if(de >= MC){
cout << s << " " << a << "-->" << b << " " << de << endl;
return 1;
}
return 0;
}
int ans = 0;
if(num)ans += Combination2(s,temp+1,num-1,a+s[temp],b,ZCD,MC);
ans += Combination2(s,temp+1,num,a,b+s[temp],ZCD,MC);
return ans;
}
void Combination(string s,int temp,char ch,int val,int num,string t){//求频繁模式时求string的所有子序列
if(s.length()-temp < num)return ;
if(num == 0){
priority_queue<pair<char,int>,vector<pair<char,int> >,cmp1>qt;
for(int i=0 ; i<t.length() ; ++i)qt.push(make_pair(t[i],M[t[i]]));
qt.push(make_pair(ch,M[ch]));
t = "";
while(!qt.empty()){
t += qt.top().first;
qt.pop();
}
if(FP.find(t) == FP.end())FP[t] = val;
else FP[t] += val;
return ;
}
Combination(s,temp+1,ch,val,num-1,t+s[temp]);
Combination(s,temp+1,ch,val,num,t);
}
void getFP(string s,char ch,int val){//求频繁模式
for(int i=1 ; i<=s.length() ; ++i){
Combination(s,0,ch,val,i,"");
}
}
void getCPB(){//求条件模式基
for(int i=1 ; head[i]!=NULL ; ++i){
node p = head[i];
/*想输出条件模式基的话取消下面3项的注释*/
//cout << "--项--" << p->key << ":" << endl;// --1
char ch = p->key;
while(p){
string s = "";
if(p->father->father){//如果父节点是根跳过
node pt = p->father;
//cout << p->value << ' ';// --2
while(pt->father){
s += pt->key;
pt = pt->father;
}
//cout << s << endl;// --3
getFP(s,ch,p->value);
}
p = p->next;
}
}
}
//---------------------------------------------------------------------------------
void init(){//初始化
M.clear();
hd.clear();
FP.clear();
while(!St.empty())St.pop();
while(!S.empty())S.pop();
while(!Qt.empty())Qt.pop();
memset(head,NULL,sizeof head);
}
int main(){
init();
//--------------------------------------------变量----------------------------------
int ZCD;//最小支持度阈值
double MC;//最小置信度
//---------------------------------------------数据获取---------------------------------
while(true){
cout << "请输入最小支持度阈值(整数):" << endl;
cin >> ZCD;
if(ZCD < 0){
cout << "输入不合法,请重新输入。" << endl;
continue;
}
cout << "请输入最小置信度(小数):" << endl;
cin >> MC;
if(MC < 0.0 || MC > 1.0){
cout << "输入不合法,请重新输入。" << endl;
}
else break;
}
cout << "请输入数据:" << endl;
/*数据预处理测试样例
最小支持度阈值为 3。
输入数据
facdgimp
abcflo
bfhjmp
bckmos
afcelnop
处理后数据
fcamp
fcabo
fbmp
cbmo
fcaop
*/
string s;
while(cin >> s){
St.push(s);
for(int i=0 ; i<s.length() ; ++i){
if(M.find(s[i]) == M.end())M[s[i]] = 1;
else M[s[i]]++;
}
}
//-----------------------------数据预处理--------------------------------------------------
while(!St.empty()){
string s1 = St.top();
St.pop();
while(!Qt.empty())Qt.pop();
for(int i=0 ; i<s1.length() ; ++i){
if(M[s1[i]] >= ZCD)Qt.push(make_pair(s1[i],M[s1[i]]));
}
s1 = "";
while(!Qt.empty()){
s1 += Qt.top().first;
Qt.pop();
}
if(s1 != "")S.push(s1);
}
/*
输出预处理结果
while(!S.empty()){
cout << S.top() << endl;
S.pop();
}
*/
//----------------------------------------建立头表-------------------------------------------------
map<char,int>::iterator it;
while(!Qt.empty())Qt.pop();
for(it=M.begin() ; it!=M.end() ; ++it){
if(it->second < ZCD)continue;
//------------这里要注意把一阶频繁项加进FP,方便以后求强规则
string t = "";
t += it->first;
FP[t] = it->second;
//-------------
Qt.push(make_pair(it->first,it->second));
}
int tot = 0;
while(!Qt.empty()){
hd[Qt.top().first] = ++tot;
Qt.pop();
}
/*测试头表排序
cout << "-----------------" << endl;
for(it=hd.begin() ; it!=hd.end() ; ++it){
cout << it->first << " " << it->second << endl;
}
*/
node root = CreateNode('-1',0);//参数随意
while(!S.empty()){
string st = S.top();
S.pop();
SolveDate(st,root);
}
getCPB();//求条件模式基
/*//输出频繁模式
cout << "----------------------------------" << endl;
map<string,int>::iterator it2;
for(it2=FP.begin() ; it2!=FP.end() ; ++it2){
if(it2->second >= ZCD){
cout << it2->first << " " << it2->second << endl;
}
}
*/
cout << "--强关联规则:--" << endl;
map<string,int>::iterator it2;
int ans = 0;
for(it2=FP.begin() ; it2!=FP.end() ; ++it2){
if(it2->second < ZCD)continue;
for(int i=1 ; i<=(it2->first).length() ; ++i){
ans += Combination2(it2->first,0,i,"","",ZCD,MC);
}
}
cout << "--总共" << ans << "条--" << endl;
return 0;
}