何政韩的博客

Thinking outside the box


  • 首页

  • 关于

  • 分类

  • 归档

  • 搜索

Leetcode(6) ZigZag Conversion

发表于 2018-02-28 | 更新于 2018-10-04 | 分类于 Leetcode刷题笔记

Description:

The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)

P A H N
A P L S I I G
Y I R

And then read line by line: "PAHNAPLSIIGYIR" Write the code that will take a string and make this conversion given a number of rows:

string convert(string text, int nRows);

convert("PAYPALISHIRING", 3) should return "PAHNAPLSIIGYIR".

解法:

python 创建二维列表,将需要的参数写入 cols 和 rows 即可

1
list_2d = [[0 for col in range(cols)] for row in range(rows)]

此外,此题中还需要注意的是python中list与str的互相转换:
str->list:

1
2
str = 'abcde'
list = list(str)

list->str:

1
2
list = ['a','b','c','d','e']
str_convert = ''.join(list)

我对此题的解法为模拟,设计一个flag变量,为-1时方向向下,为+1时方向向上,将string填入二维的list中:

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
27
28
29
30
class Solution:
def convert(self, s, numRows):
"""
:type s: str
:type numRows: int
:rtype: str
"""
if(numRows == 1 or numRows >= len(s)):
return s;
L = [[] for row in range(numRows)];
r = 0;i = 0;flag = -1;
while(i< len(s)):
if(flag == -1):
L[r].append(s[i]);
r+=1;
if(r == numRows):
flag = 1;
r = numRows - 2;
else:
L[r].append(s[i]);
r-=1;
if(r == -1):
flag = -1;
r = 1;
i+=1;
result = ""
for i in range(numRows):
r = ''.join(L[i]);
result += r;
return result

Leetcode(5) Longest Palindromic Substring

发表于 2018-01-30 | 更新于 2018-10-04 | 分类于 Leetcode刷题笔记

Description:

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example:

Input: “babad” Output: “bab” Note: “aba” is also a valid answer.

Example:

Input: “cbbd” Output: “bb”

解法:

Manacher’s Algorithm 马拉车算法

这个算法是求解回文字符串的常见方法,时间复杂度是O(n),他的本质是在常规的回文字符串的查找算法上进行优化,利用回文字符串的对称性减少计算时间。下面是算法的主要步骤:

首先,Manacher算法提供了一种巧妙地办法,将长度为奇数的回文串和长度为偶数的回文串一起考虑,具体做法是,在原字符串的每个相邻两个字符中间插入一个分隔符,同时在首尾也要添加一个分隔符,分隔符的要求是不在原串中出现,一般情况下可以用#号。下面举一个例子:

img

Manacher算法用一个辅助数组Len[i]表示以字符T[i]为中心的最长回文字串的最右字符到T[i]的长度,比如以T[i]为中心的最长回文字串是T[l,r],那么Len[i]=r-i+1。

对于上面的例子,可以得出Len[i]数组为:

img

这个数组有一个重要的性质:Len[i]-1就是该回文子串在原字符串S中的长度,证明如下:

首先在转换得到的字符串T中,所有的回文字串的长度都为奇数,那么对于以T[i]为中心的最长回文字串,其长度就为2*Len[i]-1,经过观察可知,T中所有的回文子串,其中分隔符的数量一定比其他字符的数量多1,也就是有Len[i]个分隔符,剩下Len[i]-1个字符来自原字符串,所以该回文串在原字符串中的长度就为Len[i]-1。

故计算Len数组即可求得原字符串中最长回文子串,Len计算过程如下:

从右往左计算Len数组。Manacher算法的优化之处在于利用之前已经求得的Len数组的部分值减少计算新值的循环次数。即:

当计算Len[i]时,Lenj已经计算完毕。设P为之前计算的Len数组中的最大值所对应的右端点(即当前已找到的最长回文子串的右端点),并且设取得这个最大值的位置为po(即Len取得当前已知的最大值的数组序号,即中心点的位置),分两种情况:

第一种情况:i<=P

考虑回文字符串的对称性,找到i相对于po的对称位置,设为j,又分为两种子情况:

1.1 如果Len[j]<P-i,如下图:

img

那么说明以j为中心的回文串一定在以po为中心的回文串的内部,且j和i关于位置po对称,由回文串的定义可知,一个回文串反过来还是一个回文串,所以以i为中心的回文串的长度至少和以j为中心的回文串一样,即Len[i]>=Len[j]。由对称性可知Len[i]=Len[j]。

