DM&ML_note.2.2-C4.5决策树

这个学期要学DM&ML,用的是《数据挖掘算法原理与实现》王振武 本着造福同学的思想,开一个DM&ML的笔记系列,打算给书上的源代码添加一点注释,方便阅读和理解。


写在前面:

提示:本博文不适合(未满18岁||码龄<1年||编码量<5K行||年均编码量2K行)的读者,
页面可能包含轻度或中度的吐槽,嘲讽,以及大量的不规范编码以及错误的编程设计等内容;
阅读时有可能产生轻微不适感;
请确信自己已满当地法律许可年龄且心智成熟后再来阅览;

警告:

《数据挖掘算法原理与实现》书中提供的C4.5源码,存在大量的浮点数判断错误,log函数设计错误,内存泄漏,编程设计等错误。切勿模仿。

典型错误举例分析:

  1. 浮点数判断错误:
    if((double) n==0)诸如此类的浮点数相等/大小判断,由于浮点数存储的设计决定了会存在误差,所以应该引入误差处理,或者认为设定误差精度。
    比如:

    if(fabs(1/2)-0.5<FLT_EPSILON)
    或者
    if(fabs(1/2)-0.5<0.00001/*精确到0.00001*/)。
  2. 本算法在计算信息熵的时候需要计算2为底的log值,由于math.h并不提供这样的函数,需要像ID3的实现那样使用换底公式,本实现一概没有使用。

  3. 内存泄漏:全文没有任何delete

  4. 程序设计上的错误:详情看注释
  5. 如果看到这里您并不打算浪费时间在如下地方:纠正代码错误,看懂不知所云的奇怪编程风格,纠结于递归出口,被代码误导日后编程,欢迎点击本页面的右上角查看其他关于C4.5的资料,并跳过本书的本部分教学内容。

前置知识要求:

C++,STL,树,深度优先搜索(DFS)
一点点数学(换底公式)

具体实现:

/*hiro:修改了头文件*/
/*hiro:为何要用缩进来伤害我!*/
#include <iostream>
#include <fstream>/*hiro:添加了用于文本IO的头文件*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
// using namespace std;
/*hiro:本来我可以用全局的using namesapce std的,但是考虑到
他好像写了个sort,我怕引起各方面的命名冲突,于是显式使用了明明空间
std::cout,std::endl,下面不再重复描述。*/

const int A = 4;//data中条件属性的个数
const int C = 3;//data中类的个数
const int ON = 150;//ordata中样本的个数
const int N = 120;//测试集中样本的个数
const int MAX = 200;//条件属性取值的最大值
const int CX = 2;//运行20次求平均值




double ordata[ON][A + 2];/*hiro:样例数据,更改为文本输入*/
double data[N][A + 2];//训练集
double test[ON - N][A + 2];//测试集
double rule[N][A + 2];//规则集
int bb = 0;//规则集中规则的个数
int attnum[A];//各个属性取值的个数
//int iii,jjj;
/***********统计各属性取值的个数************/
//初始化
/*
for(iii=0;iii<A;iii++)
{
attnum[iii]=1;
sort(&ordata[0][0],ON,iii+1);
for(jjj=1;jjj<ON;jjj++)
{
if(ordata[jjj][iii+1]==ordata[jjj-1][iii+1]) continue;
else attnum[iii]++;
}
std::cout<<attnum[iii]<<std::endl;
}
*/

struct node
{
    int leaf;//标志,叶子为1,否则为中间节点,取值为0
    double cla;//如果是叶子节点,表示决策类的值
    int att[A];//已确定的条件属性,1为已确定,0为未确定
    double attvalue[A];//已确定的条件属性的取值,初始值为-1
    int i;//当前进行分支的条件属性号(0~A-1)
    double nextvalue[MAX];//当前条件属性的取值
    struct node *next[MAX];//指向前条件属性的取值的下一级

}*Tree;

int noden = 0;/*hiro:结点个数*/

