结构与类共享几乎所有相同的语法,但结构比类受到的限制更多:
1、尽管结构的静态字段可以初始化,结构实例字段声明还是不能使用初始值设定项。
2、结构不能声明默认构造函数(没有参数的构造函数)或析构函数。
3、结构的副本由编译器自动创建和销毁,因此不需要使用默认构造函数和析构函数。实际上,编译器通过为所有字段赋予默认值(参见默认值表)来实现默认构造函数。结构不能从类或其他结构继承。
4、结构是值类型——如果从结构创建一个对象并将该对象赋给某个变量,变量则包含结构的全部值。
结构具有以下特点:
KNN算法非常简单而且非常有效。 KNN的模型用整个训练数据集表示。 是不是特简单?
通过搜索整个训练集内K个最相似的实例(邻居),并对这些K个实例的输出变量进行汇总,来预测新的数据点。 对于回归问题,新的点可能是平均输出变量,对于分类问题,新的点可能是众数类别值。
成功的诀窍在于如何确定数据实例之间的相似性。如果你的属性都是相同的比例,最简单的方法就是使用欧几里德距离,它可以根据每个输入变量之间的差直接计算。
优点:
缺点:
朴素贝叶斯是一种简单但极为强大的预测建模算法。
该模型由两种类型的概率组成,可以直接从你的训练数据中计算出来:1)每个类别的概率; 2)给定的每个x值的类别的条件概率。 一旦计算出来,概率模型就可以用于使用贝叶斯定理对新数据进行预测。 当你的数据是数值时,通常假设高斯分布(钟形曲线),以便可以轻松估计这些概率。
朴素贝叶斯被称为朴素的原因,在于它假设每个输入变量是独立的。 这是一个强硬的假设,对于真实数据来说是不切实际的,但该技术对于大范围内的复杂问题仍非常有效。
优点:
支持向量机也许是最受欢迎和讨论的机器学习算法之一。
超平面是分割输入变量空间的线。 在SVM中,会选出一个超平面以将输入变量空间中的点按其类别(0类或1类)进行分离。在二维空间中可以将其视为一条线,所有的输入点都可以被这条线完全分开。 SVM学习算法就是要找到能让超平面对类别有最佳分离的系数。
超平面和最近的数据点之间的距离被称为边界,有最大边界的超平面是最佳之选。同时,只有这些离得近的数据点才和超平面的定义和分类器的构造有关,这些点被称为支持向量,他们支持或定义超平面。在具体实践中,我们会用到优化算法来找到能最大化边界的系数值。在小数据集上可以表现比较好
优点:
优点:
决策树是机器学习的一种重要算法。
决策树模型可用二叉树表示。对,就是来自算法和数据结构的二叉树,没什么特别。 每个节点代表单个输入变量(x)和该变量上的左右孩子(假定变量是数字)
决策树学习速度快,预测速度快。 对于许多问题也经常预测准确,并且你不需要为数据做任何特殊准备。
优点:
缺点:
将文本文件导入到数据库,让数据库进行索引排序操作后提取数据到文件
优点:操作简单
缺点:运算速度慢,而且需要数据库设备。
操作方式:
规定一个内存大小,比如200M,200M可以记录(20010241024/4) = 52428800条记录,我们可以每次提取5000万条记录到文件进行排序,要装满9位整数需要20次,所以一共要进行20次排序,需要对文件进行20次读操作
缺点:
编码复杂,速度也慢(至少20次搜索)
关键步骤:
先将整个9位整数进行分段,亿条数据进行分成20段,每段5000万条
在文件中依次搜索05000万,500000011亿……
将排序的结果存入文件
思考下面的问题:
一个最大的9位整数为999999999
这9亿条数据是不重复的
可不可以把这些数据组成一个队列或数组,让它有0~999999999(10亿个)元素
数组下标表示数值,节点中用0表示这个数没有,1表示有这个数
判断0或1只用一个bit存储就够了
声明一个可以包含9位整数的bit数组(10亿),一共需要10亿/8=120M内存
把内存中的数据全部初始化为0, 读取文件中的数据,并将数据放入内存。比如读到一个数据为341245909这个数据,那就先在内存中找到341245909这个bit,并将bit值置为1遍历整个bit数组,将bit为1的数组下标存入文件
一般基于梯度下降的算法都需要进行归一化处理
量纲问题:归一化有利于优化迭代速度(梯度下降),提高精度(KNN)
SVM需要归一化
LDA、DT 不需要归一化
**优点: **
简单直接,易于理解,在低维数据集上有不错的效果。
缺点:
对于高维数据,其计算速度十分慢,主要是慢在计算距离上,它的另外一个缺点就是它需要我们设定希望得到的聚类数k,若我们对于数据没有很好的理解,那么设置k值就成了一种估计性的工作。
优点:
1,距离和规则的相似度容易定义,限制少;
2,不需要预先制定聚类数;
3,可以发现类的层次关系(在一些特定领域如生物有很大作用);
缺点:
1,计算复杂度太高(考虑并行化);
2,奇异值也能产生很大影响;
3,算法很可能聚类成链状(一层包含着一层);
模糊聚类分析一般是指根据研究对象本身的属性来构造模糊矩阵,并在此基础上根据一定的隶属度来确定聚类关系,即用模糊数学的方法把样本之间的模糊关系定量的确定,从而客观且准确地进行聚类。
聚类就是将数据集分成多个类或簇,使得各个类之间的数据差别应尽可能大,类内之间的数据差别应尽可能小,即为“最小化类间相似性,最大化类内相似性”原则 。
从训练集划分点数据出来形成验证集来近似测试误差;
对训练误差进行某种转化来近似测试误差。
引入拉格朗日因子
最小化问题中,包含着最大化问题,对偶后得到
求导=0,带入后得到(被称为KKT条件)
SVM最佳化形式转化为只与αn有关
其中,满足最佳化的条件称之为Karush-Kuhn-Tucker(KKT)
二叉树、满二叉树、完全二叉树、二叉排序树、平衡二叉树
B树、B+树、红黑树、键树、字典树、区间树与线段树、败者树与胜者树
#include
using namespace std;
/*
二分查找思想:
1、数组从小到大排序;
2、查找的key每次和中间数比较,如果key小于mid 查找mid左侧的数组部分;
如果key大于mid,则查找mid右侧的数组部分;如果相等,则直接返回mid。
输入:排序数组-array,数组大小-aSize,查找值-key
返回:返回数组中的相应位置,否则返回-1
*/
//非递归查找
int BinarySearch(int *array, int aSize, int key)
{
if (array == NULL || aSize == 0 )
return -1;
int low = 0;
int high = aSize - 1;
int mid = 0;
while ( low <= high )
{
mid = (low + high )/2;
if ( array[mid] < key)
low = mid + 1;
else if ( array[mid] > key )
high = mid - 1;
else
return mid;
}
return -1;
}
//递归
int BinarySearchRecursive(int *array, int low, int high, int key)
{
if ( low > high )
return -1;
int mid = ( low + high )/2;
if ( array[mid] == key )
return mid;
else if ( array[mid] < key )
return BinarySearchRecursive(array, mid+1, high, key);
else
return BinarySearchRecursive(array, low, mid-1, key);
}
int main()
{
int array[10];
for (int i=0; i<10; i++)
array[i] = i;
cout<<"No recursive:"<
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
算法步骤:
1 从数列中挑出一个元素,称为 “基准”(pivot)
2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。 递归的最底部情形,是数列的大小是0或1,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
快速排序是基于分治模式处理的,对一个典型子数组A[p…r]排序的分治过程为三个步骤:
1.分解:
A[p…r]被划分为俩个(可能空)的子数组A[p …q-1]和A[q+1 …r],
使得 A[p …q-1] <= A[q] <= A[q+1 …r]
2.解决:
通过递归调用快速排序,对子数组A[p …q-1]和A[q+1 …r]排序。
3.合并。
Partition过程:假设数组{6,10,10,3,7,1,8}
(1)从前都和8比较,6小于8,自己和自己交换,不变
(2)10大于8,不进入if
(3)10大于8,不进入if
(4)3小于8,交换到10之前
(5)7小于8,交换到10之前
(6)1小于8,交换到10之前
#include
using namespace std;
int Partition(int a[], int low, int high)
{
int x = a[high]; // 将输入数组的最后一个数作为主元,用它来对数组进行划分
int i = low - 1; // i是最后一个小于主元的数的下标
for (int j = low; j < high; j++) //遍历下标由low到high-1的数
{
if (a[j] < x) // 如果数小于主元的话就将i向前挪动一个位置
{ // 并且交换j和i所分别指向的数
int temp;
i++;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
// 经历上面的循环之后下标为从low到i(包括i)的数就均为小于x的数了
// 现在将主元和i+1位置上面的数进行交换
a[high] = a[i + 1];
a[i + 1] = x;
return i + 1;
}
void QuickSort(int a[], int low, int high)
{
if (low < high)
{
int q = Partition(a, low, high);
QuickSort(a, low, q - 1);
QuickSort(a, q + 1, high);
}
}
int main()
{
int buf[10] = { 12, 4, 34, 6, 8, 65, 3, 2, 988, 45 };
cout << "排序前:" << endl;
for(int i=0;i<10;i++)
cout << buf[i] <<" ";
QuickSort(buf, 0, 9);
cout << "\n\n排序后:" << endl;
for(int i=0;i<10;i++)
cout << buf[i] <<" ";
}
排序前: 12 4 34 6 8 65 3 2 988 45
排序后: 2 3 4 6 8 12 34 45 65 988