1.2 如果Len[j]>=P-i,如下图:

img

由对称性,说明以i为中心的回文串可能会延伸到P之外,而大于P的部分我们还没有进行匹配,所以要从P+1位置开始一个一个进行匹配,直到发生失配,从而更新P和对应的po以及Len[i]。

第二种情况:i>P

如果i比P还要大,说明对于中点为i的回文串还一点都没有匹配,这个时候,就只能老老实实地使用常规算法进行一个一个匹配了,匹配完成后要更新P的位置和对应的po以及Len[i]。

总而言之:

计算Len数组时考虑对称性减少匹配次数,新增两个辅助变量mx和id,其中id为当前已知的最大回文子串中心的位置(Len(j)取最大值时的j值),mx是Len取最大值时回文串能延伸到的最右端的位置,这个算法的最核心的一行如下:

1
p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;

我的代码:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution:
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
t = "#";
i = 0
# 预处理
while(i<len(s)):
t+=s[i];
t+="#";
i+=1;
# 计算
rmax = 0; center = 0; i = 0; rescenter = 0; reslen = 0;p = [0]*len(t);
while(i<len(t)):
if(rmax>i):
x = rmax-i;
y = p[int(2*center-i)];
if(x<y):
p[i] = x;
else:
p[i] = y;
else:
p[i] = 1;
# 向两边查找对称字符串(常规方法)
if(i-p[i]>=0 and i+p[i]<len(t)):
while(t[i+p[i]] == t[i-p[i]]):
p[i]+=1;
if(i-p[i]<0 or i+p[i]>=len(t)):
break;
if(i+p[i]-1>rmax):
rmax = i + p[i] - 1;
center = i;
if(p[i]>reslen):
reslen = p[i];
rescenter = i;
i+=1;
r = t[rescenter-reslen+1:rescenter+reslen-1].replace('#','');
return r;

Leetcode(4) Median of Two Sorted Arrays

发表于 2018-01-28 | 更新于 2018-10-04 | 分类于 Leetcode刷题笔记

Description:

There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

Example 1:

nums1 = [1, 3]
nums2 = [2]

The median is 2.0

Example 2:

nums1 = [1, 2]
nums2 = [3, 4]

The median is (2 + 3)/2 = 2.5

解法:

我采用的解法是用2个变量分别指向两个数组,每次取较小的一个,然后将其指针后移动,直至找到中位数

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
class Solution:
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
if(len(nums1) == 0):
if(len(nums2)%2 != 0):
return nums2[int(len(nums2)/2)];
else:
result1 = nums2[int(len(nums2)/2-1)];
result = nums2[int(len(nums2)/2)];
result = (result+result1)/2;
return result;
if (len(nums2) == 0):
if (len(nums1) % 2 != 0):
return nums1[int(len(nums1) / 2)];
else:
result1 = nums1[int(len(nums1) / 2 - 1)];
result = nums1[int(len(nums1) / 2)];
result = (result + result1) / 2;
return result;
t = len(nums1) + len(nums2);
if (t % 2 != 0):
pos = t / 2;
flag = 0;
else:
pos1 = t / 2 - 1;
pos2 = t / 2;
flag = 1;
i = 0;
j = 0;
k = 0;
result = -1;
while (i < len(nums1) or j < len(nums2)):
if(i == len(nums1)):
result = nums2[j];
j+=1;k+=1;
elif(j == len(nums2)):
result = nums1[i];
i += 1;
k += 1;

elif (nums1[i] < nums2[j]):
result = nums1[i];
i += 1;
k += 1;
elif (nums1[i] > nums2[j]):
result = nums2[j];
j += 1;
k += 1;
else:
result = nums1[i];
i += 1;
k += 2;
j += 1;
if (flag == 0 and k > pos):
return result;
if (flag == 1 and k > pos1):
if(i == len(nums1) and j == len(nums2)):
return result;
if(i == len(nums1)):
result1 = nums2[j];
result = (result + result1) / 2.0;
return result;
if(j == len(nums2)):
result1 = nums1[i];
result = (result + result1) / 2.0;
return result;
if (nums1[i] < nums2[j]):
result1 = nums1[i];
else:
result1 = nums2[j];
result = (result + result1) / 2.0;
return result;