void Input();/*hiro:添加的文本IO的函数*/

void out(double *a, int n);//输出数组a,它有n行,A+2列
void sort(double *a, int num, int n);//对长为num的a数组根据第n列进行排序
struct node * root();//生成根节点
double gainratio(double *d, int n, int a);//求有n个样本的数据集d的属性a的信息增益率
struct node * copynode(struct node *a, struct node *b);//拷贝a节点的数据到b节点
int isleaf(struct node *p);//判断节点p是否为叶子节点
void nextnode(struct node *p);//根据当前节点生成孩子节点,返回值为0表示都是叶子节点,为1表示还存在孩子节点
void outnode(struct node *p);//输出节点p
void outTree(struct node *p);//输出树,p为根,
int nodemaxi(struct node *p);//求节点p的i

//函数声明;

void main()
{
    Input();/*hiro:添加的文本IO*/
    int i, j, k;
    int bz1, bz2;
    int reco[CX];//能正确识别的样本个数
    int cannotreco[CX];//拒绝识别
    int recoerr[CX];//错误识别
    double recoratio[CX];//能正确识别的样本个数
    double cannotrecoratio[CX];//拒绝识别
    double recoerrratio[CX];//错误识别
    double nodenumber[CX];//节点个数


    double averagereco = 0;//平均正确识别率
    double averagecannotreco = 0;//平均拒绝识别率
    double averagerecoerr = 0;//平均错误识别率
    double averagerule = 0;//平均规则个数
    double averagenodenumber = 0;//平均节点个数
    int c;
    for (c = 0; c<CX; c++)
    {
        reco[c] = 0;
        recoerr[c] = 0;
        cannotreco[c] = 0;
    }
    /************统计各属性取值的个数*************/
    //初始化
    for (i = 0; i<A; i++)
    {
        attnum[i] = 1;
        sort(&ordata[0][0], ON, i + 1);
        //  out(&ordata[0][0],ON);
        for (j = 1; j<ON; j++)
        {
            if (ordata[j][i + 1] == ordata[j - 1][i + 1]) 
                /*hiro:!!!严重错误!!!,注意这里两个是浮点数,浮点数的相等判断是不能直接用
                ==的,*/
                continue;
            else 
                attnum[i]++;
        }
        //  std::cout<<attnum[i]<<std::endl;
    }

    /*hiro:循环CX次*/
    for (c = 0; c<CX; c++)
    {

        noden = 0;
        bb = 0;
        int l = 0;
        /******************生成训练集*******************/
        int a[ON];//标志
        for (i = 0; i<ON; i++)
            a[i] = 0;//标志初始化为0
        /*hiro:在大数据的情况下我觉得不允许这样子写产生随机数
        不然会死循环,之前写一个10000选7500的,像他这种暴力随机选取,
        基本可以当作死循环了。应该维护一个未被选中的链表,或者一开始就生成
        一个随机数map,从里面遍历取随机ID,*/
        for (i = 0; i<N; i++)
        {
            /*hiro:↓↓↓我就想问一句,rand要不要初始化设seed?*/
            k = rand() % ON;
            if (a[k] == 0)//该随机产生的样本尚未被选中
            {
                //该样本记入data[i]
                for (j = 0; j<A + 2; j++)
                    data[i][j] = ordata[k][j];
                    //标记a[rand()%150]为1
                a[k] = 1;
            }
            else//该随机产生的样本已经被选中
            {
                k = rand() % ON;
                //继续产生下一个随机数,直到对应样本未被选中
                while (a[k] == 1)
                {
                    k = rand() % ON;
                }
                //该样本记入data[i]
                for (j = 0; j<A + 2; j++)
                    data[i][j] = ordata[k][j];
                //标记a[rand()%150]为1
                a[k] = 1;
            }
        }

        /******************生成测试集*******************/
        /*hiro:看样子,提供的ON个数中,N个数用于训练这个决策树,ON-N是用来检验这个
        决策树的*/
        k = 0;
        for (i = 0; i<ON - N; i++)
        {
            while (a[k] == 1)
            {
                k++;
            }
            for (j = 0; j<A + 2; j++)
                test[i][j] = ordata[k][j];
            k++;
        }

        for (i = 0; i<N; i++)
            /*hiro:A+1列当中,具体的数字貌似代表了分类,
            考虑可读性其实可以用宏,关于这份代码,样例,场景的可读性,我会在后文详述*/
            if (data[i][A + 1] == 2)
                l++;
        //   std::cout<<"aaaa==  "<<l<<std::endl;
        /*hiro:给提取出来的数据(测试集和训练集)重新从1开始编号*/
        for (i = 0; i<N; i++)
            data[i][0] = i + 1;
        for (i = 0; i<ON - N; i++)
            test[i][0] = i + 1;
        //for(i=0;i<A;i++)
        //std::cout<<i<<":"<<gainratio(&ordata[0][0],ON,i)<<std::endl;
        /*hiro:处理完数据,开始建树*/
        Tree = root();
        nextnode(Tree);
        out(&data[0][0], N);
        std::cout << std::endl;
        std::cout << std::endl;
        std::cout << std::endl;
        std::cout << std::endl;
        std::cout << std::endl;
        out(&test[0][0], ON - N);
        //outnode(Tree);
        outTree(Tree);
        //std::cout<<bb<<std::endl;

        for (i = 0; i<ON - N; i++)
        {
            bz2 = 1;//默认为拒绝识别的

            for (j = 0; j<bb; j++)
            {
                bz1 = 1;//默认为匹配的
                for (k = 0; k<A; k++)
                {
                    if ((test[i][k + 1] == rule[j][k + 1]) || (rule[j][k + 1] == 0)) 
                        bz1 = bz1 * 1;/*hiro:骚年你需要与或非吗?想出*1,*0也是辛苦你了*/
                    else
                    {
                        bz1 = bz1 * 0;
                        break;
                    }
                }

                if ((bz1 == 1) && (test[i][A + 1] == rule[j][A + 1]))
                {
                    reco[c]++;
                    bz2 = 0;
                    break;
                }
                /*hiro:我没有眼花??不是我码多了吧??↓↓↓对比↑↑↑*/
                /*if ((bz1 == 1) && (test[i][A + 1] != rule[j][A + 1]))
                {
                    recoerr[c]++;
                    bz2 = 0;
                    break;
                }*/
            }
            if (bz2 == 1)
            {
                cannotreco[c]++;
            }
        }
        /*hiro:计算一堆统计信息,详情看对应注释*/
        nodenumber[c] = (double)noden;
        recoratio[c] = (double)reco[c] / (double)(ON - N);
        recoerrratio[c] = (double)recoerr[c] / (double)(ON - N);
        cannotrecoratio[c] = (double)cannotreco[c] / (double)(ON - N);

        std::cout << "次数:" << c << std::endl;

        std::cout << "recog-right:" << (double)reco[c] / (double)(ON - N) << std::endl;

        std::cout << "recog-worng:" << (double)recoerr[c] / (double)(ON - N) << std::endl;

        std::cout << "recog-cannot:" << (double)cannotreco[c] / (double)(ON - N) << std::endl;

        std::cout << "nodenumber:" << noden << std::endl;

        averagerule = averagerule + bb;

    }
    /*hiro:统计各种平均值*/
    for (c = 0; c<CX; c++)
    {
        averagereco = averagereco + recoratio[c];
        averagerecoerr = averagerecoerr + recoerrratio[c];
        averagecannotreco = averagecannotreco + cannotrecoratio[c];
        averagenodenumber = averagenodenumber + nodenumber[c];
    }


    averagereco = averagereco / (double)CX;
    averagerecoerr = averagerecoerr / (double)CX;
    averagecannotreco = averagecannotreco / (double)CX;
    averagenodenumber = averagenodenumber / (double)CX;

    std::cout << "average recog-right:" << averagereco << std::endl;

    std::cout << "average recog-worng:" << averagerecoerr << std::endl;

    std::cout << "average recog-cannot:" << averagecannotreco << std::endl;

    std::cout << "average nodenumber:" << averagenodenumber << std::endl;

    std::cout << "averagerule:" << averagerule / CX << std::endl;

    //  out(&test[0][0],ON-N);
    //  std::cout<<ON-N<<std::endl;
    //  out(&ordata[0][0],ON);
    //  std::cout<<std::endl;
    //  out(&data[0][0],N);


    std::cout << "sss" << std::endl;
}

