算分-DESIGN THECHNIQUES

Divide-and-Conquer:

  教材中是用快排作为例子介绍分治算法的,主要的是几个式子:

  最坏情况下的快排:T(n) = n + T(n-1)

  最好情况下的快排:T(n) = n + 2*T((n-1) / 2)

  随机情况下的快排:T(n) = n + 1/n * sum(T(i) + T(n-1-i)) for i = 0,1,2,...,n-1

  值得一提的是一个尾递归的问题,如果是带有尾递归的话,调用栈的空间占用会达到n的规模,但是取消尾递归且每次调用较小的那一段,调用栈的空间只需要logn的规模,见代码:

 1 void Quicksort(int l, int r){
 2     if(l < r){
 3         int m = split(l, r);
 4         Quicksort(l, m - 1);
 5         Quicksort(m + 1, r);
 6         //有尾递归
 7     }
 8 }
 9
10 void Quicksort(int l, int r){
11     int i = l, j = r;
12     while(i < j){
13         int m = split(i, j);
14         if(m - i < j - m){
15             Quicksort(i, m - 1);
16             i = m + 1;
17         }
18         else{
19             Quicksort(m + 1, j);
20             j = m - 1;
21         }
22     }//取消尾递归
23 }

Prune-and-Search:

  教材中用了一个寻找第k大/小的数作为例子,首先如果k = 1是很简单的,问题就在于怎么对其他的k进行处理。

  一个比较直观的想法是用快排的思路进行处理,见下:

int rSelect(int l, int r, int i){
    int q = rSplit(l, r);
    int m = q - l + 1;
    if(i < m){
        return rSelect(l, q - 1, i);
    }
    else if(i == m) return q;
    else{
        return rSelect(q + 1, r, i - m);
    }
}

  这个算法实际上就是一个分治的想法,不难发现T(n)这个复杂度函数是单调递增的,所以可以借助之前快排的分析方法得到T(n)是O(n)的,要注意的T是平均复杂度。那么对于最差的情况,分裂的树还是有可能很不均匀,此时T(n)就会达到n^2的规模,实际上是有办法来解决这件事情的。

  用中位数的想法+快排的想法来做,把n个元素分为ceil(n/5)个组,每个组最多5个元素,不妨设分成了m组,第i组有中位数xi,x1~xm有中位数y,然后对y进行快排的split操作,由于至少有3/10n个元素小于等于y,至少有3/10n个元素大于等于y,所以最差情况下的T也会满足:T(n) <= n(split带来的)+ T(n/5)(找中位数的中位数) + T(7/10n) (左/右任意一边小于等于7/10n个)

  由于1/5 + 7/10 < 1所以可以得到T(n)是O(n)的,此时的T(n)是关于寻找第k大的数的最坏的复杂度!

Dynamic Programming:

  动态规划是在之前两种的方法下产生的,目的是为了减少子问题的重复计算。教材里是用单词编辑距离来作为例子的,两个单词之间的编辑距离定义成通过删除某个字母,替换某个字母,插入某个字母的操作使得两个单词一样的总操作的次数。例如FOOD和MONEY之间的距离是4,FOOD-MOOD-MOND-MONE-MONEY,总共4此操作。但是对于一个比较长的单词就比较难以下手,设两个单词分别是A[1,n]和B[1,m]我们引入一个E(i,j)来记录A[1,i]和B[1,j]之间的最少移动次数,且我们的方法一直是从A变化到B(这个是无所谓的)。

  这里要提一下最优子结构的问题,教材里有一个比较不错的想法是用反证法来说明,就是E的一系列操作都是最优的,否则去掉最后一步剩下的如果非最优的可以进行替换得到矛盾(这里是一个理解的方法)。然后就是递推关系的建立了。比如E(i,0) = i, E(0,j)= j; 删除时:E(i,j) = E(i-1,j) + 1;插入时:E(i,j) = E(i,j-1) + 1;替换时:E(i,j) = E(i-1,j-1) + P(i,j),其中P(i,j) = I[A[i] == B[j]],是一个逻辑变量。这样一来就可以进行递推了。写个简陋的程序:

 

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 int main(){
 5     int E[20][20];
 6     char s[20], t[20];
 7     cin >> s >> t;
 8     int lens = strlen(s), lent = strlen(t);
 9     E[0][0] = 0;
10     for(int i = 1; i <= lens; i++){
11         E[i][0] = i;
12     }
13     for(int j = 1; j <= lent; j++){
14         E[0][j] = j;
15     }
16     for(int i = 0; i < lens; i++){
17         for(int j = 0; j < lent;j++){
18             int m = (s[i] != t[j]);
19             E[i + 1][j + 1] = min(E[i][j + 1] + 1, min(E[i + 1][j] + 1, E[i][j] + m));
20         }
21     }
22     for(int i = 0; i <= lens; i++){
23         for(int j = 0; j <= lent; j++){
24             cout << E[i][j] << ‘,‘;
25         }
26         cout << endl;
27     }
28     return 0;
29 } 

  这样一来,我们就可以得到整个E的表了,同时我们可以通过比较E[i][j]和之前的E的大小关系来确定当前步是进行了哪一步操作(插入、删除、替换)。