还可以采用分治法解决该问题 参考链接:http://blog.csdn.net/hk2291976/article/details/51107778

Leetcode(3) Longest Substring Without Repeating Characters

发表于 2018-01-23 | 更新于 2018-10-04 | 分类于 Leetcode刷题笔记

Description:

Given a string, find the length of the longest substring without repeating characters.

Examples:

Given “abcabcbb”, the answer is “abc”, which the length is 3.

Given “bbbbb”, the answer is “b”, with the length of 1.

Given “pwwkew”, the answer is “wke”, with the length of 3. Note that the answer must be a substring, “pwke” is a subsequence and not a substring.

解法:

最开始采用暴力法,毫无疑问超时了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
count = [];
for i in range(len(s)):
r = s[i];
cnt = 1;
j = i + 1;
while (j < len(s)):
if (s[j] not in r):
r += s[j];
cnt+=1;
j+=1;
else:
break;
count.append(cnt);
if(len(count) == 0):
return 0;
maxcnt = max(count);
index = count.index(maxcnt);
return maxcnt;

采用滑动窗格法:

用一个数据结构记录序列中存在的字符,考虑i与j两个指针之间的子串长度,若[i,j)为序列且s[j]不存在于此序列中,则s[j]加入字符集,j指针右移;否则i指针右移,且将s[i]移出字符集(此处存在优化,即i不必一次次右移,直接移至序列中与s[j]相同的字母的下一个即可),给出未优化的滑动窗格法代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
i = 0;j = 0; ans = 0;list = [];
while(i<len(s) and j <len(s)):
if(s[j] not in list):
list.append(s[j]);
ans = max(ans,j - i + 1);
j+=1;
else:
list.remove(s[i]);
i+=1;
return ans;

排序算法之冒泡排序

发表于 2017-06-05 | 更新于 2018-10-04 | 分类于 排序算法

冒泡排序