void Input(){

    std::ifstream fin;
    char c;
    fin.open("input.txt");
    for (int i = 0; i < ON; i++){
        for (int j = 0; j < A + 2; j++){
            fin >> ordata[i][j];
        }
    }

}
/*hiro:输出数组a[n][A+2]的内容*/
void out(double *a, int n)
{
    int i, j;
    for (i = 0; i<n; i++)
    {
        //std::cout<<a[i*(A+2)]<<",";
        //std::cout<<a[i*(A+2)+A+1]<<",";

        for (j = 0; j<A + 2; j++)
        {/*hiro:这位兄台的加法真是好,看来是学习汇编很透彻,
         对指针运算也很了解嘛
         but,
         why not a[i][j]???*/
            std::cout << a[i*(A + 2) + j] << ',';
        }
        std::cout << std::endl;
    }
}

void sort(double *a, int num, int n)//对a数组根据第n列进行排序
{


    int i = 0, j = 0, k = 0;
    double aa;
    /*hiro:检验输入的n的合法性,但,,,
    why 0<n&&n<A+2   ???*/
    k = 0;
    for (i = 0; i<A + 2; i++)
    {
        if (n != i) 
            k++;
    }
    if (k == A + 2)
    {
        std::cout << "依据错误的属性序号进行排序!" << std::endl;
        exit(0);
    }

    /*hiro:嗯,下面开始排序,看好了,认真看*/
    for (i = 0; i<num; i++)
    {
        j = i;
        k = i;
        /*hiro:先遍历整个表,找到最小值的下标*/
        while (j<num)
        {
            /*hiro:大家当作复习指针加法*/
            if (a[j*(A + 2) + n]<a[k*(A + 2) + n])
            {
                k = j;
            }
            j++;
        }
        /*hiro:然后,将最小的放到表的最前面*/
        for (j = 0; j<A + 2; j++)
        {
            aa = a[i*(A + 2) + j];
            a[i*(A + 2) + j] = a[k*(A + 2) + j];
            a[k*(A + 2) + j] = aa;
        }

        /*hiro:.......*/
    }

    /*hiro:嗯,这位兄弟就是这样排序的。到这里我真的是死心了,
    估计这位兄弟是数学系的研究生被老师抓去写代码了。
    【这位道友,你没有听说过快排,写个冒泡总还可以吧,你这足足的O(N^2)啊...
    冒泡都起码是O((N^2)/2)啊..........】
    不过一想到可能是数学系的可能没有学过算法,只能用原生的思想写程序,能写这么长,已经是很不错的了。
    嗯。
    可这本是教材啊,不是写编程语言的实验啊,别误人子弟啊!*/
}

