数据挖掘任务常用算法笔记

        数据挖掘概述

         数据挖掘任务一般可以分两类:描述和预测。描述性挖掘任务刻画数据库中数据的一般特征。预测性数据挖掘任务在当前的数据上进行推断,以进行预测。数据挖掘功能用于指定数据挖掘任务中要寻找的模式类型,包括数据特征化和区分,关联分析,分类,聚类,孤立点分析,演变分析等。

        其中分类、聚类可以将数据库中的数据自动地分类,因此也有人把它们统称为数据库分段(Database Segmentation),是一直以来广受关注的非常活跃的研究领域。 

        分类(Classification)在数据挖掘中是一项非常重要的任务,目前在商业上应用非常广泛。分类的目的是建立一个分类函数或分类模型(也常常称作分类器),该模型能把数据库中的数据项映射到给定类别中的某一个。

        分类和回归都可用于预测,也就是从利用历史数据纪录中自动推导出对给定数据的推广描述,从而能对未来数据进行外推。和回归方法不同的是,分类的输出是离散的类别值,而回归的输出则是连续数值。分类的效果一般和数据的特点有关。有些数据噪声大,有些有缺值,有些分布稀疏,有些属性间相关性强,有些属性是离散的而有些是连续值或混合式的。目前普遍认为不存在一种方法能适合于各种特点的数据。

        贝叶斯分类

        贝叶斯分类算法是统计学的一种分类方法,它是一类利用概率统计知识进行分类的算法。在许多场合,朴素贝叶斯(Na?ve Bayes,NB)分类算法可以与决策树和神经网络分类算法相媲美,该算法能运用到大型数据库中,而且方法简单、分类准确率高、速度快。

         其依据为贝叶斯定理,由于P(X)对于所有类为常数,最大化后验概率P(Ci|X)可转化为最大化先验概率P(X|Ci)P(Ci)。如果训练数据集有许多属性和元组,计算P(X|Ci)的开销可能非常大,为此,通常假设各属性的取值互相独立,这样

先验概率P(x1|Ci),P(x2|Ci),…,P(xn|Ci)可以从训练数据集求得。
        根据此方法,对一个未知类别的样本X,可以先分别计算出X属于每一个类别Ci的概率P(X|Ci)P(Ci),然后选择其中概率最大的类别作为其类别。
      朴素贝叶斯算法成立的前提是各属性之间互相独立。当数据集满足这种独立性假设时,分类的准确度较高,否则可能较低。另外,该算法没有分类规则输出。(不可行,或者将指令分级)

决策树

决策树:这种算法使用某种属性作为选择度量,对中每个非树叶节点选择一个测试属性,并据此对整个样本集进行划分,直到样本集只包含一种类型的数据或没有用于分割的属性为止。通常还采用某种剪枝方法试图剪去反映数据中噪声的分枝,提高准确分类效 率。早期的决策树算法假定数据是驻留内存的,这对大型数据库上的数据挖掘是一种能力上的限制。判定树算法的优点之一在于分类结果容易转换成分类规则。其具有计算量相对较小,可以处理数值和枚举两种属性,能够清晰地显示哪些字段比较重要,容易转换成简单直观的IF_THEN分类规则,容易转换成数据库查询语句等优点。在某些情况下,其精度较高。类似于if-else流程图


神经网络


构造节点

分治算法

分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。

分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。

分治法所能解决的问题一般具有以下几个特征:

1) 该问题的规模缩小到一定的程度就可以容易地解决

2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。

3) 利用该问题分解出的子问题的解可以合并为该问题的解;

4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

动态规划

基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。
由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。
与分治法最大的差别是:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)。

