Apriori算法是一个容易理解,逻辑简单,代码容易编写的一个大数据频繁项集查找的算法。
设最小支持度计数为3 即个数要大于等于3的才是频繁项
如图1--原始数据库 计数得到图2--每个东西的个数 则得到图3的频繁一项
图1 图2 图3
由频繁一项集两两配对得到候选项集合{A,B},{A,E},{B,E}
扫描原始数据库,即图1
{A,B}2个,{A,E}3个,{B,E}1个,所以得到频繁二项集{A,E}。由于此时二项集只剩下一个了,所以无法产生三项集,故程序结束。
假设现在有这样的频繁二项集{A,B},{B,C};那么两个频繁项中仅有一个事物不同,所以可以合并这两个得到候选三项{A,B,C}进而在扫描数据库判断是否是频繁三项。
假设现在有这样的频繁二项集{A,B},{C,D};那么两个频繁项中事物不同,所以不能合并出候选三项。
看如下图所示程序流程图:
图5(图片来源于网络,仅供学习参考)
C++代码:完整说明以及大数据测试数据可以从这里下载http://download.csdn.net/download/my_acm/8042909
本程序为了简单编程使用直接把原始数据读入内存中,所以不能处理非常大的数据,而正真的apriori算法处理超大规模数据时会一遍一遍的从数据库中读取数据。这也是为什么apriori算法效率不高的原因之一。
/*
function: apriori算法查找频繁集
authour:
date:
Code::Blocks 12.11下运行通过
*/
#include<vector>
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<map>
using namespace std;
#define maxn 100000
#define maxItem 90
#define suport 0.01 //最小支持度
struct Item{
vector <int> num;
};
typedef vector <int> i_list;
class Apriori{
public:
int suport_num; //最小支持度计数
int num_max; //单行最大数量
int num_sol; //交易条数
int num_fre; //频繁项种类的数量
Item item[maxn]; //初始交易信息
vector <i_list> fre_item[maxn]; //保存所有频繁项信息,fre_item[i]表示频繁i项信息
map <int,int> list_item; //标记某一个项目序号的总数
public:
void init(){
num_fre=0;
num_max=0;
suport_num=0;
num_sol=0;
}
void input(){ //输入,初始化交易信息
char str[3000];
int t=0;
while(gets(str)){ //按行逐个读入数据
char *f;
f=strtok(str," ");
while(f){
int x;
x=atoi(f);
list_item[x]++; //商品计数
item[t].num.push_back(x);
f=strtok(NULL," ");
}
if(item[t].num.size()>num_max)
num_max=item[t].num.size();
t++;
}
num_sol=t;
for(int i=0;i<num_sol;i++){
sort(item[i].num.begin(),item[i].num.end()); //交易项目序号从小到大排列
}
suport_num=ceil(suport*num_sol); //最小支持度计数
cout<<"数据总行数: "<<num_sol<<" 最小支持度"<<suport<<" 最小支持度计数: "<<suport_num<<endl;
// for(int i=0;i<num_sol;i++){
// int len=item[i].num.size();
// for(int j=0;j<len;j++){
// printf("%d ",item[i].num[j]);
// }
// printf("\n");
// }
}
void output(){ //输出频繁项集
if(!fre_item[1].size()){
printf("no frequent item!\n");
return ;
}
for(int k=1;k<=num_fre;k++){
printf("%d frequent item is:\n",k);
for(int i=0;i<fre_item[k].size();i++){
for(int j=0;j<fre_item[k][i].size();j++){
printf("%d ",fre_item[k][i][j]);
}
printf("\n");
}
printf("\n");
}
}
void LCS(i_list &tmp,i_list &t1,i_list &t2){//匹配最长公共子序列
int len=t1.size();
int sucess=1;
for(int i=0;i<len-1;i++){
if(t1[i]!=t2[i]){
sucess=0;
break;
}
}
if(t1[len-1]==t2[len-1])
sucess=0;
if(sucess){
tmp=t1;
tmp.push_back(t2[len-1]);
}
}
int judge(i_list tmp){ //判断tmp是否是频繁项
int len=tmp.size();
int sum=0;
int sucess=1;
for(int i=0;i<num_sol;i++){ //遍历所有原始数据
sucess=1;
if(item[i].num.size()<len)
continue;
for(int k=0;k<len;k++){
int j;
int tlen=item[i].num.size();
for(j=0;j<tlen;j++){
if(tmp[k]==item[i].num[j])
break;
}
if(j>=tlen){
sucess=0;
break;
}
}
if(sucess)
sum++;
}
if(sum>=suport_num) //大于等于最小支持度则是频繁项
return 1;
return 0; //不是频繁项
}
void find_fre_item(){ //找出所有频繁项
for(map <int,int>::iterator it=list_item.begin();it!=list_item.end();it++){ //找出所有的频繁一项
if(it->second>=suport_num){
i_list tmp;
tmp.push_back(it->first);
fre_item[1].push_back(tmp);
}
}
list_item.clear(); //释放lisg_item
if(0==fre_item[1].size())
return ;
if(1==fre_item[1].size()){
num_fre=1;
return ;
}
int len=num_max;
for(int k=2;k<=len;k++){ //找出所有二项以后的频繁项集
//枚举k-1项的所有单项的数据,进行LCS匹配,得到k频繁项
if(fre_item[k-1].size()>=2)
for(int i=0;i<fre_item[k-1].size();i++){
for(int j=i+1;j<fre_item[k-1].size();j++){
i_list tmp; tmp.clear();
LCS(tmp,fre_item[k-1][i],fre_item[k-1][j]);
if(!tmp.size())
continue;
if(judge(tmp)){
fre_item[k].push_back(tmp);
num_fre=k;
}
}
}
}
}
void calculate_fre_item(){ //频繁项计算
init();
input();
find_fre_item();
output();
}
}sol;
int main(){
freopen("retail.dat","r",stdin);
freopen("result.txt","w",stdout);
sol.calculate_fre_item();
return 0;
}