struct node * root()//生成根节点
{
    /*hiro:果然是没有delete的,注意内存回收*/
    struct node *r = new node;
    noden++;
    int i;
    int k;//标志
    double gainr[A];
    int maxi = 0;
    double max = 0;


    //如果训练集为空,则直接返回空
    if (N == 0) return(NULL);
    //判断训练集是否只含有一个类
    /*hiro:这个变量k完全可以不要*/
    k = 0;
    for (i = 1; i<N; i++)
    {
        if (data[i][A + 1] != data[0][A + 1])
        {
            k = 1;
            break;
        }
        else continue;
    }
    /*hiro:直接判断i==N就行了*/
    //如果标志k==1,表示不止一个类
    if (k == 0)
    {
        r->leaf = 0;
        r->cla = data[0][A + 1];
    }
    else
    {
        r->leaf = 0;
        r->cla = 0;
        for (i = 0; i<A; i++)
        {
            r->att[i] = 0;
            r->attvalue[i] = -1;
        }
        //计算每个未确定条件属性的信息增益比
        for (i = 0; i<A; i++)
        {
            gainr[i] = 0;
            if (r->att[i] == 1) 
                continue;
            gainr[i] = gainratio(&data[0][0], N, i);
            if (gainr[i]>max)
            {
                maxi = i;
                max = gainr[i];
            }
        }//maxi记录信息增益比最大的属性(0~3)
        r->i = maxi;
    }
    return (r);
}