冒泡排序算法的流程如下:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
//外层循环表示待排序的数组长度
//内层循环表示每一轮往上浮动的元素
for (i = length - 1; i > 0 ; i--) {
for (j = 0; j < i; j++) {
if (array[j] > array[j + 1]) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}

监督学习之朴素贝叶斯分类

发表于 2017-05-24 | 更新于 2018-10-04 | 分类于 机器学习

朴素贝叶斯分类

参考:http://www.cnblogs.com/leoo2sk/archive/2010/09/17/naive-bayesian-classifier.html 基本概念: P(A|B) 表示事件B已经发生的前提下,事件A发生的概率,叫做事件B发生下事件A的条件概率。其基本求解公式为:P(A|B) = P(AB)/P(B)。贝叶斯定理: 原理与流程: 流程如下: 那么现在的关键就是如何计算第3步中的各个条件概率。我们可以这么做: 图示: 实例: 这个问题是这样的,对于SNS社区来说,不真实账号(使用虚假身份或用户的小号)是一个普遍存在的问题,作为SNS社区的运营商,希望可以检测出这些不真实账号,从而在一些运营分析报告中避免这些账号的干扰,亦可以加强对SNS社区的了解与监管。 如果通过纯人工检测,需要耗费大量的人力,效率也十分低下,如能引入自动检测机制,必将大大提升工作效率。这个问题说白了,就是要将社区中所有账号在真实账号和不真实账号两个类别上进行分类,下面我们一步一步实现这个过程。 首先设C=0表示真实账号,C=1表示不真实账号。 1、确定特征属性及划分 这一步要找出可以帮助我们区分真实账号与不真实账号的特征属性,在实际应用中,特征属性的数量是很多的,划分也会比较细致,但这里为了简单起见,我们用少量的特征属性以及较粗的划分,并对数据做了修改。 我们选择三个特征属性:a1:日志数量/注册天数,a2:好友数量/注册天数,a3:是否使用真实头像。在SNS社区中这三项都是可以直接从数据库里得到或计算出来的。 下面给出划分:a1:{a<=0.05, 0.05<a<0.2, a>=0.2},a1:{a<=0.1, 0.1<a<0.8, a>=0.8},a3:{a=0(不是),a=1(是)}。 2、获取训练样本 这里使用运维人员曾经人工检测过的1万个账号作为训练样本。 3、计算训练样本中每个类别的频率 4、计算每个类别条件下各个特征属性划分的频率

  1. 使用分类器进行鉴别

下面我们使用上面训练得到的分类器鉴别一个账号,这个账号使用非真实头像,日志数量与注册天数的比率为0.1,好友数与注册天数的比率为0.2。 可以看到,虽然这个用户没有使用真实头像,但是通过分类器的鉴别,更倾向于将此账号归入真实账号类别。这个例子也展示了当特征属性充分多时,朴素贝叶斯分类对个别属性的抗干扰性。

排序算法之插入排序

发表于 2017-05-24 | 更新于 2018-10-04 | 分类于 排序算法

插入排序

基本思想

1.从第一个元素开始,该元素可以认为已经被排序
2.取出下一个元素,在已经排序的元素序列中从后向前扫描
3.如果该元素(已排序)大于新元素,将该元素移到下一位置
4.重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置
5.将新元素插入到该位置后
6.重复步骤 2~5

代码实现

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<iostream>
using namespace std;
void Insertsort(int a[],int n)
{
for (int i = 0; i < n; i++) //总共需要查找n个元素的位置
{
int j;
for (j = i - 1; j >= 0; j--)//对每一个元素,向前查找到小于它的第一个元素
{
if (a[j] < a[i])
{
break;
}
}
if (j != i - 1) //如果不是它之前的第一个,就将j之后的元素向后移一个,并插入自己
{
int tmp = a[i];
int k;
for (k = i - 1; k > j; k--)
{
a[k + 1] = a[k];
}
a[k + 1] = tmp;
}
}
}

int main(void) {
int a[8] = { 2,1,4,5,3,7,8,6 };
cout << "before:" << endl;
for (int i = 0; i < 8; i++) {
cout << a[i] << ",";
}
cout << endl;
Insertsort(a, 8);
cout << "after:" << endl;
for (int i = 0; i < 8; i++) {
cout << a[i] << ",";
}
cout << endl;
system("pause");
}

更加简短的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Solution {
void insert_sort(int a[]) {
for (int i = 1; i < a.length; i++) {
int j = i - 1;
int tmp = a[i];
while (j >= 0 && a[j] > tmp) {
a[j + 1] = a[j];
j -= 1;
}
a[j + 1] = tmp;
}
}
}

排序算法之快速排序

发表于 2017-05-23 | 更新于 2018-10-15 | 分类于 排序算法

快速排序

时间复杂度:O(NlogN),最坏为O(N\N)(逆序的情况)

基本思想:

1.先从数列中取出一个数作为基准数。

2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。

3.再对左右区间重复第二步,直到各区间只有一个数

本质上是分治算法

关键步骤:

算法的关键在于实现对于数组的左右分割,即实现将比pivot小的置于其左侧,比pivot大的置于其右侧。

具体实现步骤如下:

1.选取数组的最后一项为pivot;

2.令i指向第一个比pivot大的数,即对于input[start:i-1],都小于pivot,input[i:end-1]都大于pivot;这个过程用循环实现

3.将pivot于i指向的元素互换位置,使得比pivot小的在其左侧,比pivot大的在其右侧。

4.对pivot两边的子序列递归,进行同样的操作,直至操作序列为一个数。

下面给出一个分割的例子

假设用户输入了如下数组:

下标 0 1 2 3 4 5
数据 6 2 7 9 8 3

此时i = 0; j =0,因为input[0]>pivot,故比pivot小的数没有,不需要改变i的指向,结果如下:

下标 0 1 2 3 4 5
数据 6 2 7 9 8 3

此时i = 1; j = 1,因为j指向的元素2比pivot小(这里的input在这句话上面),故需要交换位置,并将i+1,结果如下:

下标 0 1 2 3 4 5
数据 2 6 7 9 8 3

………

最终将pivot于i的指向的值交换位置,得到如下结果:

下标 0 1 2 3 4 5
数据 2 3 7 9 8 6

然后,对pivot两边的数据,再分组分别进行上述的过程,直到不能再分组为止。

注意:第一遍快速排序不会直接得到最终结果,只会把比k大和比k小的数分到k的两边。为了得到最后结果,需要再次对下标2两边的数组分别执行此步骤,然后再分解数组,直到数组不能再分解为止(只有一个数据),才能得到正确结果。

代码实现:

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
27
28
29
30
31
32
33
class Solution {
void quicksort(int[] input) {
helper(input, 0, input.length - 1);
}

void helper(int[] input, int start, int end) {
if (start >= end) {
return;
}
int pivot = input[end];
int i = start;
//在循环中中,每次都保证如下条件
//对于input[start:i-1],都小于pivot
//对于input[i:j],都大于pivot
for (int j = start; j < end; j++) {
if (input[j] < pivot) {
swap(input, i, j);
i++;
}
j
//分类完成,将pivot与i指向的元素互换
//因为i指向的是比pivot大的元素,互换不影响分割
swap(input, i, end);
helper(input, start, i - 1);
helper(input, i + 1, end);
}

void swap(int[] x, int a, int b) {
int tmp = x[a];
x[a] = x[b];
x[b] = tmp;
}
}

序列模式挖掘之PrefixSpan算法

发表于 2017-05-23 | 更新于 2018-10-04 | 分类于 数据挖掘

PrefixSpan算法原理总结

参考:http://www.cnblogs.com/pinard/p/6323182.html

基本概念

项集数据和序列数据:

左边的数据集就是项集数据,每个项集数据由若干项组成,这些项没有时间上的先后关系。而右边的序列数据则不一样,它是由若干数据项集组成的序列。比如第一个序列<a(abc)(ac)d(cf)>,它由a,abc,ac,d,cf共5个项集数据组成,并且这些项有时间上的先后关系。对于多于一个项的项集我们要加上括号,以便和其他的项集分开。同时由于项集内部是不区分先后顺序的,为了方便数据处理,我们一般将序列数据内所有的项集内部按字母顺序排序。

子序列与频繁序列:

了解了序列数据的概念,我们再来看看上面是子序列。子序列和我们数学上的子集的概念很类似,也就是说,如果某个序列A所有的项集在序列B中的项集都可以找到,则A就是B的子序列。当然,如果用严格的数学描述,子序列是这样的: 对于序列A={a1,a2,…ana1,a2,…an}和序列B={b1,b2,…bmb1,b2,…bm},n≤mn≤m,如果存在数字序列1≤j1≤j2≤…≤jn≤m1≤j1≤j2≤…≤jn≤m, 满足a1⊆bj1,a2⊆bj2…an⊆bjn,则称A是B的子序列。当然反过来说, B就是A的超序列。 而频繁序列则和我们的频繁项集很类似,也就是频繁出现的子序列。比如对于下图,支持度阈值定义为50%,也就是需要出现两次的子序列才是频繁序列。而子序列<(ab)c>是频繁序列,因为它是图中的第一条数据和第三条序列数据的子序列,对应的位置用蓝色标示:

前缀与前缀投影:

在PrefixSpan算法中的前缀prefix通俗意义讲就是序列数据前面部分的子序列。比如对于序列数据B=<a(abc)(ac)d(cf)>,A=<a(abc)a>,则A是B的前缀。当然B的前缀不止一个,比如, , <a(ab)> 也都是B的前缀。 我们再来看前缀投影,其实前缀投影这儿就是我们的后缀,有前缀就有后缀嘛。前缀加上后缀就可以构成一个我们的序列。下面给出前缀和后缀的例子。对于某一个前缀,序列里前缀后面剩下的子序列即为我们的后缀。如果前缀最后的项是项集的一部分,则用一个“_”来占位表示。在PrefixSpan算法中,相同前缀对应的所有后缀的结合我们称为前缀对应的投影数据库。

算法流程

PrefixSpan算法类似Aprior,它从长度为1的前缀开始挖掘序列模式,搜索对应的投影数据库得到长度为1的前缀对应的频繁序列,然后递归的挖掘长度为2的前缀所对应的频繁序列,以此类推,一直递归到不能挖掘到更长的前缀挖掘为止。流程如下: 1)找出所有长度为1的前缀和对应的投影数据库 2)对长度为1的前缀进行计数,将支持度低于阈值αα的前缀对应的项从数据集S删除,同时得到所有的频繁1项序列,i=1. 3)对于每个长度为i满足支持度要求的前缀进行递归挖掘:

  1. 找出前缀所对应的投影数据库。如果投影数据库为空,则递归返回。
  2. 统计对应投影数据库中各项的支持度计数。如果所有项的支持度计数都低于阈值αα,则递归返回。
  3. 将满足支持度计数的各个单项和当前的前缀进行合并,得到若干新的前缀。
  4. 令i=i+1,前缀为合并单项后的各个前缀,分别递归执行第3步。

