#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <fstream>
#include <ctime>
#include <cstring>
#include <set>
using namespace std;
#define MAXSIZE 5000
double support; //最小支持度
int support_num; //最小支持数
int L1_support[MAXSIZE]; // 记录频繁一项集中各项的支持度
int pos[MAXSIZE]; // 记录频繁一项集中各项在头表中的行数
int max_col = 0; // 记录频繁项集中最大集合的项数
set<vector<int>> freqItemList; // 存放频繁项集
typedef vector<int>::iterator Vint_iter;
// FpTree 的节点
struct fpNode {
int id;
int count; // 该点出现次数
vector<fpNode*> children; // 孩子节点
fpNode* parent; // 父节点
fpNode* next; // 下一个相同 id 节点
fpNode() {
id = -1;
parent = next = nullptr;
count = 0;
}
};
// 排序规则
bool cmp(int a, int b) {
return L1_support[a] > L1_support[b];
}
// 读入项集
void read(vector<pair<vector<int>, int>>& origin_data,
vector<int>& l1, vector<fpNode*>& header_table)
{
ifstream ifs("D:\\文档\\数据挖掘\\实验\\合肥第一次实验要求\\data.txt", ios::in);
int rowNum = 0; //原始数据的行数
string line;
while (getline(ifs, line))
{
vector<int> items;
line += " ";
int temp_num = 0; //用来保存分割出来的数
for (int i = 0; line[i] != '\0'; i++) {
if (isdigit(line[i]))
{
temp_num *= 10; //从左往右的话 高位数先读 所以乘10再累加
temp_num += line[i] - '0'; //转换成数字
}
else {
if (isdigit(line[i - 1])) {
items.push_back(temp_num);
l1.push_back(temp_num);
L1_support[temp_num]++;
}
temp_num = 0; //置0
}
}
sort(items.begin(), items.end());
origin_data.push_back(make_pair(items, 1)); // 将项添加进项集
rowNum++;
}
support_num = ceil(rowNum * support);
ifs.close();
// 使用unique去重前的排序
sort(l1.begin(), l1.end());
l1.erase(unique(l1.begin(), l1.end()), l1.end());
sort(l1.begin(), l1.end(), cmp); // 按出现次数排序
// 过滤掉低于最小支持度的项
for (auto it = l1.begin(); it != l1.end(); it++)
{
if (L1_support[*it] < support_num)
{
l1.erase(it, l1.end());
// 已经按次数排序了 所以发现一个低于最小支持数的 就把后面的全删除即可
break;
}
}
int itemCount = l1.size();
for (int i = 0; i < itemCount; i++)
pos[l1[i]] = i;
header_table.resize(itemCount);
}
// 更新 FpTree
void updateFpTree(fpNode* inTree, vector<fpNode*>& header_table,
Vint_iter it, Vint_iter end, int count) {
if (it == end)
return; //空集
bool flag = false; //false:树中没有重复项
int len = inTree->children.size();
for (int i = 0; i < len; i++)
{
fpNode* child = inTree->children[i];
//该项已经存在于树中时
if (child->id == *it)
{
child->count += count;
//递归更新
updateFpTree(child, header_table, it + 1, end, count);
flag = true;
break;
}
}
// 如果树中没有重复项 那么直接将剩下的项全接到该节点上
if (!flag)
{
while (it != end)
{
fpNode* tmp = new fpNode();
tmp->id = *it;
tmp->parent = inTree;
tmp->count = count;
//指向下一个相同ID的节点
tmp->next = header_table[pos[*it]];
//更新该节点的最新位置
header_table[pos[*it]] = tmp;
//加入当前节点的孩子集合
inTree->children.push_back(tmp);
//指针下移
inTree = tmp;
it++;
}
}
}
// 建立 FpTree
void createFpTree(fpNode* fpTree,
vector<pair<vector<int>, int>>& origin_data,
vector<fpNode*>& header_table)
{
for (auto row : origin_data)
{
// 按支持度降序排序
stable_sort(row.first.begin(), row.first.end(), cmp);
// 过滤掉每行不频繁的项
for (auto it = row.first.begin(); it != row.first.end(); it++)
{
if (L1_support[*it] < support_num)
{
row.first.erase(it, row.first.end());
break;
}
}
updateFpTree(fpTree, header_table, row.first.begin(), row.first.end(), row.second);
}
}
// 获取项集
vector<int> getItemSet(fpNode* fp, vector<int> S)
{
if (fp->id == -1) //空节点直接返回
return S;
S.push_back(fp->id);
return getItemSet(fp->parent, S);
}
void fpGrowth(vector<fpNode*> header_table, vector<int> path)
{
for (int i = header_table.size() - 1; i >= 0; i--)
{
fpNode* fp = header_table[i];
vector<pair<vector<int>, int> > cond_pattern_base; // 条件模式基
vector<int> cond_item_set; //条件FP树包含的项
vector<fpNode*> cond_header_table; //条件FP树的头表
// 重置支持度和位置计数
memset(L1_support, 0, sizeof(L1_support));
memset(pos, -1, sizeof(pos));
while (fp != NULL)
{
vector<int> tmp;
//获得 以该节点为叶节点的FP子树 上的所有祖先
vector<int> condEvent = getItemSet(fp->parent, tmp);
sort(condEvent.begin(), condEvent.end());
//该条件模式基的支持度 为 该叶子节点的支持度
cond_pattern_base.push_back(make_pair(condEvent, fp->count));
for (auto item : condEvent)
{
//更新子树中每一项的支持度
L1_support[item] += fp->count;
cond_item_set.push_back(item);
}
fp = fp->next;
}
// 条件项集去重
sort(cond_item_set.begin(), cond_item_set.end());
cond_item_set.erase(unique(cond_item_set.begin(), cond_item_set.end()), cond_item_set.end());
// 并按支持度排序
sort(cond_item_set.begin(), cond_item_set.end(), cmp);
// 剔除支持度不够的项
for (auto it = cond_item_set.begin(); it != cond_item_set.end(); it++)
{
if (L1_support[*it] < support_num) {
cond_item_set.erase(it, cond_item_set.end());
break;
}
}
// 根据 该条件FP树的节点数 记录项的头表位置、设置条件头表的大小
int itemCount = cond_item_set.size();
for (int i = 0; i < itemCount; i++)
pos[cond_item_set[i]] = i;
cond_header_table.resize(itemCount);
// 建立条件 FpTree
fpNode* condFpTree = new fpNode();
createFpTree(condFpTree, cond_pattern_base, cond_header_table);
// newpath累计当前路径
vector<int> newPath = path;
newPath.push_back(header_table[i]->id);
sort(newPath.begin(), newPath.end());
if (newPath.size() > max_col)
max_col = newPath.size();
freqItemList.insert(newPath);
//项 与 条件FP树中保留的节点依次组合 形成频繁项集
for (auto item : cond_item_set)
{
vector<int> tmp = newPath;
tmp.push_back(item);
sort(tmp.begin(), tmp.end());
if (tmp.size() > max_col)
max_col = tmp.size();
freqItemList.insert(tmp);
}
// 若条件头表非空 则递归挖掘
if (cond_header_table.size())
{
fpGrowth(cond_header_table, newPath);
}
}// for
}
void showFreqItemSet()
{
for (int k = 1; k <= max_col; ++k)
{
printf("频繁%d项集:\n", k);
int count = 0;
for (auto i = freqItemList.begin(); i != freqItemList.end(); i++)
{
if ((*i).size() == k)
{
cout << "[";
for (int j = 0; j < (*i).size(); ++j)
if (k == 1)
printf(" %2d", (*i)[j]);
else
printf(" %d", (*i)[j]);
cout << " ] ";
++count;
}
if (count % 11 == 0 && count != 0)
{
count = 0;
cout << endl;
}
}
cout << endl;
}
printf("共%d项\n", freqItemList.size());
}
int main(void)
{
cout << endl;
cout << "-------------- FP-growth --------------" << endl << endl;
cout << "请输入最小支持度:";
cin >> support;
double startTime = clock();
vector<pair<vector<int>, int>> origin_data; // 项集
vector<int> l1; // 频繁一项集
vector<fpNode*> header_table; // 表头
fpNode* fpTree = new fpNode(); // FpTree
read(origin_data, l1, header_table); // 读入
printf("数据行数:%d 最小支持度:%.2lf 最小支持数:%d \n",
origin_data.size(), support, support_num);
createFpTree(fpTree, origin_data, header_table); // 建立 FpTree
//cout << "建树耗时:" << (clock() - startTime) / CLOCKS_PER_SEC << "s" << endl;
vector<int> path;
fpGrowth(header_table, path);
//cout << "总耗时:" << (clock() - startTime) / CLOCKS_PER_SEC << "s" << endl;
showFreqItemSet();
system("pause");
return 0;
}