/*hiro:警告!!!!
该函数内有大量的浮点数错误,log函数调用错误,程序设计错误等问题
切勿模仿*/
double gainratio(double *d, int n, int a)//求有n个样本的数据集d的属性a的信息增益率
{
    sort(d, n, a);
    int i, j, k, m, s;
    double sp = 0;//属性a的信息熵
    double I = 0;//决策类的熵
    double E = 0;//属性a的条件熵
    double E1 = 0;
    //value数组记录属性a各个取值
    //double *value=new double(attnum[a]);
    //num数组记录决策类各个取值的个数
    int num[C];



    /*******************求决策类的熵***********************/
    sort(d, n, A + 1);
    i = 0;
    k = 1;/*hiro:K为计数变量*/
    for (j = 1; j<n; j++)
    {
        if (d[j*(A + 2) + A + 1] == d[i*(A + 2) + A + 1]) 
            k++;/*hiro:因为已经排序,所以可以这样统计有多少个相等的值*/
        else
        {
            /*hiro:是不是应该用换底公式计算??貌似math.h库没有提供2为底的log函数
            参考自www.cplusplus.com:
            The natural logarithm is the base-e logarithm:
            the inverse of the natural exponential function (exp). 
            For common (base-10) logarithms, see log10.*/
            I = I - (double)k / (double)n*log((double)k / (double)n);
            i = j;
            k = 1;
        }
    }
    I = I - (double)k / (double)n*log((double)k / (double)n);
    //std::cout<<"I="<<I<<std::endl;
    /*******************求属性a的熵和条件熵***********************/
    sort(d, n, a + 1);
    i = 0;
    k = 1;
    /*hiro:同上,先排序,然后统计个数,实际操作起来,因为这样利用到了cache
          所以很有可能更加快,
          相关问题阅读:
          http://stackoverflow.com/questions/11227809/why-is-it-faster-to-process-a-sorted-array-than-an-unsorted-array*/
    for (j = 1; j<n; j++)
    {
        /*hiro:浮点数判断问题*/
        if (d[j*(A + 2) + a + 1] == d[i*(A + 2) + a + 1])
        {
            k++;
            continue;
        }
        else
        {/*hiro:同理,应该要使用换底公式*/
            sp = sp - (double)k / (double)n*log((double)k / (double)n);//熵
            for (m = 0; m<C; m++)
                num[m] = 0;
            for (s = i; s<j; s++)
            {
                for (m = 0; m<C; m++)
                {
                    /*hiro:同样的严重问题,浮点数的相等判断*/
                    if (d[s*(A + 2) + A + 1] == (double)(m + 1)) 
                        num[m]++;
                }
            }
            E1 = 0;
            for (m = 0; m<C; m++)
            {
                /*hiro:同样的严重问题,浮点数的相等判断*/
                if ((double)num[m] / (double)k == 0) 
                    continue;
                /*hiro:同样的问题,需要换底*/
                E1 = E1 - (double)num[m] / (double)k*log((double)num[m] / (double)k);//条件熵
            }
            E = E + (double)k / (double)n*E1;

            i = j;
            k = 1;


        }


    }
    /*hiro:需要换底*/
    sp = sp - (double)k / (double)n*log((double)k / (double)n);
    /*hiro:下面这一段应该不是手抖复制多了的
    是由于上面的大循环写的姿势不算很好,对于有n类值的属性,上面的大循环只能
    处理n-1,于是,,,他,,,,他,,,,他,,,,把第n次的处理,,,直接
    ,,,,,复制了一次代码。。。。。。
    我是这么理解的*/
    for (m = 0; m<C; m++) 
        num[m] = 0;
    for (s = i; s<j; s++)
    {
        for (m = 0; m<C; m++)
        {
            /*hiro:浮点数问题*/
            if (d[s*(A + 2) + A + 1] == (double)(m + 1)) 
                num[m]++;
            // break;
        }
    }
    E1 = 0;
    for (m = 0; m<C; m++)
    {
        if ((double)num[m] / (double)k == 0) 
            continue;

        E1 = E1 - (double)num[m] / (double)k*log((double)num[m] / (double)k);//条件熵
    }
    E = E + (double)k / (double)n*E1;
    /*hiro:到这里为止,*/
    //  std::cout<<"E="<<E<<std::endl;
    //  std::cout<<"sp="<<sp<<std::endl;

    /*******************求属性a的条件熵***********************/

    if (sp == 0) /*hiro:浮点数问题*/
        return(-10000);
    return ((I - E) / sp);
}


