人工智能课的实验。

数据结构:多叉树

这个实验我写了好久,开始的时候从数据的读入和表示入手,写到递归建树的部分时遇到了瓶颈,更新样例集和属性集的办法过于繁琐;

于是参考网上的代码后重新写,建立决策树类,把属性集、样例集作为数据成员加入类中,并设立访问数组,这样每次更新属性集、样例集时只是标记访问数组的对应元素即可,不必实际拷贝。

主函数:

 #include "Decision_tree.h"
using namespace std;
int main()
{
int num_attr,num_example;
char filename[];
cout << "请输入训练集文件名:" << endl;
cin >> filename;
freopen(filename, "r", stdin);//从样例文件读入训练内容
cin >> num_attr >> num_example;//读入属性个数、例子个数
Decision_tree my_tree=Decision_tree(num_attr,num_example);
fclose(stdin);
freopen("CON", "r", stdin);//重定向标准输入到控制台
my_tree.display_attr();
cout << "决策树已建成,按深度优先遍历结果如下:" << endl;
my_tree.traverse();
do{
cout << "请输入测试数据,格式:属性1值 属性2值..." << endl;
Example test;
for (int i = ; i < num_attr; i++)
cin >> test.values[i];
int result = my_tree.judge(test);
if (result == ) cout << "分类结果为P" << endl;
else if (result == -) cout << "分类结果为N" << endl;
else if (result == -) cout << "无法根据已有样例集判断" << endl;
cout << "继续吗?(y/n)";
fflush(stdin);
} while (getchar() == 'y');
}

属性结构体

struct Attribute//属性
{
string name;
int count;//属性值个数
int number;//属性的秩
string values[MAX_VAL];
};

样例结构体

struct Example//样例
{
string values[MAX];
int pn;
Example(){ pn = ; }//默认为未分类的
};

决策树的结点

typedef struct Node//树的结点
{
Attribute attr;
Node* children[MAX_VAL];
int classification[MAX_VAL];
Node(){}
}Node;

