zoukankan      html  css  js  c++  java
  • Apriori算法的C++实现

    Apriori是经典的购物篮分析算法。该算法用SQL实现难度较大,所以考虑用C++实现。

    花了两天,代码例如以下。原创转载请注明出处

    //Apriori.c 
    #include<iostream>
    #include<set>
    #include<map>
    #include<string>
    #include<vector>
    using namespace std;
    typedef map<set<string>,int> map_s;
    
    map_s Ck ;//候选集Ck
    map_s Lk ; //频繁项集Lk
    vector<set<string> >  data; //原始数据
    vector<set<string> >  L;    //频繁项集合
    vector<string>        data2; //切割后的原始数据
    int n,m;
    int minSup = 2;
    int minConf = 0.2;
    set<string> s;
    string in;
    void Delete(map_s &Ck)
    {
       for(  map_s::iterator l_it=Ck.begin();l_it!=Ck.end();l_it++ )
       {
          if( l_it->second< minSup)
          {
            Ck.erase(l_it);
          }
       }
    }
    
    int compset(set<string> s1,set<string> s2)
    {
      int flag=0;
      //推断集合s1是不是s2的子集
      for( set<string>::iterator it=s1.begin(); it!=s1.end();it++ )
      {
         //s1有元素不在s2中
         if( s2.find(*it)==s2.end() )
         {
             flag=10;
             break;
         }
      }
      for( set<string>::iterator it=s2.begin(); it!=s2.end();it++ )
      {
         //s2有元素不在s1中
          if( s1.find(*it)==s1.end() )
          {
             flag+=1;               
             break;
          }
      }
      /*当flag==0,s1元素所有在s2中,s2元素也所有在s1中,s1==s2
        当flag==10,s1有元素不在s2中,s2所有元素都在s1中。s1包括了s2
        当flag==1,s1所有元素在s2中,s2有元素不在s1中。s2包括了s1
        当flag==11,s1 s2集合互不包括
      */
      return flag;
    }
    
    map_s apriori_gen(map_s &Ck,int k)
    {
       //生成子集
       map_s Ck_temp;
       set<string> s_temp;
       for( map_s::iterator l_it1=Ck.begin();l_it1!=Ck.end();l_it1++ )
       {
          for( map_s::iterator l_it2=Ck.begin();l_it2!=Ck.end();l_it2++ )
          {
             //假设两个set一样,则说明是同一个KEY。跳过
             if(!((l_it1->first > l_it2->first)||(l_it1->first < l_it2->first)))
                       continue;
             //否则開始组装,遍历整个Ck
             for( set<string>::iterator s_it=l_it2->first.begin();s_it!=l_it2->first.end();s_it++ )
             {
                   //假设该值在l_it1 set里面能够找到,不能组装
                   if( l_it1->first.find(*s_it)!=l_it1->first.end())
                      continue;
                   //否则进行组装,先把l_it1的set复制进去
                   s_temp = l_it1->first;
                   //再把l_it2的值放进去
                   s_temp.insert(*s_it);
                   //推断该组装的set是否已在生成集合中。假设之前已生成。则不须要往下运算
                   if(Ck_temp.find(s_temp)!=Ck_temp.end())
                        continue;
                   else  //否则放到生成的子集中
                   {
                      Ck_temp.insert(pair<set<string>,int >(s_temp,0));
                   }
             }
          }
       }
       
       //对于k=2的情况。须要扫描原始数据得出计数值
       if( 2 == k )
       {  
         for( map_s::iterator l_it=Ck_temp.begin();l_it!=Ck_temp.end();l_it++ ) 
          for(int i=0;i<data.size();i++)
           //l_it集合被data[i]完整包括,则计数值+1
           if( (10 == compset(data[i],l_it->first)) || (0 == compset(data[i],l_it->first))  )
                    Ck_temp[l_it->first]++;
        
           //扫描完之后排除 非频繁项
         for( map_s::iterator l_it=Ck_temp.begin();l_it!=Ck_temp.end();l_it++ )
             if( Ck_temp[l_it->first] < minSup )
                 Ck_temp.erase(l_it);
       }
       //假设是大于2的情况,扫描k-1的频繁项子集
       if( 2 < k )
       {
          //每次都循环获取每一个Ck的k-1子集元素
          //如{I1,I2,I3}C3的子集是{I1,I2} {I2,I3} {I3,I4}
          //假设Ck的子集不在k-1的频繁项子集中,则去掉该Ck项
          for( map_s::iterator l_it=Ck_temp.begin();l_it!=Ck_temp.end();l_it++ )
          {
             int flag;
             for( set<string>::iterator s_it=l_it->first.begin();s_it!=l_it->first.end();s_it++ ) 
             {
               //開始求子集
               //首先把当前Ck项的集合保存
               s_temp=l_it->first;
               //去掉一个元素。即是它的k-1子集
               s_temp.erase(*s_it);
               //遍历频繁项集合L。看看是不是在频繁集中
               flag=1;
               for( int i=0;i<L.size();i++  )
               {
                 //假设K-1子集在频繁项集中存在,则保留
                 if( 0 == compset(s_temp,L[i]) )
                 {
                      flag=0;
                      break;
                 }
               }
               //假设找到了哪怕一个k-1子集项不在频繁项集中。直接退出
               if( flag ) break;
             }
             //仅仅有所有的k-1子集在频繁项集中。才保留该Ck项
             if( flag ) Ck_temp.erase(l_it);
          }
       }
    
       cout<<"由L"<<k-1<<"产生的候选集C"<<k<<"   "<<"cout数(k=2以上不做计数)"<<endl;
       for( map_s::iterator l_it=Ck_temp.begin();l_it!=Ck_temp.end();l_it++ )
       {
            for( set<string>::iterator s_it=l_it->first.begin();s_it!=l_it->first.end();s_it++ )
                 cout<<*s_it<<"  ";
            cout<<l_it->second<<endl;
       }
       return Ck_temp;
    }
    
    int main()
    { 
      cout<<"请输入事务,第一行输入事务数。每行第一个值输入该事务的item数"<<endl;
      //生成原始数据集
      cin>>n;
      for(int i=0;i<n;i++)
      { 
         s.clear();
         cin>>m;
         for(int j=0;j<m;j++)
         {
            cin>>in;
            s.insert(in);
            data2.push_back(in);
         }
         data.push_back(s); 
      }
      //扫描数据集D。生成C1
         //对于每一个候选集里面的元素
         for( int j=0; j<data2.size();j++ )
         {
          int flag=1;
          //假设C1中存在该元素,计数值加1
          for( map_s::iterator l_it=Ck.begin();l_it!=Ck.end();l_it++ )
          {
            if( (l_it->first).find(data2[j]) != (l_it->first).end() )
            {
               Ck[l_it->first]++;
               flag=0;
               break;
            }
          }
         //不存在,插入到C1集合中
         if(flag)
         { 
              s.clear();
              s.insert(data2[j]);
              Ck.insert(pair<set<string>,int>(s,1));
         }
        }
        //去掉支持度不足的
        for(  map_s::iterator l_it=Ck.begin();l_it!=Ck.end();l_it++ )
        {
          if( l_it->second< minSup)
            Ck.erase(l_it);
        }
    
    
    
      cout<<"C1候选集:"<<endl;
      cout<<"项集"<<"     "<<"支持度计数"<<endl;
      
      for( map_s::iterator l_it=Ck.begin();l_it!=Ck.end();l_it++ )
      {
          for( set<string>::iterator s_it=(l_it->first).begin(); s_it!=(l_it->first).end(); s_it++)
             cout<<*s_it<<" "<<l_it->second<<endl;
      }
      
      int f_count=2;
      while( f_count )
      {
          //将Ck内的k-1频繁集所有保存到L集中
          for( map_s::iterator l_it=Ck.begin();l_it!=Ck.end();l_it++ )
            L.push_back(l_it->first);
          //获取Ck集,已清除掉小于支持度的候选集
          Ck=apriori_gen(Ck,f_count);     
          
          if( Ck.empty() )
          {
              break;
          }else{ f_count++; }
      }
     
      cout<<"终于的频繁集集合"<<endl;
      for( int i=0; i<L.size(); i++ )
      {
          for( set<string>::iterator s_it=L[i].begin(); s_it!=L[i].end(); s_it++)
             cout<<*s_it<<" ";
          cout<<endl;
      }
    
    }
    


    眼下仅仅实现到产生频繁集合,支持度默认计数2。在g++环境下编译 g++ -g Apriori.c -o apr  


    測试数据例如以下:

    4
    3 I1 I2 I6
    4 I1 I2 I3 I5
    3 I2 I3 I7
    5 I1 I3 I5 I6 I2 


    输出例如以下:



    用ORACLE格式化数据导出成文件,C++处理后返回文件给ORACLE,传到推荐数据库中就可以完毕这个算法的全流程。

    当然眼下还仅仅是儿童玩具,由于全部的数据都是放在内存里的,数据量一大这个代码就不有用了。

    C++牛逼的地方在于能够直接调用linux底层的接口处理这一类问题。比方当内存不足时数据存到磁盘。我们这边内存有几十G。订单总量不超过1G。所以这个代码应该够用了,不够用时能够再扩展



  • 相关阅读:
    Linux 文件权限
    Linux 查看磁盘使用情况
    绑定到外部验证服务LDAP、配置 autofs
    创建逻辑卷
    查找一个字符串
    查找用户目录下的指定文件
    配置NTP时间服务器
    通过Roslyn构建自己的C#脚本(更新版)(转)
    Elon Musk
    可能改变世界的13个“终结”(上)
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/7079379.html
Copyright © 2011-2022 走看看