struct node * copynode(struct node *a, struct node *b)//拷贝a节点的数据到b节点,非完全拷贝,后三项重新附初值
{
    /*hiro:这是我目前在这份代码里看得最舒服的一段函数了*/
    int i;
    b->leaf = a->leaf;
    b->cla = a->cla;
    for (i = 0; i<A; i++)
    {
        b->att[i] = a->att[i];
        b->attvalue[i] = a->attvalue[i];
    }
    b->i = 0;
    for (i = 0; i<MAX; i++)
    {
        b->nextvalue[i] = 0;
        b->next[i] = NULL;
    }
    return(b);
}

void nextnode(struct node *p)//根据当前节点生成孩子节点,返回非叶子节点的个数
{/*hiro:呃。。。你还真好意思眼睁睁看着函数的返回类型写这
 种函数说明的注释诶,,返回非叶子结点的个数*/
    int notleaf = 0;
    sort(&data[0][0], N, 0);
    int i, j, k;
    int jsq;
    struct node *q;
    //  double gainr[A];
    //  int maxi=0;
    //  double max=0;
    /****************生成当前数组d**************/
    double *d;
    int bz[N];
    int n;//d中样本的个数
    for (i = 0; i<N; i++) 
        bz[i] = 1; //等于1为满足节点p的样本
    for (i = 0; i<A; i++)
    {
        if (p->att[i] == 0) 
            continue;
        for (j = 0; j<N; j++)
        {
            /*hiro:浮点数问题*/
            if (data[j][i + 1] != p->attvalue[i])
                bz[j] = bz[j] * 0;
        }
    }
    n = 0;
    /*hiro:统计个数*/
    for (i = 0; i<N; i++)
    {
        if (bz[i] == 1) 
            n++;
    }
    //  std::cout<<"aaaaaaaaan"<<n<<std::endl;
    /*hiro:忘记delete了吧*/
    d = new double[n*(A + 2)];
    k = 0;
    /*hiro:寻找满足结点的样本并提取到d数组中*/
    for (i = 0; i<n; i++)
    {
        while ((bz[k] == 0) && (k<N))
            k++;
        //      std::cout<<k<<std::endl;
        for (j = 0; j<A + 2; j++)
        {
            /*hiro:↓真是一日看尽长安花式的数组调用*/
            d[i*(A + 2) + j] = data[k][j];
        }
        k++;
    }

    //  out(d,n);~
    //std::cout<<"aaaaaaaaaaaaaan:"<<n<<std::endl;
    /************生成孩子节点************/
    sort(&d[0], n, (p->i) + 1);
    //  std::cout<<"p->i:"<<p->i<<std::endl;
    k = 0;
    jsq = 0;//计数器清零
    for (i = 0; i<n; i++)
    {
        if (d[i*(A + 2) + (p->i) + 1] == d[k*(A + 2) + (p->i) + 1]) 
            continue;
        /*hiro:忘记delete了大兄弟*/
        q = new node;
        noden++;
        q = copynode(p, q);
        q->att[p->i] = 1;
        q->attvalue[p->i] = d[k*(A + 2) + (p->i) + 1];
        p->nextvalue[jsq] = d[k*(A + 2) + (p->i) + 1];
        p->next[jsq] = q;
        jsq++;
        k = i;
    }
    //最后一个孩子节点
    q = new node;
    noden++;
    q = copynode(p, q);
    q->att[p->i] = 1;
    q->attvalue[p->i] = d[k*(A + 2) + (p->i) + 1];
    p->nextvalue[jsq] = d[k*(A + 2) + (p->i) + 1];
    p->next[jsq] = q;
    jsq++;
    p->nextvalue[jsq] = -1;
    //标志叶子节点
    //  std::cout<<"jsq:"<<jsq<<std::endl;
    for (i = 0; i<jsq; i++)
    {
        //      std::cout<<i<<std::endl;
        if (isleaf(p->next[i]) != 0)
        {
            p->next[i]->leaf = 1;
            p->next[i]->cla = isleaf(p->next[i]);
        }
        else
        {
            //  if(p->nextvalue[i]==-1) break;
            notleaf++;
            //计算每个未确定条件属性的信息增益比
            p->next[i]->i = nodemaxi(p->next[i]);
            //;     std::cout<<"aaaaaaaaa"<<std::endl;
            nextnode(p->next[i]);

        }
    }
    /*hiro:忘记了写return?
    这个递归函数我怎么看怎么觉得很危险,
    各种爆栈,递归出口不明确*/
    //return(notleaf);
}