决策树类的实现

 class Decision_tree//决策树
{
Node *root;
Example e[MAX];//样例全集
Attribute a[MAX_ATTR];//属性全集
int num_attr, num_example;
int visited_exams[MAX];//样例集的访问情况
int visited_attrs[MAX_ATTR];//属性集的访问情况
Node* recursive_build_tree(int left_e[], int left_a[])//递归建树
{
double max = ;
int max_attr=-;
for (int i = ;i<num_attr;i++)
{//求信息增益最大的属性
if (left_a[i]) continue;
double temp = Gain(left_e, i);
if (max<temp)
{
max = temp;
max_attr = i;
}
}
if (max_attr == -) return NULL;//已没有可判的属性,返回空指针
//cout << a[max_attr].name << endl;
//以这个属性为结点,以各属性值为分支递归建树
int p = , n = ;
Node *new_node=new Node();
new_node->attr = a[max_attr];
for (int i = ; i<a[max_attr].count;i++)
{//遍历这个属性的所有属性值
for (int j = ; j < num_example;j++)
{//得到第i个属性值的正反例总数
if (left_e[j]) continue;
if (!e[j].values[max_attr].compare(a[max_attr].values[i]))
{//例子和属性都是循秩访问的,所以向量元素的顺序不能变
if (e[j].pn) p++;
else n++;
}
}
//cout << a[max_attr].values[i] << " ";
//cout << p << " " << n << endl;
if (p && !n)//全是正例,不再分
{
//cout << "P" << endl;
new_node->classification[i] = ;
new_node->children[i] = NULL;
}
else if (n && !p)//全是反例,不再分
{
//cout << "N" << endl;
new_node->classification[i] = -;
new_node->children[i] = NULL;
}
else if (!p && !n)//例子集已空
{
//cout << "none" << endl;
new_node->classification[i] = -;//表示未训练到这种分类,无法判断
new_node->children[i] = NULL;
}
else//例子集不空,且尚未能区分正反,更新访问情况,递归
{
new_node->classification[i] = ;
left_a[max_attr] = ;//更新属性访问情况
int left_e_next[MAX];//下一轮的例子集(为便于回溯,不修改原例子集)
for (int k = ; k < num_example; k++)
left_e_next[k] = left_e[k];
for (int j = ; j < num_example; j++)
{
if (left_e[j]) continue;
if (!e[j].values[max_attr].compare(a[max_attr].values[i]))
left_e_next[j] = ;//属性值匹配的例子,入选下一轮例子集
else left_e_next[j] = ;//属性值不匹配,筛除
}
new_node->children[i] = recursive_build_tree(left_e_next, left_a);//递归
left_a[max_attr] = ;//恢复属性访问情况
}
p = ;
n = ;
}
return new_node;
}
double I(int p, int n)
{
double a = p / (p + (double)n);
double b = n / (p + (double)n);
if (a == || b == ) return ;
return -a*log(a) / log() - b*log(b) / log();
}
double Gain(int left_e[], int cur_attr)//计算信息增益
{
int sum_p=, sum_n=;
int p[] = { }, n[] = { };
for (int i = ; i < num_example; i++)
{//求样例集的p,n
if (left_e[i]) continue;
if (e[i].pn) sum_p++;
else sum_n++;
}
if (!sum_p && !sum_n)
{
//cout << "no more examples!" << endl;
return -;//样例集是空集
} double sum_Ipn = I(sum_p, sum_n);
for (int i = ; i < a[cur_attr].count; i++)
{//求第i个属性值的p,n
for (int j = ; j < num_example; j++)
{
if (left_e[j]) continue;
if (!e[j].values[cur_attr].compare(a[cur_attr].values[i]))
if (e[j].pn) p[i]++;
else n[i]++;
}
}
double E = ;
for (int i = ; i < a[cur_attr].count; i++)//计算属性的期望
E += (p[i] + n[i])*I(p[i], n[i]);
E /= (sum_p + sum_n);
//cout << a[cur_attr].name <<sum_Ipn - E << endl;
return sum_Ipn - E;
}
void recursive_traverse(Node *current)//DFS递归遍历
{
if (current == NULL) return;
cout << current->attr.name << endl;
for (int i = ; i < current->attr.count; i++)
{
cout << current->attr.values[i] << " " << current->classification[i] << endl;
recursive_traverse(current->children[i]);
}
}
int recursive_judge(Example exa, Node *current)
{
for (int i = ; i < current->attr.count; i++)
{
if (!exa.values[current->attr.number].compare(current->attr.values[i]))
{
if (current->children[i]==NULL) return current->classification[i];
else return recursive_judge(exa, current->children[i]);
}
}
return ;
}
public:
Decision_tree(int num1,int num2)
{ //通过读文件初始化
num_attr = num1;
num_example = num2; for (int i = ; i<num_attr; i++)
{
a[i].number = i;//属性的秩
cin>>a[i].name;//读入属性名
cin>>a[i].count;//读入此属性的属性值个数
for (int j = ; j<a[i].count; j++)
{
cin>>a[i].values[j];//读入各属性值
}
} for (int i = ; i<num_example; i++)
{
string temp;
for (int j = ; j < num_attr; j++)
{
cin>>e[i].values[j];
}
cin >> temp;
if (!temp.compare("P")) e[i].pn = ;
else e[i].pn = ;
}
//检查
/*for (int i = 0; i<num_attr; i++)
{
cout << a[i].name << endl;//读入属性名
for (int j = 0; j<a[i].count; j++)
{
cout<<a[i].values[j]<<" ";//读入各属性值
}
cout << endl;
}
for (int i = 0; i<num_example; i++)
{
for (int j = 0; j < num_attr; j++)
cout<<e[i].values[j]<<" ";
cout<<e[i].pn<<endl; }
*/
memset(visited_exams, , sizeof(visited_exams));
memset(visited_attrs, , sizeof(visited_attrs));
root = recursive_build_tree(visited_exams,visited_attrs);
}
void traverse()
{
recursive_traverse(root);
}
int judge(Example exa)//判断
{
int result=recursive_judge(exa,root);
return result;
}
void display_attr()//显示属性
{
cout << "There are " << num_attr << " attributes, they are" << endl;
for (int i = ; i < num_attr; i++)
{
cout << "[" << a[i].name << "]" << endl;
for (int j = ; j < a[i].count; j++)
cout << a[i].values[j] << " ";
cout << endl;
}
}
};

Decision_tree

现在这个版本的代码用了10小时完成,去检查时被研究生贬得一文不值。。。也的确,现在我们写的实验题目面向的都是规模非常小的问题,自然体会不到自己的代码在大数据面前的劣势。不过我现在确实学得太少了,很多数据结构都没有动手实现过,算法也是。对C++也只能算入了门。俗话说“磨刀不误砍柴工”,“工欲善其事,必先利其器”,先把基础知识学好,多做基本练习,学到的数据结构和算法都动手实现一遍,这样遇到实际问题也好对应到合适的数据结构和算法。

另外,参照一本书学习好的代码风格和习惯也是很重要的,因为写代码的习惯是思维习惯的反映,而我现在还处于初学者阶段,按照一种典型的流派模仿,构建起自己的思维模式后再谈其他的。

忽然觉得自己学了快两年编程还这么水实在是不能忍,都怪大一时年少不懂事没好好学基础。。。

不过,“悟已往之不谏,知来者之可追”,有了方向,一步步走下去就好,不求优于别人,但一定要“优于过去的自己”。