1、构造问题所对应的过程。
2、思考过程的最后一个步骤,看看有哪些选择情况。
3、找到最后一步的子问题,确保符合“子问题重叠”,把子问题中不相同的地方设置为参数。
4、使得子问题符合“最优子结构”。
5、找到边界,考虑边界的各种处理方式。
6、确保满足“子问题独立”,一般而言,如果我们是在多个子问题中选择一个作为实施方案,而不会同时实施多个方案,那么子问题就是独立的。
7、考虑如何做备忘录。
8、分析所需时间是否满足要求。
9、写出转移方程式。


有25匹马算法

有25匹马,每匹马都以恒定的速度赛跑,当然马与马之间的速度是不相等的,总共有5个赛道,就是说每轮最多只能有5个马同时赛跑。问题是:要确定出跑的最快的前三名马,需要最少多少轮比赛?


聚类

聚类是数据挖掘领域中一个非常活跃的研究课题。与分类方法不同,聚类分析的输入集是一组未标定的记录,也就是说此时输入的记录还没有进行任何分类。聚类的目的是根据一定的规则,合理地划分记录集合,使得在同一个簇中的对象之间具有较高的相似度,而不同簇中的对象差别较大,并用显式或隐式的方法对其进行描述。所依据的规则由采用的聚类方法定义,对于相同的记录集合不同的算法可能有不同的划分。聚类是无指导学习的一个例子,属观察式学习,而不是事例式学习。 关于数据的预处理为提高分类的精度和效率,可以对数据进行一定的预处理,包括: 
A) 数据清理:旨在消除或减少数据噪声,以及处理空缺值。 
B) 相关性分析:数据中许多属性可能与分类任务不相关,此外,一些属性可能是冗余的。删除这些多余属性的过程称为特征选择。 
C) 数据变换:数据可以概化到较高层概念,也可以进行规范化。

可以考虑将查询指令合并,状态更改指令合并等.


  贪心算法

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。

贪婪算法是一种改进了的分级处理方法。其核心是根据题意选取一种量度标准。然后将这多个输入排成这种量度标准所要求的顺序,按这种顺序一次输入一个量。如果这个输入和当前已构成在这种量度意义下的部分最佳解加在一起不能产生一个可行解,则不把此输入加到这部分解中。这种能够得到某种量度意义下最优解的分级处理方法称为贪婪算法。
对于一个给定的问题,往往可能有好几种量度标准。初看起来,这些量度标准似乎都是可取的,但实际上,用其中的大多数量度标准作贪婪处理所得到该量度意义下的最优解并不是问题的最优解,而是次优解。因此,选择能产生问题最优解的最优量度标准是使用贪婪算法的核心。

http://baike.baidu.com/view/298415.htm?fr=aladdin


N个数中随机抽取m个数

但是如果n是个不确定的数?比如服务器每天会收到数以亿计的请求,但是目前服务器端不希望保存所有的请求,只想随机保存这些请求中的m个。试设计一种算法,能够使服务器实时保存m个请求,并使这些请求是从所有请求中的大致等概率被选中的结果。注意:不到一天的结束,是不能提前知道当天所有请求数n是多少的。下面我们分两种情况讨论(1)n已知,(2)n未知。


1 n已知


可以将问题简化为:从 集合A(a_1, a_2, … ,a_n),中随机选取m(0≤m≤n)个元素,使得每个数被选取的概率相等。可以很简单的计算每个数被选取的概率是 m/n 。 如果集合A里面的元素本来就具有随机性, 每个元素在各个位置上出现的概率相等, 并且只在A上选取一次数据,那么直接返回A的前面m个元素就可以了, 或者可以采取每隔k个元素取一个等类似的方法。这样的算法局限很大, 对集合A的要求很高。


假设集合A中的元素在各个位置上不具有随机性, 比如已经按某种方式排序了,那么我们可以遍历集合A中的每一个元素a_i, 根据一定的概率选取a_i,这个概率是多少呢, 设m’为还需要从A中选取的元素个数, n’为元素a_i及其右边的元素个数, 也即n’=(n-i+1)。那么选取元素a_i的概率为 m’/n’。 这个证明较复杂,下面简单验证一下前两个元素被选中的概率:(设p(a_i=1)表示a_i被选中的概率,p(a_i=0)表示a_i没有被选中的概率)