实例:

设支持度阈值为50%。下图长度为1的前缀包括, , , , , ,我们需要对这6个前缀分别递归搜索找各个前缀对应的频繁序列。如下图所示,每个前缀对应的后缀也标出来了。由于g只在序列4出现,支持度计数只有1,因此无法继续挖掘。我们的长度为1的频繁序列为, , , , ,。去除所有序列中的g,即第4条记录变成<e(af)cbc> 现在我们开始挖掘频繁序列,分别从长度为1的前缀开始。这里我们以d为例子来递归挖掘,其他的节点递归挖掘方法和D一样。方法如下图,首先我们对d的后缀进行计数,得到{a:1, b:2, c:3, d:0, e:1, f:1,_f:1}。注意f和_f是不一样的,因为前者是在和前缀d不同的项集,而后者是和前缀d同项集。由于此时a,d,e,f,_f都达不到支持度阈值,因此我们递归得到的前缀为d的2项频繁序列为和。接着我们分别递归db和dc为前缀所对应的投影序列。首先看db前缀,此时对应的投影后缀只有<_c(ae)>,此时_c,a,e支持度均达不到阈值,因此无法找到以db为前缀的频繁序列。现在我们来递归另外一个前缀dc。以dc为前缀的投影序列为<_f>, <(bc)(ae)>, ,此时我们进行支持度计数,结果为{b:2, a:1, c:1, e:1, _f:1},只有b满足支持度阈值,因此我们得到前缀为dc的三项频繁序列为。我们继续递归以为前缀的频繁序列。由于前缀对应的投影序列<(_c)ae>支持度全部不达标,因此不能产生4项频繁序列。至此以d为前缀的频繁序列挖掘结束,产生的频繁序列为。同样的方法可以得到其他以, , , , 为前缀的频繁序列。

