重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
java内的Collections类自带了一个shuffle洗牌算法。
创新互联建站是一家专业提供嘉善企业网站建设,专注与成都网站设计、成都做网站、外贸网站建设、HTML5、小程序制作等业务。10年已为嘉善众多企业、政府机构等服务。创新互联专业网站建设公司优惠进行中。
static void shuffle(List? list)
使用默认随机源对指定列表进行置换。
static void shuffle(List? list, Random rnd)
使用指定的随机源对指定列表进行置换。
现在你可以把52张牌放进一个List里,调用他的shuffle算法打乱顺序。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
/**
文件名:FileEncrypter.java
JDK:1.40以上
说明:文件加密
加密方法:三重DES加密
加密过程:对选中的文件加密后在同文件夹下生成一个增加了".tdes"
扩展名的加密文件
解密过程:对选中的加密文件(必须有".tdes"扩展名)进行解密
*/
public class FileEncrypter extends JFrame{
public static final int WIDTH = 550;
public static final int HEIGHT = 200;
public static void main(String args[]) {
FileEncrypter fe = new FileEncrypter();
fe.show();
}
FileEncrypter(){
this.setSize(WIDTH,HEIGHT);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension screenSize = tk.getScreenSize();
this.setLocation((screenSize.width - WIDTH)/2,
(screenSize.height - HEIGHT)/2);
this.setTitle("文件加密器(TriDES)");
Container c = this.getContentPane();
c.setLayout( new FlowLayout());
final FilePanel fp = new FilePanel("文件选择");
c.add(fp);
final KeyPanel pp = new KeyPanel("密码");
c.add(pp);
JButton jbE = new JButton("加密");
c.add(jbE);
jbE.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
File file = new File(fp.getFileName());
if (file.exists())
encrypt(file.getAbsoluteFile(),pp.getKey());
else
JOptionPane.showMessageDialog(
null,"请选择文件!","提示",JOptionPane.OK_OPTION);
}
});
JButton jbD = new JButton("解密");
c.add(jbD);
jbD.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
File file = new File(fp.getFileName());
if (file.exists())
decrypt(file.getAbsoluteFile(),pp.getKey());
else
JOptionPane.showMessageDialog(
null,"请选择文件!","提示",JOptionPane.OK_OPTION);
}
});
}
/**
加密函数
输入:
要加密的文件,密码(由0-F组成,共48个字符,表示3个8位的密码)如:
AD67EA2F3BE6E5ADD368DFE03120B5DF92A8FD8FEC2F0746
其中:
AD67EA2F3BE6E5AD DES密码一
D368DFE03120B5DF DES密码二
92A8FD8FEC2F0746 DES密码三
输出:
对输入的文件加密后,保存到同一文件夹下增加了".tdes"扩展名的文件中。
*/
private void encrypt(File fileIn,String sKey){
try{
if(sKey.length() == 48){
byte[] bytK1 = getKeyByStr(sKey.substring(0,16));
byte[] bytK2 = getKeyByStr(sKey.substring(16,32));
byte[] bytK3 = getKeyByStr(sKey.substring(32,48));
FileInputStream fis = new FileInputStream(fileIn);
byte[] bytIn = new byte[(int)fileIn.length()];
for(int i = 0;iFILEIN.LENGTH();I++){
bytIn[i] = (byte)fis.read();
}
//加密
byte[] bytOut = encryptByDES(encryptByDES(
encryptByDES(bytIn,bytK1),bytK2),bytK3);
String fileOut = fileIn.getPath() + ".tdes";
FileOutputStream fos = new FileOutputStream(fileOut);
for(int i = 0;iBYTOUT.LENGTH;I++){
fos.write((int)bytOut[i]);
}
fos.close();
JOptionPane.showMessageDialog(
this,"加密成功!","提示",JOptionPane.OK_OPTION);
}else
JOptionPane.showMessageDialog(
this,"密码长度必须等于48!","错误信息",JOptionPane.ERROR_MESSAGE);
}catch(Exception e){
e.printStackTrace();
}
}
/**
解密函数
输入:
要解密的文件,密码(由0-F组成,共48个字符,表示3个8位的密码)如:
AD67EA2F3BE6E5ADD368DFE03120B5DF92A8FD8FEC2F0746
其中:
AD67EA2F3BE6E5AD DES密码一
D368DFE03120B5DF DES密码二
92A8FD8FEC2F0746 DES密码三
输出:
对输入的文件解密后,保存到用户指定的文件中。
*/
private void decrypt(File fileIn,String sKey){
try{
if(sKey.length() == 48){
String strPath = fileIn.getPath();
if(strPath.substring(strPath.length()-5).toLowerCase().equals(".tdes"))
strPath = strPath.substring(0,strPath.length()-5);
else{
JOptionPane.showMessageDialog(
this,"不是合法的加密文件!","提示",JOptionPane.OK_OPTION);
return;
}
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
chooser.setSelectedFile(new File(strPath));
//用户指定要保存的文件
int ret = chooser.showSaveDialog(this);
if(ret==JFileChooser.APPROVE_OPTION){
byte[] bytK1 = getKeyByStr(sKey.substring(0,16));
byte[] bytK2 = getKeyByStr(sKey.substring(16,32));
byte[] bytK3 = getKeyByStr(sKey.substring(32,48));
FileInputStream fis = new FileInputStream(fileIn);
byte[] bytIn = new byte[(int)fileIn.length()];
for(int i = 0;iFILEIN.LENGTH();I++){
bytIn[i] = (byte)fis.read();
}
//解密
byte[] bytOut = decryptByDES(decryptByDES(
decryptByDES(bytIn,bytK3),bytK2),bytK1);
File fileOut = chooser.getSelectedFile();
fileOut.createNewFile();
FileOutputStream fos = new FileOutputStream(fileOut);
for(int i = 0;iBYTOUT.LENGTH;I++){
fos.write((int)bytOut[i]);
}
fos.close();
JOptionPane.showMessageDialog(
this,"解密成功!","提示",JOptionPane.OK_OPTION);
}
}else
JOptionPane.showMessageDialog(
this,"密码长度必须等于48!","错误信息",JOptionPane.ERROR_MESSAGE);
}catch(Exception e){
JOptionPane.showMessageDialog(
this,"解密失败,请核对密码!","提示",JOptionPane.OK_OPTION);
}
}
/**
用DES方法加密输入的字节
bytKey需为8字节长,是加密的密码
*/
private byte[] encryptByDES(byte[] bytP,byte[] bytKey) throws Exception{
DESKeySpec desKS = new DESKeySpec(bytKey);
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey sk = skf.generateSecret(desKS);
Cipher cip = Cipher.getInstance("DES");
cip.init(Cipher.ENCRYPT_MODE,sk);
return cip.doFinal(bytP);
}
/**
用DES方法解密输入的字节
bytKey需为8字节长,是解密的密码
*/
private byte[] decryptByDES(byte[] bytE,byte[] bytKey) throws Exception{
DESKeySpec desKS = new DESKeySpec(bytKey);
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey sk = skf.generateSecret(desKS);
Cipher cip = Cipher.getInstance("DES");
cip.init(Cipher.DECRYPT_MODE,sk);
return cip.doFinal(bytE);
}
/**
输入密码的字符形式,返回字节数组形式。
如输入字符串:AD67EA2F3BE6E5AD
返回字节数组:{173,103,234,47,59,230,229,173}
*/
private byte[] getKeyByStr(String str){
byte[] bRet = new byte[str.length()/2];
for(int i=0;iSTR.LENGTH()
Integer itg =
new Integer(16*getChrInt(str.charAt(2*i)) + getChrInt(str.charAt(2*i+1)));
bRet[i] = itg.byteValue();
}
return bRet;
}
/**
计算一个16进制字符的10进制值
输入:0-F
*/
private int getChrInt(char chr){
int iRet=0;
if(chr=="0".charAt(0)) iRet = 0;
if(chr=="1".charAt(0)) iRet = 1;
if(chr=="2".charAt(0)) iRet = 2;
if(chr=="3".charAt(0)) iRet = 3;
if(chr=="4".charAt(0)) iRet = 4;
if(chr=="5".charAt(0)) iRet = 5;
if(chr=="6".charAt(0)) iRet = 6;
if(chr=="7".charAt(0)) iRet = 7;
if(chr=="8".charAt(0)) iRet = 8;
if(chr=="9".charAt(0)) iRet = 9;
if(chr=="A".charAt(0)) iRet = 10;
if(chr=="B".charAt(0)) iRet = 11;
if(chr=="C".charAt(0)) iRet = 12;
if(chr=="D".charAt(0)) iRet = 13;
if(chr=="E".charAt(0)) iRet = 14;
if(chr=="F".charAt(0)) iRet = 15;
return iRet;
}
}
/**
文件选择组件。
*/
class FilePanel extends JPanel{
FilePanel(String str){
JLabel label = new JLabel(str);
JTextField fileText = new JTextField(35);
JButton chooseButton = new JButton("浏览...");
this.add(label);
this.add(fileText);
this.add(chooseButton);
clickAction ca = new clickAction(this);
chooseButton.addActionListener(ca);
}
public String getFileName(){
JTextField jtf = (JTextField)this.getComponent(1);
return jtf.getText();
}
private class clickAction implements ActionListener{
clickAction(Component c){
cmpt = c;
}
public void actionPerformed(ActionEvent event){
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
int ret = chooser.showOpenDialog(cmpt);
if(ret==JFileChooser.APPROVE_OPTION){
JPanel jp = (JPanel)cmpt;
JTextField jtf = (JTextField)jp.getComponent(1);
jtf.setText(chooser.getSelectedFile().getPath());
}
}
private Component cmpt;
}
}
/**
密码生成组件。
*/
class KeyPanel extends JPanel{
KeyPanel(String str){
JLabel label = new JLabel(str);
JTextField fileText = new JTextField(35);
JButton chooseButton = new JButton("随机产生");
this.add(label);
this.add(fileText);
this.add(chooseButton);
clickAction ca = new clickAction(this);
chooseButton.addActionListener(ca);
}
//返回生成的密码(48个字符长度)
public String getKey(){
JTextField jtf = (JTextField)this.getComponent(1);
return jtf.getText();
}
private class clickAction implements ActionListener{
clickAction(Component c){
cmpt = c;
}
public void actionPerformed(ActionEvent event){
try{
KeyGenerator kg = KeyGenerator.getInstance("DES");
kg.init(56);
Key ke = kg.generateKey();
byte[] bytK1 = ke.getEncoded();
ke = kg.generateKey();
byte[] bytK2 = ke.getEncoded();
ke = kg.generateKey();
byte[] bytK3 = ke.getEncoded();
JPanel jp = (JPanel)cmpt;
JTextField jtf = (JTextField)jp.getComponent(1);
jtf.setText(getByteStr(bytK1)+getByteStr(bytK2)+getByteStr(bytK3));
}catch(Exception e){
e.printStackTrace();
}
}
private String getByteStr(byte[] byt){
String strRet = "";
for(int i=0;iBYT.LENGTH;I++){
//System.out.println(byt[i]);
strRet += getHexValue((byt[i]240)/16);
strRet += getHexValue(byt[i]15);
}
return strRet;
}
private String getHexValue(int s){
String sRet=null;
switch (s){
case 0: sRet = "0";break;
case 1: sRet = "1";break;
case 2: sRet = "2";break;
case 3: sRet = "3";break;
case 4: sRet = "4";break;
case 5: sRet = "5";break;
case 6: sRet = "6";break;
case 7: sRet = "7";break;
case 8: sRet = "8";break;
case 9: sRet = "9";break;
case 10: sRet = "A";break;
case 11: sRet = "B";break;
case 12: sRet = "C";break;
case 13: sRet = "D";break;
case 14: sRet = "E";break;
case 15: sRet = "F";
}
return sRet;
}
private Component cmpt;
}
}
挖掘频繁模式前首先要构造FP-Tree,算法为码如下:
输入:一个交易数据库DB和一个最小支持度threshold.
输出:它的FP-tree.
步骤:
1.扫描数据库DB一遍.得到频繁项的集合F和每个频繁项的支持度.把F按支持度递降排序,结果记为L.
2.创建FP-tree的根节点,记为T,并且标记为’null’.然后对DB中的每个事务Trans做如下的步骤.
根据L中的顺序,选出并排序Trans中的事务项.把Trans中排好序的事务项列表记为[p|P],其中p是第一个元素,P是列表的剩余部分.调用insert_tree([p|P],T).
函数insert_tree([p|P],T)的运行如下.
如果T有一个子结点N,其中N.item-name=p.item-name,则将N的count域值增加1;否则,创建一个新节点N,使它的count为1,使它的父节点为T,并且使它的node_link和那些具有相同item_name域串起来.如果P非空,则递归调用insert_tree(P,N).
注:构造FP-Tree的算法理解上相对简单,所以不过多描述 对FP-Tree进行挖掘,算法如下:
输入:一棵用算法一建立的树Tree
输出:所有的频繁集
步骤:
调用FP-growth(Tree,null).
procedure FP-Growth ( Tree, x)
{
(1)if (Tree只包含单路径P) then
(2) 对路径P中节点的每个组合(记为B)
(3) 生成模式B并x,支持数=B中所有节点的最小支持度
(4) else 对Tree头上的每个ai,do
{
(5) 生成模式B= ai 并 x,支持度=ai.support;
(6) 构造B的条件模式库和B的条件FP树TreeB;
(7)if TreeB != 空集
(8)then call FP-Growth ( TreeB , B )
}
}
#includestdio.h
#includestdlib.h
#includestring.h
#include"malloc.h"
#define LEN 20
#define L_size 100
typedef struct Nodeone
{ char a[2];
struct Nodeone *next;
}Nodeone,*TNodeone;
struct
{
char rA[20];
char ra[20];
}rule[20];
struct
{
char mA[20];
char ma[20];
}mean[20];
/////////////////////////////////////////////
//栈的操作//////
typedef struct node
{
char data;
struct node * next;
}LinkStackNode,* LinkStack;
void InitStack(LinkStack * top)
{
(* top)=(LinkStack)malloc(sizeof(LinkStackNode));
(* top)-next=NULL;
}
//用头插法建栈
void Push(LinkStack top,char a)
{
LinkStackNode * temp;
temp=(LinkStackNode *)malloc(sizeof(LinkStackNode));
temp-data=a;
temp-next=top-next;
top-next=temp;
}
//删除栈顶元素
void Pop(LinkStack top,char *x)
{
LinkStackNode * temp;
temp=top-next;
top-next=temp-next;
*x=temp-data;
free(temp);
}
//////////////////////////////////////////////////////////////
////队列的操作////
typedef struct Node
{
char data;
struct Node * next;
}LinkQueueNode;
typedef struct
{
LinkQueueNode * front;
LinkQueueNode * rear;
}LinkQueue;
//链队列初始化
void InitQueue(LinkQueue *Q)
{
Q-front=(LinkQueueNode *)malloc(sizeof(LinkQueueNode));
if(Q-front!=NULL)
{
Q-rear=Q-front;
Q-front-next=NULL;
}
}
//链队列入队操作
void EnterQueue(LinkQueue *Q,char x)
{
LinkQueueNode * NewNode;
NewNode=(LinkQueueNode *)malloc(sizeof(LinkQueueNode));
if(NewNode!=NULL)
{
NewNode-data=x;
NewNode-next=NULL;
Q-rear-next=NewNode;
Q-rear=NewNode;
}
}
//链队列出队操作
void DeleteQueue(LinkQueue *Q,char *x)
{
LinkQueueNode * p;
p=Q-front-next;
Q-front-next=p-next;
if(Q-rear==p)
Q-rear=Q-front;
*x=p-data;
free(p);
}
//读取文件
void read_rulefile()
{
FILE * fp;
char filename[LEN];
int i=0;
printf("请输入要打开规则文件的完整路径及其文件名:\n"); //1. 读取文本文件(rule.txt), 将具体规则的对应 关系读入数组或链表中
gets(filename);
fp=fopen(filename,"rt");
if(fp==NULL)
{
printf("\n打开文件失败,%s文件可能不存在\n",filename);
return ;
}
while(fscanf(fp,"%s %s",rule[i].rA,rule[i].ra)!=EOF)
{
printf("%s %s\n",rule[i].rA,rule[i].ra);
i++;
}
return ;
}
void read_meanfile()
{
FILE * fp;
char filename[LEN];
int i=0;
printf("请输入要打开mean文件的完整路径及其文件名:\n"); //2. 读取文本文件(mean.txt), 将小写字母及其 对应的含义读入数组或链表中
gets(filename);
fp=fopen(filename,"rt");
if(fp==NULL)
{
printf("\n打开文件失败,%s文件可能不存在\n",filename);
return ;
}
while(fscanf(fp,"%s %s",mean[i].mA,mean[i].ma)!=EOF)
{
printf("%s %s\n",mean[i].mA,mean[i].ma);
i++;
}
return ;
}
///////////////////////////////////////////////////////////////////////
//思想:当没遇到闭括号时,一直压栈(top栈)。一旦遇到闭括号,首先找到与这个闭括号最近的匹配的开括号
//找到这两个括号“(” “)”之间的第一个 字符
//利用队列(Q 入队)以及(top栈 出栈)完成转换,再把转换后的队列,添加到(top栈中)把剩余的字符 原样添加到top栈中
//在把top栈 逆置到 top1栈中
//再利用top1栈出栈,给L重新赋值。
//递归,看L中是否还有括号
void tackle_2(LinkStackNode * top,LinkStackNode * top1,LinkQueue * Q,char L[])
{
int i=0,j;
char * a;
char first;
a=(char *)malloc(sizeof(char));
InitStack(top);
InitQueue(Q);//辅助用来输出括号内的内容
InitStack(top1);
while(L[i]!=')'L[i]!='\0')
{
Push(top,L[i]);//当不等于‘)’时,往top栈中压。
i++;
}
if(L[i]==')') //当等于')'时
{
j=i;
while(L[j]!='(')
{ j--;
if(L[j]=='(')
{
j++;break;
}
}
first=L[j];//找到当前‘( ’内的第一个字符
for(;ji-1;j++)
{
EnterQueue(Q,first);
Pop(top,a);
EnterQueue(Q,*a);
}
Pop(top,a); //这个是括号内的第一个字符
EnterQueue(Q,*a);
Pop(top,a);//把‘(’删掉
while(Q-front-next!=NULL)
{
DeleteQueue(Q,a);
Push(top,*a);
}
}// if
i++;//跳过‘)’
while(L[i]!=NULL)
{ Push(top,L[i]);
i++;
}
while(top-next!=NULL)
{
Pop(top,a);
Push(top1,*a);
}
i=0;
while(top1-next!=NULL)
{
Pop(top1,a);
L[i]=*a;
i++;
}
L[i]=NULL;
i=0;j=0;
while(L[i]!=NULL)
{
i++;
if(L[i]=='(' || L[i]==')')
j++;
}
if(j==0) return;
else tackle_2(top,top1,Q,L);
}
//////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
//实现提示: 处理规则形式(1) 问题并没有规定具体规则的数量,
//比如增加 一条具体规则 C→eB 那么对魔王语言就要多处理一次,
//因此处理 规则形式(1)时需要递归处理,当语言中没有 大写字母后递归结束。
void tackle_1(char L[],LinkQueue * Q)
{
int i=0,j,z;
int k=0;
char * a;
a=(char *)malloc(sizeof(char));
while(L[i]!=NULL)
{ z=0;
for(j=0;j20;j++)
{
if(L[i]==rule[j].rA[0])
{ k=0;
z=1;
while(rule[j].ra[k]!=NULL)
{
EnterQueue(Q,rule[j].ra[k]);
k++;
}
}
}
if(z==1)
i++;
else
{
EnterQueue(Q,L[i]);
i++;
}
}
i=0;
while(Q-front-next!=NULL)
{
DeleteQueue(Q,a);
L[i]=(*a);
i++;
}
L[i]=NULL;
i=0; j=0;
while(L[i]!=NULL)
{
if(L[i]='A' L[i]='Z')
j++;
i++;
}
if(j==0)return;
else tackle_1(L,Q);
}
TNodeone tackle_12(char L[])
{ TNodeone head,r,s;
int i=0;
int j;
head=(Nodeone*)malloc(sizeof(Nodeone));
head-next=NULL;
r=head;
while(L[i]!=NULL)
{
for(j=0;j20;j++)
{
if(L[i]==mean[j].mA[0])
printf("%s",mean[j].ma);
s=(Nodeone*)malloc(sizeof(Nodeone));
strcpy(s-a,mean[j].ma);
r-next=s;
r=s;
}
i++;
}
r-next=NULL;
return head;
}
void savefile(TNodeone head)
{ FILE *fp;
TNodeone h;
h=head-next;
fp=fopen("c:/result.txt","wb");
if(fp==NULL)
{ printf("读文件失败,按任意键退出!");
getchar();
exit(1);
}
for(;h;h=h-next)
fprintf(fp,"%s",h-a);
fclose(fp);
printf("\n");
printf("保存文件成功!");
}
void main()
{ TNodeone head;
read_rulefile();
read_meanfile();
char L[L_size]; //魔王说的话
printf("请输入魔王语言:\n");
scanf("%s",L);
LinkStackNode * top, * top1;
top=(LinkStackNode * )malloc(sizeof(LinkStackNode));
top1=(LinkStackNode * )malloc(sizeof(LinkStackNode));
LinkQueue *Q;
Q=(LinkQueue *)malloc(sizeof(LinkQueue));
tackle_2(top,top1,Q,L);
printf("魔王要说的话是:%s\n",L);
tackle_1(L,Q);
printf("魔王要说的话是(转换成小写后):%s\n",L);
head=tackle_12(L);
savefile(head);
}
FP—tree由标记为“null”的一个根节点(root)、作为根节点之子节点的项前缀子树(item prefix subtrees)的集合、和—个频繁项头表(frequent item header table)所组成。
项前缀子树中的每个节点包含3个域:item_name,count和node_link。item _name记录了该节点所代表的项的名字。count记录了所在路径代表的事务中达到此节点的事务个数。 node_link指向下一个具有同样的item_name域的节点,如果没有这样一个节点,则其值被置为null。
频繁项头表包含两个域:Item_name和head of node_link. head of node_link指向FP—tree中具有相同Item_name的第一个节点。
根据FP_tree 的定义,我们得到FP_tree的构造方法
2 FP—tree的生成
FP—tree是通过两次扫描事务集建立的.
(1)扫描事务集D,获得D中所包含的全部频繁项集合F,及它们各自的支持度。对F中的频繁项按其支持度降序排序,结果记为L;
(2)创建FP—tree的根节点T,以“null”标(3) 记;然后对D中的每个事务Trans进行如下操作:根据L中的顺序,选出并排序Trans的频繁项.设排序后的频繁项表为[x|P],其中x是第一个频繁项。而P是剩余的频繁项:调用insert—tree([x|P],T):insert—tree([x|P],T)过程执行情况如下:如果T有子女N并使得N.item_name=x.item_name,则N的计数增加1;否则创建一个新节点N,将其计数设置为1,链接到它的父节点T、、并且通过节点链结构将其链接到具有相同item_name的节点;如果P非空,递归地调用INSERT—tree(P,N)。在事务集再次扫描完毕时,一个完全的FP—tree就建成了。
3 频繁模式的挖掘
FP—tree被建立后,频繁模式是采用下面的FP—growth方法对FP—tree进行挖掘得到的. Procedure FP_growth(Tree,α)//tree为α的条件模式树,α为后缀项(集)
{ if Tree 仅含一条路径P then
for 路径P中节点的每个组合(记作?) do
产生模式?Uα、并且把它的支持度supcoun指定为?中节点的最小支持度
else for 对Tree的头表从表尾到表头的每一个表项(记为α1)do {
产生一个模式?=α1Uα,其支持度计数supcount=α1.supcount:
创建?的条件模式基,然后构造?的条件模式树FP—tree? ;
if FP—tree ?= !Φ then 调用FP_growth(FP—tree?、?)}
从算法2、3中,我们可以看出FP—growth挖掘过程是一种分而治之的过程。而且,即使数据库可能产生指数级大量的频繁模式,FP—tree的规模还是很小,不会呈指数级增长。例如,对于一个长度为100的频繁模式“a1,…,a100”,FP—tree只会生成唯一一条长度为100的路径,如“a1a2…a100”。而且,FP—growth算法可以导出所有的大约1030个频繁模式(如果时间允许的话),如“a1, a2,..,a1a2,…,a1a2a3:,…,a1…a100”。
3.程序实现
程序在定义数据结构和实现算法的时候主要考虑一下因素。
首先速度。速度问题在频繁集产生的算法中应该是比较重要的.因为它们常常涉及大量数据的处理.因此在程序中许多需要排序的数据结构都使用了平衡树,这是一种高效的对全序元素的组织方式。
其次,空间。同样因为要处理大量的数据,所以对内存的管理要尤其严格,如果出现内存泄漏将是很麻烦的事情。
第三,编程风格的考虑.要尽量采用通用化的代码.因为对于一个比较大的系统来说,任何一个小的部分都应该清楚明白,便于别人阅读和修改。
基于上面三点,在对程序的数据结构的定义和算法的实现的时候大量采用了C++的标准模板库(STL,Standard Template Library).这是基于以下的事实,关联规则的挖掘所涉及到的数据结构和基本算法都是以往的常用数据结构和算法,如向量类,集合类,快速排序算法等.而STL正是包含了这样许多通用的数据结构和基本算法的库。
例如,在STL中有一大类模板叫容器模板(container),简单的说就是包含了许多元素的类的模板,象vector,array,set等就属于容器模板.对于这些模板中的元素的访问都可以通过它们的遍历子,用完全一致的方式进行.请看下面两段程序清单:
void print(FreqSet* pSet){
FreqSet_Iter fit;
for(fit=pSet-begin();fit!=pSet-end();fit++){
printf("%s %d \n",(*fit).second.name,(*fit).second.count);
}
}
void print(Table* pTable){
Table_Iter tit;
for(tit=pTable-begin();tit!=pTable-end();tit++){
printf("%s %d \n",(*tit).name,(*tit).count);
}
}
这两个函数的作用是在调试的时候分别打印出频繁集pSet和头表pTable中的元素。
尽管pSet的类FreqSet是由map模板生成的,pTable的类Table是由multiset生成的,但对它们的元素的访问形式几乎一模一样都是利用相应的遍历子.涉及的函数名也很相似.其实,所有的容器模板都定义了函数begin()和end()代表元素列表的起始和结束.另外还有大量的具有相同名字和类似功能的函数,方便了我们对数据结构的管理。
在选择数据库访问方式的时候,用了最近比较流行的ADO方式,它比ODBC更加灵活,速度也有改善.对网络数据库的支持也很好。
对应于算法描述中所出现的各个对象分别定义了一些数据结构,仅以trans为例说明
class Trans:public std::vectorItem
{
public:
int TID;
Trans(){TID=0;};
Trans(int t){
TID=t;
}
bool operator==(const Trans right);
bool operator(const Trans right);
}
typedef Trans::iterator Trans_Iter;
Trans类对应于交易对象(Transaction).对它的定义利用了STL中的vector模板,把它定义为一个以Item为元素的向量.把它定义为向量,是因为后来需要对其中的元素进行排序,向量可以使用快速排序。
这个Trans 类身肩两任,它不仅代表事务对象,还在最后的结果输出中还被用来存放频繁集。
Trans_Iter是Trans类的遍历子.在以后使用STL容器模板定义的类都会有一个与之相对应的遍历子,不再赘述。
作者根据算法用 Vsual C++编写了程序,程序己在机器上调试通过(运行环境为: Windows Xp、Vsual C++ 6.0).为节约篇幅,并且方便用户阅读,作者列出了部分关键程序清单
//根据条件模式库来生成用于构建条件树的频繁项
void FPTree::BuildCPTree(long p_Frequnce)
{
std::vectorstd::vectorassociate ::iterator it_CPBase=m_CPBase.begin();
std::vectorassociate::iterator it_vector,it_temp1;
std::mapstd::string,int Item_include;//包含非频繁的项的集合
std::mapstd::string,int::iterator it_map,it_temp;
std::string l_string;
long Frequnce=p_Frequnce;
bool flag=true;
int l_test;
//统计每个项目的频繁度
for (;it_CPBase!=m_CPBase.end();it_CPBase++)
{
it_vector=(*it_CPBase).begin();
while (it_vector!=(*it_CPBase).end())
{
l_string=(*it_vector).chr;
l_test=(*it_vector).num;
if (Item_include.count(l_string))
{
Item_include[l_string]+=(*it_vector).num;
l_test=Item_include[l_string];
}
else
Item_include[l_string]=(*it_vector).num;
it_vector++;
}
}
//除去map中的非频繁项
bool flag1=true;
it_map=Item_include.begin();
while ((it_map!=Item_include.end()) flag1)
{
l_string=it_map-first;
l_test=it_map-second;
if (it_map-second Frequnce)
{
it_temp=it_map;
it_map++;
Item_include.erase(it_temp);
if (it_map==Item_include.end())
flag1=false; }
else
it_map++;
}
if (!Item_include.empty())
{