(1)很显然  p(a_1=1)=m/n


(2)p(a_2=1)= p(a_2=1,a_1=1)+p(a_2=1,a_1=0) 
= p(a_1=1)*p(a_2=1│a_1=1)+ p(a_1=0)* p(a_2=1│a_1=0) 
= m/n * (m-1)/(n-1) + (n-m)/n*m/(n-1) 
= m/n


实际编程中选取某个元素时,可以生成一个[0,1]之间的随机数k, 若k<=m'/n'则选取这个元素,否则抛弃。


2 n未知


这个问题可以简化为:一个整数序列生成器,以一定时间间隔生成一个新的整数,一天之内会生成N个,希望实时保存m个整数,使得任何时刻这m个整数都是当前已生成的所有整数数量n中等概率抽取的结果,即概率均为m/n。由于n是未知的,我们需要 以某种特殊的方式进行判决保存还是不保存,以使得满足概率要求。具体步骤如下:


(1)对于前m个请求直接保存到服务器上,对应整数序列相当于,整数数组的前m个直接存下来。


(2)对于m个以后的第k个新请求,以m/k的概率选择保存,并同从已保存的m个请求中随机选出的一个进行交换。


细说就是,


对于第m+1个请求,以m/(m+1)的概率选择留下,如果留下了则从已保存的m个请求中随机选出一个,同它交换;
对于第m+2个请求,以m/(m+2)的概率选择留下,如果留下了则从已保存的m个请求中随机选出一个,同它交换;
对于第m+3个请求,以m/(m+3)的概率选择留下,如果留下了则从已保存的m个请求中随机选出一个,同它交换;
            …


下面我们用数学归纳法证明这个方法使每个元素被选取的概率是m/n:


(1)当n=m+1时, 
对于第m+1个请求以概率m/(m+1)选择留下,显然满足m/n的要求; 
对于前m个请求中的任何一个,能被选择留下有两种情况:a.第m+1个请求被选择留下了并且没有和自己进行交换; b.第m+1个请求没有被选择留下来而自己确实已被选择留下来了。 
所以,概率计算为 m/(m+1) * (m-1)/m + (1 – m/(m+1)) * 1 = (m-1)/(m+1) + 1/(m+1) = m/(m+1)


(2)假设当n=N时,

仍然正确,即任何一个请求被选中的概率都是m/N,现在推到证明当n=N+1时,任何一个请求被选中留下的概率是m/(N+1)。 

对于第N+1个请求,因为是以m/n=m/(N+1)的概率选中的,所以显然满足要求; 
对于前m个请求中任何一个,能被选中留下同样分为同上的两种情况:a.一种是第N+1个被选中了但随机抽取出与它交换的不是自己;  b.另一种情况是自己已留下并且第N+1个未被选中留下。并且前m个请求中的某个被选中的前提是:在处理完第N个请求后,该请求被选中,根据假设这个概率是m/N。


和概率为: [ m/(N+1) * (m-1)/m + (1-m/(N+1)) ]* m/N = [ (m-1)/(N+1) + (N+1-m)/(N+1) ] * m/N = m/(N+1)。


即当n=N+1时,仍然正确。


综合1)、2)可知,此方法满足等概率要求


当然这种选取方法也适用于n已知的情况。




题目


写一个函数,随机地从大小为n的数组中选取m个整数。要求每个元素被选中的概率相等。


解答


这道题目和随机洗牌问题类似,只需要随机选取1个元素, 然后在剩下的元素里面随机选取下一个元素,不断这样操作即可。


这样做能保证每个元素选中的概率一样吗?也就是选中每个元素的概率都是1/n? 答案是YES,让我们来做一下简单的计算。