Greedy Algorithms:

  贪心算法的核心在于贪心,也就是只顾眼前的最优,不管将来,但是最重要的是去证明可行性(也就是正确性)。

  第一个例子是关于任务选择,每个任务有开始时间si和结束时间fi,现在要你尽可能的选择出多的任务使得任务之间的时间不会有交集。想法是先按fi排序,然后先选择1号任务,再顺序找出第一个si>f1的任务,把结束时间定成fi,再依次找出开始时间大于fi的,依次类推,伪代码如下:

1 sort(A + 1, A + n, f_lower);
2 c = 1, S = {1}
3 for i = 1 to n:
4     if f[i] >= s[c]:
5         S = S U {i}
6         c = i
7 return S  

  这样子就可以找到整个S了,为什么呢?我们只需要考察正确答案的第一个任务a即可,设我们选择的任务是b,由于b结束时间小于等于a,如果b的开始时间大于等于a的结束时间,就可以加入a,矛盾!否则可以删除b而加入a不影响最优解的数量,所以可以证明我们的选择是可行的。复杂度是O(nlogn)。

  第二个例子是二叉哈夫曼树,首先构造哈夫曼树的方法是每次都选择最小权重的两个节点,然后删除两个节点,生成一个新节点,权重为两个旧节点的和。至于正确性的证明分两步:

  第一步,证明命题:如果T是一棵满二叉树,P(T)是它的带权外部路径长度,S(T)是所有节点的集合,其中u,v有最小的权重,我们可以构造一棵树T‘,满足下面三个条件,1)S(T‘) = (S(T) - {u,v})U{k},其中k是由u,v合并得到的,2)w(k) = w(u) + w(v), 3)P(T‘) <= P(T) - w(u) - w(v),当且仅当u,v为兄弟时取等号。

  第二步时用归纳法的方法来证明对于任何的节点集合按照上述方法产生的哈夫曼树的外部权重路径最小。(注意用归纳法证明)

原文地址:https://www.cnblogs.com/zyna/p/12235579.html

时间: 2024-08-30 08:08:33

算分-DESIGN THECHNIQUES的相关文章

Lucene TF-IDF 相关性算分公式

转自: http://lutaf.com/210.htm Lucene在进行关键词查询的时候,默认用TF-IDF算法来计算关键词和文档的相关性,用这个数据排序 TF:词频,IDF:逆向文档频率,TF-IDF是一种统计方法,或者被称为向量空间模型,名字听起来很复杂,但是它其实只包含了两个简单规则 某个词或短语在一篇文章中出现的次数越多,越相关 整个文档集合中包含某个词的文档数量越少,这个词越重要 所以一个term的TF-IDF相关性等于 TF * IDF 这两个规则非常简单,这就是TF-IDF的核

Lucene TF-IDF 相关性算分公式(转)

Lucene在进行关键词查询的时候,默认用TF-IDF算法来计算关键词和文档的相关性,用这个数据排序 TF:词频,IDF:逆向文档频率,TF-IDF是一种统计方法,或者被称为向量空间模型,名字听起来很复杂,但是它其实只包含了两个简单规则 某个词或短语在一篇文章中出现的次数越多,越相关 整个文档集合中包含某个词的文档数量越少,这个词越重要 所以一个term的TF-IDF相关性等于 TF * IDF 这两个规则非常简单,这就是TF-IDF的核心规则,第二个的规则其实有缺陷的,他单纯地认为文本频率小的