int isleaf(struct node *p)//判断节点p是否为叶子节点,否返回0,是返回决策类的值
{
    sort(&data[0][0], N, 0);
    int i, j, k;
    //  int jsq;
    //  struct node *q;
    /****************生成当前数组d**************/
    double *d;
    int bz[N];
    int n;//d中样本的个数
    for (i = 0; i<N; i++) 
        bz[i] = 1; //等于1为满足节点p的样本
    for (i = 0; i<A; i++)
    {
        if (p->att[i] == 0)
            continue;
        for (j = 0; j<N; j++)
        {
            if (data[j][i + 1] != p->attvalue[i])
                bz[j] = bz[j] * 0;
        }
    }
    n = 0;
    for (i = 0; i<N; i++)
    {
        if (bz[i] == 1) n++;
    }
    //  std::cout<<"n"<<n<<std::endl;
    d = new double[n*(A + 2)];
    k = 0;
    for (i = 0; i<n; i++)
    {
        while ((bz[k] == 0) && (k<N)) k++;
        //      std::cout<<k<<std::endl;
        for (j = 0; j<A + 2; j++)
        {
            d[i*(A + 2) + j] = data[k][j];
        }
        k++;
    }
    /*hiro:嗯,是不是觉得上面的代码好眼熟?
    同样的也是做提取数据的操作
    写个函数比复制要困难很多吗?
    实际上判断叶子节点的是下面这点*/
    for (i = 1; i<n; i++)
    {
        if (d[i*(A + 2) + A + 1] != d[0 * (A + 2) + A + 1])
            return(0);
    }
    p->cla = (int)d[0 * (A + 2) + A + 1];
    return((int)d[0 * (A + 2) + A + 1]);
}