ID3算法 决策树 C++实现的更多相关文章

  1. ID3算法 决策树的生成(2)

    # coding:utf-8 import matplotlib.pyplot as plt import numpy as np import pylab def createDataSet(): ...

  2. ID3算法 决策树的生成(1)

    # coding:utf-8 import matplotlib.pyplot as plt import numpy as np import pylab def createDataSet(): ...

  3. Python 实现基于信息熵的 ID3 算法决策树模型

    版本说明 Python version: 3.6.6 |Anaconda, Inc.| (default, Jun 28 2018, 11:21:07) [MSC v.1900 32 bit (Int ...

  4. 决策树笔记:使用ID3算法

    决策树笔记:使用ID3算法 决策树笔记:使用ID3算法 机器学习 先说一个偶然的想法:同样的一堆节点构成的二叉树,平衡树和非平衡树的区别,可以认为是"是否按照重要度逐渐降低"的顺序 ...

  5. 决策树---ID3算法(介绍及Python实现)

    决策树---ID3算法   决策树: 以天气数据库的训练数据为例. Outlook Temperature Humidity Windy PlayGolf? sunny 85 85 FALSE no ...

  6. 02-21 决策树ID3算法

    目录 决策树ID3算法 一.决策树ID3算法学习目标 二.决策树引入 三.决策树ID3算法详解 3.1 if-else和决策树 3.2 信息增益 四.决策树ID3算法流程 4.1 输入 4.2 输出 ...

  7. 决策树ID3算法的java实现(基本试用所有的ID3)

    已知:流感训练数据集,预定义两个类别: 求:用ID3算法建立流感的属性描述决策树 流感训练数据集 No. 头痛 肌肉痛 体温 患流感 1 是(1) 是(1) 正常(0) 否(0) 2 是(1) 是(1 ...

  8. 数据挖掘之决策树ID3算法(C#实现)

    决策树是一种非常经典的分类器,它的作用原理有点类似于我们玩的猜谜游戏.比如猜一个动物: 问:这个动物是陆生动物吗? 答:是的. 问:这个动物有鳃吗? 答:没有. 这样的两个问题顺序就有些颠倒,因为一般 ...

  9. 决策树 -- ID3算法小结

          ID3算法(Iterative Dichotomiser 3 迭代二叉树3代),是一个由Ross Quinlan发明的用于决策树的算法:简单理论是越是小型的决策树越优于大的决策树. 算法归 ...

随机推荐

  1. linux系统下,11款常见远程桌面控制软件

    linux系统下,11款常见远程桌面控制软件 一. Grdc 它是一个用GTK+编写的,适用于gnome桌面环境的远程桌面访问软件.看图: 常见功能: 1.提供全屏,窗口化的远程控制.支持高分辨率下的 ...

  2. linux命令行常用快捷键

    方向          <-前               后 ->删除ctrl + d      删除光标所在位置上的字符相当于VIM里x或者dlctrl + h      删除光标所在 ...

  3. UESTC_邱老师玩游戏 2015 UESTC Training for Dynamic Programming<Problem G>

    G - 邱老师玩游戏 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submi ...

  4. 【错误】:MySql Host is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'

    错误:MySql Host is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts' 解决 ...

  5. AJAX最简单的原理以及应用

    Ajax是创建快速动态网页的技术,通过后台与服务器少量的数据交互,是网页实现异步更新.也就是在不整个刷新页面的情况下,可以更新网页中的局部区域. 在原始web应用的模式中: 浏览器       以 h ...

  6. Lua多重继承

    http://blog.csdn.net/ssihc0/article/details/7742421 代码收藏了,以后用的到 --多重继承 local function search(k,plist ...

  7. poj 3894 System Engineer (二分图最大匹配--匈牙利算法)

    System Engineer Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 507   Accepted: 217 Des ...

  8. ARM9嵌入式学习笔记(2)-Vi使用

    ARM9嵌入式学习笔记(2) 实验1-1-3 Vi使用 vi创建文件vi hello.c:vi smb.conf-打开文件smb.conf i键-插入模式:esc键-命令行模式::-底行模式: 底行模 ...

  9. 实现android4.4新特性:沉浸式状态栏

    先放效果图: 所谓沉浸式状态栏,就是android4.4以后新加入的ui效果,能使最顶部的状态栏自动适宜app顶部的颜色,使两者看起来更像融为一体.下面放上实现代码: requestWindowFea ...

  10. 封装一些数据库SQLCipher的方法(增、删、改、查)

    上一篇随笔只是简单的说了一下使用SQLCipher框架,介绍的比较笼统,可能看一遍之后更加蒙圈了,为了更好的使用这个数据库,整理了我在公司项目的需要用的方法,包括创建表,插入数据,更新数据,搜索查询数 ...