Solr相似度算法一:Lucene TF-IDF 相关性算分公式

Lucene在进行关键词查询的时候,默认用TF-IDF算法来计算关键词和文档的相关性,用这个数据排序 TF:词频,IDF:逆向文档频率,TF-IDF是一种统计方法,或者被称为向量空间模型,名字听起来很复杂,但是它其实只包含了两个简单规则 某个词或短语在一篇文章中出现的次数越多,越相关 整个文档集合中包含某个词的文档数量越少,这个词越重要 所以一个term的TF-IDF相关性等于 TF * IDF 这两个规则非常简单,这就是TF-IDF的核心规则,第二个的规则其实有缺陷的,他单纯地认为文本频率小的

UnixBench算分介绍

关于如何用UnixBench,介绍文章很多,这里就不展开了.这里重点描述下它是如何算分的. 运行参数碰到很多客户,装好后,直接./Run,就把结果跑出来了,然后还只取最后一个分值,比谁高谁低.下面列一下4C8G的结果: Benchmark Run: 一 6月 25 2018 20:25:47 - 20:54:194 CPUs in system; running 1 parallel copy of tests Dhrystone 2 using register variables 30971

常用搜索引擎的算分,你get了嘛?

搜索引擎发展至今,已公布了多种算法.作为SEOER的你,还不懂,就out啦.懂了不会用,也是然并卵的一种行为.了解算法知识并不懂得如何把算法实践于SEO工作的你,还是处于学生思维,是时候该升级了.且听我介绍这9个算法及用法 NO.1 绿萝算法 算法内容:为了打击买卖外链.批量群发外链的行为.目的,避免站长不用心做用户体验,纯粹的利用搜索引擎漏洞投机取巧,影响搜索引擎自身用户体验.主要打击的网站类型有,超链中介.出售链接网站.购买链接的网站. 实操说明:还不知死活,拼命买卖外链的小伙伴,赶紧收手吧

算分-Searching

Binary Search Trees: BST树里首先讲了插入删除查找等操作,比较常规.查找:最差O(n),最好O(logn),平均O(logn):插入:成功的插入平均O(logn),最差也是O(n):删除里有三种情况,对于一次成功的删除,待删除的结点v的子结点个数只可能是0.1.2,如果是0的话就直接删除无妨,是1的话就把其子结点作为待删除结点的父节点的子结点(原话是:make that child the child of the parent of v),如果有2个子结点的话,就从左子树

算分-PRIORITIZING

Heaps and Heapsort: 堆是一种快速访问最大优先级元素的数据结构,这是快速实现选择排序的基础,但是总体来说平均速度比快排要慢一点,不过其最坏情况和平均情况是差不多的. 首先是堆的定义,每个结点比它的孩子结点都小(所以父节点最小),或者每个结点都比它的孩子结点大(父节点最大),把A[1,n]看做一个堆,i的两个孩子结点分别是2i和2i+1,这个非常方便实用.接着是删除堆的最小值(最大值同理),想法就是先删除最小值,然后把最后一个元素放到根节点并且不断sift-down,这个是一个结

算分-NP COMPLETENESS

Easy and Hard Problems: NP完全理论是为了在可处理的问题和不可处理的问题之间画一条线,目前最重要的问题是是否这两者是本质不同的,而这个问题目前还没有被解决.典型的结果是一些陈述,比如“如果问题B有一个多项式时间的算法,那么C也会有一个多项式时间的算法”,或者逆否表述为“如果C没有多项式时间的算法,那么B也没有”.第二个表述是说,对于一些问题C以及一个新的问题B,我们先去找是否有这种关系,如果有的话我们可能不想去做问题B,先来看看C.为了描述这些东西,需要不少形式主义的记号

elasticsearch 算分脚本

可以将上一章的脚本,放到elasticsearch/config/scripts 目录下,比如命名为my_script.groovy. 然后使用下面的代码进行调用: "script_score" : { "script" : { "file" : "my_script" } } 这个文件夹的文件60s更新一次,可以随时增加或者删除.  script_score 官方网站上存在性能问题,建议使用Native Java Scrip