void outnode(struct node *p)//输出节点p
{
    int i;
    std::cout << "p->leaf:" << p->leaf << std::endl;
    std::cout << "p->cla:" << p->cla << std::endl;
    std::cout << "   att:  ";
    for (i = 0; i<A; i++)
    {
        std::cout << i << "\t";
    }
    std::cout << std::endl;
    std::cout << " att[i]: ";
    for (i = 0; i<A; i++)
    {
        std::cout << p->att[i] << "\t";
    }
    std::cout << std::endl;
    std::cout << "attvalue: ";
    for (i = 0; i<A; i++)
    {
        std::cout << p->attvalue[i] << "\t";
    }
    std::cout << std::endl;
    std::cout << "p->i:" << p->i << std::endl;

    std::cout << " next[i]: " << std::endl;
    i = 0;
    while (p->nextvalue[i] != -1)
    {
        std::cout << p->nextvalue[i] << "\t" << isleaf(p->next[i]) << std::endl;;
        i++;
    }
    std::cout << std::endl;

    std::cout << std::endl;
    std::cout << std::endl;
    std::cout << std::endl;
}

void outTree(struct node *p)//输出树
{
    int i;

    if (isleaf(p) != 0)//是叶子
    {
        for (i = 0; i<A; i++)/*hiro:←这个for很可能是忘记和下面的一并注释的*/
            //      std::cout<<p->attvalue[i]*p->att[i]<<"\t";
            //  std::cout<<p->cla<<std::endl;
            /*******写入规则集*******/
            rule[bb][0] = bb;
        for (i = 0; i<A; i++)
        {
            /*hiro:编程语言都支持逻辑运算的,请放过乘法器*/
            rule[bb][i + 1] = p->attvalue[i] * p->att[i];
        }
        rule[bb][A + 1] = p->cla;

        bb++;

    }
    else//是中间节点
    {
        i = 0;
        while (p->nextvalue[i] != -1)
        {
            outTree(p->next[i]);
            i++;
        }
    }

}

int nodemaxi(struct node *p)//求节点p的i
{
    int notleaf = 0;
    sort(&data[0][0], N, 0);
    int i, j, k;
    //  int jsq;
    //  struct node *q;
    double gainr[A];
    int maxi = -1;
    double max = -1;
    /****************生成当前数组d**************/
    double *d;
    int bz[N];
    int n;//d中样本的个数
    for (i = 0; i<N; i++) 
        bz[i] = 1; //等于1为满足节点p的样本
    for (i = 0; i<A; i++)
    {
        if (p->att[i] == 0) 
            continue;
        for (j = 0; j<N; j++)
        {
            if (data[j][i + 1] != p->attvalue[i])
                bz[j] = bz[j] * 0;
        }
    }
    n = 0;
    for (i = 0; i<N; i++)
    {
        if (bz[i] == 1) 
            n++;
    }/*hiro:至此为止统计了训练集中满足结点P属性的数量n*/
    //
    //  std::cout<<"n"<<n<<std::endl;
    /*hiro:老问题,内存泄漏了*/
    d = new double[n*(A + 2)];
    k = 0;
    for (i = 0; i<n; i++)
    {
        while ((bz[k] == 0) && (k<N))
            k++;
        //      std::cout<<k<<std::endl;
        for (j = 0; j<A + 2; j++)
        {
            d[i*(A + 2) + j] = data[k][j];
        }
        k++;
    }
    /*hiro:生成了一个待求gain_ratio的数据集及其样本数量n*/
    /*hiro:下面就是用来求最大gain_ratio*/
    //  out(d,n);
    max = -1;
    for (j = 0; j<A; j++)
    {

        gainr[j] = 0;
        if (p->att[j] == 1) 
            continue;
        gainr[j] = gainratio(d, n, j);
        //  if(gainr[j]<=0) continue;
        //  std::cout<<"aaaaaaaaaaa"<<gainr[j]<<std::endl;
        if (gainr[j]>max)
        {
            maxi = j;
            max = gainr[j];
        }
    }//maxi记录信息增益比最大的属性(0~3)
    //  std::cout<<max<<","<<maxi<<std::endl;

    return(maxi);


}

感想:

误人子弟。

附:

样例数据

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值