选第1个元素:在n个中随机选,因此概率为1/n
选第2个元素:在剩下的n-1个中随机选:1/(n-1),由于第1次没有选中它, 而是在另外n-1个中选:(n-1)/n,因此概率为:(n-1)/n * 1/(n-1) = 1/n
选第3个元素:同上:(n-1)/n * (n-2)/(n-1) * 1/(n-2) = 1/n
。。。


因此,按照这种方法选取k个元素,每个元素都是以1/n的概率被选出来的。代码如下: 选出的m个数放到数组前m个位置。




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <cstdlib>
using namespace std;
 
void Swap(int &a, int &b){// 有可能swap同一变量,不能用异或版本
    int t = a;
    a = b;
    b = t;
}
void PickMRandomly(int a[], int n, int m){
    for(int i=0; i<m; ++i){
        int j = rand() % (n-i) + i;// 产生i到n-1间的随机数
        Swap(a[i], a[j]);
    }
}
int main(){
    srand((unsigned)time(0));
    int n = 9, m = 5;
    int a[] = {
        1, 2, 3, 4, 5, 6, 7, 8, 9
    };
    PickMRandomly(a, n, m);
    for(int i=0; i<m; ++i)
        cout<<a[i]<<endl;
    return 0;
}









N个自然数中,少了一个,找出这个数



O(n)空间的比较简单,下面给出几个个只需要1,2个额外变量的算法


(1)求和-容易溢出


S1=1+2+...+N=(N+1)N/2


然后遍历数列每次从S1中减去当前的数字


最后剩下的数字就是所求


为了防止溢出我们可以每次在S1大于一定的数字后,就去减,然后继续求和,再大于就继续减,以此类推。


(2)异或


Y1=1^2^3...^N


然后遍历数列每次异或当前的数字


最后剩下的就是要求的数字


实际上平时我们用的比较多的互逆运算有  加/减   乘/除  对数/幂数 


往往忽略了异或也是有一定的互逆性的


(3)O(N)时间的移动-排序


将a[i]移动到a[a[i]],使得数组有序


然后找出空着的位置


(4)O(NlogN)时间的移动-排序


用快排的思想,在1-N中选取游标X对数组快排一次,如果X被放在a[X-1]的位置上那么,要找的数字在X-N之间


否则X被放在a[X-2]的位置上 要找的数字在1-X-1之间   递归求解,直到找的要找的数字。


 


 










问题2:


1N个自然数,少了两个,找出这两个数


(1)求和-容易溢出


S1=1+2+...+N=(N+1)N/2


S2=1^2+2^2+...+N^2=(N+1)(2N+1)N/6


...


对于少了K个数的情况,如果K很少,我们可以找出K个和上面类似的函数,计算总体值,然后用解K元一次方程得到结果


但要注意函数的选择


(2)异或


按照上面同样的方法,求出最后的值P等于两个数的异或


确定P从低位到高位的第一个1是第i位


现在用快排的思想,将数列分成两个区间A和B


其中A中第i位是0,B中的第i位是1


然后调用问题1中的方法来分别求解A和B


(3)O(N)时间移动-排序


跟上面一样,实际上这种方法对于少了K个数的情况都能适用。


(4)O(NlogN)时间移动-排序


跟上面的方法一样


如果X被放在a[X-1]位置上,要找的两个数字在X-N之间


如果X被放在a[X-2]位置上,要找的数字一个在1-X-1间,一个在X-N之间


如果X被放在a[X-3]位置上,要找的数字都在1-X-1间


对于少了K个数字的情况,这种方法也可以做,但实现起来就比较复杂了


 










问题3:


给你n个数,其中有且仅有一个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那一个数。


(1)异或


经过上面的介绍,应该想到异或


一个数跟自己偶数次异或是0


奇数次异或是自己


 










问题4:


给你n个数,其中有且仅有两个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那两个数。


看看问题2和3中用异或的方法,应该知道答案了吧?
经过上面的介绍,应该想到异或


一个数跟自己偶数次异或是0


奇数次异或是自己












蒙特卡洛随机方法
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值