人工智能课的实验。

数据结构:多叉树

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

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

主函数:

 #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. 【转】Android 平台下使用 i2c-tools

    原文网址:http://my.oschina.net/luoly/blog/368881 Android 平台下使用 i2c-tools Andorid 开发板为 Freescale imx6 的 S ...

  2. 无序线性搜索(Unordered Linear Search)

    假定有一个元素顺序情况不明的数组.这种情况如果我们要搜索一个元素就要遍历整个数组,才能知道这个元素是否在数组中. 这种方法要检查整个数组,核对每个元素.下面是算法实现: #include<std ...

  3. 转化率最高的16个WordPress 电子商务主题

    想自己开一个WordPress的电子商务商店?下面我们分享转化率最高的16个WordPress 电子商务主题,它们拥有最棒的用户体验,集成最新的用户体验,慢慢欣赏吧! 原文地址:http://thet ...

  4. 关于 require的缓存

    有两个文件 a.js内容如下: var add = require("./t.js").add; var add2 = require("./t.js").ad ...

  5. php php打乱数组二维数组、多维数组

    php中的shuffle函数只能打乱一维数组,有什么办法快速便捷的打乱多维数组?手册上提供了 <?php function shuffle_assoc($list) {      if (!is ...

  6. 【剑指Offer学习】【面试题60:把二叉树打印出多行】

    题目:从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印一行. 解题思路 用一个队列来保存将要打印的结点.为了把二叉树的每一行单独打印到一行里,我们须要两个变量:一个变量表示在当前的 ...

  7. AS3聊天单行输入框图文混排完美实现

    几年前刚毕业.第一个游戏模块做的就是聊天.到如今.几个游戏写过几次聊天模块. 之前在4399做的<幻龙骑士>(又名<神骑士>),还有上周六刚上线的<疯狂的子弹>, ...

  8. Oracle SQL函数之聚组函数

    AVG([distinct|all]x) [功能]统计数据表选中行x列的平均值. [参数]all表示对所有的值求平均值,distinct只对不同的值求平均值,默认为all 如果有参数distinct或 ...

  9. HTML之学习笔记(二)颜色体系

    html页面的颜色表示法有三种:英文表示,16进制表示和10进制表示.颜色通过三原色即红.绿.蓝三种按比例混合而成,如红色的10进制表示为255,0,0,即按照红色.绿色.蓝色的格式,权值取0~255 ...

  10. XHR 框架与 Dojo( xhrGet,xhrPut,xhrDelete)

    总结 本文介绍了 Dojo 中三种浏览器与服务器交互的方式,这三种方式各有优缺点,但是在使用方式却出奇的一致: xhr 框架的函数,dojo.io.iframe.dojo.io.script 对象的函 ...