关联规则之Apriori算法

发表于 2017-05-22 | 更新于 2018-10-04 | 分类于 数据挖掘

关联规则之Apriori&Ms-Apriori算法

Apriori

参考:http://blog.csdn.net/u011067360/article/details/24810415

  • 基本概念

1、支持度 关联规则A->B的支持度support=P(AB),指的是事件A和事件B同时发生的概率。 2、置信度 置信度confidence=P(B|A)=P(AB)/P(A),指的是发生事件A的基础上发生事件B的概率。比如说在规则Computer => antivirus_software , 其中 support=2%, confidence=60%中,就表示的意思是所有的商品交易中有2%的顾客同时买了电脑和杀毒软件,并且购买电脑的顾客中有60%也购买了杀毒软件。 3、k项集 如果事件A中包含k个元素,那么称这个事件A为k项集,并且事件A满足最小支持度阈值的事件称为频繁k项集。 4、由频繁项集产生强关联规则 1)K维数据项集LK是频繁项集的必要条件是它所有K-1维子项集也为频繁项集,记为LK-1 2)如果K维数据项集LK的任意一个K-1维子集Lk-1,不是频繁项集,则K维数据项集LK本身也不是最大数据项集 3)同时满足最小支持度阀值和最小置信度阀值的规则称为强规则。

  • 基本思想

第一步通过迭代,检索出事务数据库中的所有频繁项集,即支持度不低于用户设定的阈值的项集; 第二步利用频繁项集构造出满足用户最小信任度的规则。 实例:

Ms-Apriori

参考:http://blog.csdn.net/androidlushangderen/article/details/45082337 Ms-Apriori算法采用另外一种办法,既然统一的支持度值不能兼顾所有的情况,那我可以设置多个支持度值啊,每个种类项都有一个最小支持度阈值,然后一个频繁项的最小支持度阈值取其中项集元素中的最小支持度值作为该项集的最小支持度值。这样的话,如果一个频繁项中出现了稀有项集,则这个项集的最小支持度值就会被拉低,如果又有某个项都是出现频率很高的项构成的话,则支持度阈值又会被拉高。当然,如果出现了一个比较难变态的情况就是,频繁项中同时出现了稀有项和普通项,我们可以通过设置SDC支持度差别限制来限制这种情况的发生,使之挖掘的频繁项更加的合理。通过这里的描述,你就可以发现,当mis最小支持度阈值数组的个数只有1个的时候,ms-apriori算法就退化成了Apriori算法了。

1…161718
Zhenghan He

Zhenghan He

171 日志
7 分类
1 标签
GitHub E-Mail
© 2020 Zhenghan He
由 Hexo 强力驱动 v3.7.1
|
主题 – NexT.Pisces v6.4.2