zoukankan      html  css  js  c++  java
  • CLRS2e读书笔记—Chapter8

    计数排序C代码如下,显然这货是不能用于字符串排序的(非比较排序只有基数排序可以用来排序字符串):

    #include <stdio.h>
    #include <malloc.h>
    #include <assert.h>
    #include <stdlib.h>
    #define MAX 100         //取值范围k=99,为了保持main形式上的一致性,这里用了宏
    
    void counting_sort(int* A,int p,int r)
    {
        int* C=(int *)calloc(MAX,sizeof(int));
        int* B=(int *)malloc(r*sizeof(int));
        assert( C && B);
        
        for(int i=p;i<r;++i)
            C[A[i]]+=1;
        for(int j=1;j<MAX;++j)
            C[j]+=C[j-1];
        for(int i=r-1;i>=p;--i)
        {
            //Attention here!
            B[C[A[i]]-1]=A[i];
            --C[A[i]];
        }
        for(int j=p;j<r;++j)
            A[j]=B[j];
         //memcpy(A,B,sizeof(int)*r);
    
         free(C);
         free(B);
    }
    int main()
    {
        int A[30];
        int i=0;
        printf("Original:\n");
        for (;i<30;i++)
        {
            A[i]=rand()%(MAX);
            printf("%d\t",A[i]);
        }
        counting_sort(A,0,30);
        printf("\nRearrange:\n");
        for(i=0;i<30;++i)
        {
            printf("%d\t",A[i]);
        }
        printf("\n");
    }

    基数排序的抽象程度比较高(这也是为啥伪代码那么短的原因),它的思想就是对待排序元素进行拆分,然后从低位到高位进行列排序。不同类型的抽取方式差别很大,前面写的大部分C程序,如果改用C++的模板来实现,代码并不用做太多修改,泛型函数和普通函数的差别并不大(只要重载了<操作符)。这里比较妥当的方法是写一个模板类,实现“抽取各位”这个行为。另外,这里有个比较蛋疼的事情,就是数据的类型与数据各位的类型并不一致,不过简单来说可以都认为是char。

    桶排序假设输入平均分布,将输入范围均分为n个“桶”,然后遍历所有元素,将之放入对应的桶中。然后对所有的桶调用插入排序。

    exercises:

    8.1-1 显然是n

    8.1-2 利用

    \[  \int_{0+}^n lgk \le \sum_{k=1}^n lgk \le \int_1^{n+1} lg k \]

    积分左端可得。

    8.1-3 反证法,假设有一半以上的输入可以通过线性比较运算得出,那么有

    \[ 2^n \ge \frac{n!}{2} \Rightarrow n \ge lg(n!)-1 \]

    而右式显然不成立。

    其余类似。

    8.1-4 hint表示不能直接将n/k个klgk相加。实际上算法是先对子序列排序,然后再将所有子序列的首元素排序,复杂度为(n/k)*klgk+(n/k)*lg(n/k),忽略常数项后仍然是$\Omega(nlgk)$.

    8.2-2 Couting-Sort的稳定性主要是因为line9,假设A中有元素A[i]=A[j](i<j),在line9会先将A[j]放入B[C[A[j]]],然后将C[A[j]]的值减1,这样放入A[i]的时候,A[i]必定在A[j]左侧,因此稳定性成立(似乎归纳法更严格一些)。

    8.2-3 修改后算法不再稳定,除非把line11中的递减改为递增

    8.2-4 这个…算法其实有很多吧,不一定非要用计数排序的思想,直接遍历求和不也行,只有$\Theta(n)$。

    如果用计数排序的思想,就取其伪代码line1 to line 7,然后返回C[b]-C[a-1]即可。

    8.3-2 insertion-sort和merge-sort都是稳定的,而quick-sort和heap-sort都是不稳定的。

    想要保持稳定,必须有一个额外的数组记录其左右顺序,空间消耗为$\Theta(n)$,不过参考答案给的是nlgn,lgn代表的是bits…我搞不清楚为啥必须是lgn bits,这不和具体环境相关么?有人知道的话请告诉我:)

    8.3-4 将这n个整数看成是n进制的两位数,那么使用基数排序+计数排序就可以搞定了(每位的范围都是0~n-1)

    8.3-5 这个,我算的结果是n(1-1/(2d)),倒着算的,最低位最多有n/2堆,每堆元素有两个(如果元素只有一个就不用再建新堆),次低位就是n/4,这样加下去就是

    n/2+n/4+...+n/2d,最后的结果也就是上面那个了…

    8.4-2 最坏情况显然是输入非常不均匀——所有元素都在一个桶里;如果想要改善最坏状况,可以在桶内使用合并排序或者堆排序这些最坏nlgn的算法。

    8.4-3 概率题。E[x2]=1.5,E2[x]=1.

    8.4-4 将单位圆按面积均分成n份,依半径的区间分布是$[0,\sqrt{1/n}],[\sqrt{1/n},\sqrt{2/n}]\cdots [\sqrt{n-1/n},1] $

    problems:

    8-1 本题证明了比较排序的平均情况下界为nlgn

    a>这是决策树的叶节点计算方法…因为n个不同元素的可能排列方式有n!个,所以最终的比较结果也只能到达这n!个结点,且概率是等可能的。

    b>D(T)通过D(RT)和D(LT)到达叶节点的方式共有k条【可以从a中推出这个结论】,所以D(RT)+D(LT)+k=D(T).

    c>根据提示,假设有一颗这样的决策树:其LT可达的叶节点数是$i_0$,其RT可达的叶节点数就是$k-i_0$,那么根据b,D(T)的最小值是

    \[ D(T)_{min}=\min\{D(RT)+D(LT)+k\} \]

    假设d(k)是D(T)的最小值,d(k)的定义是到达k个结点的所有路径总和的最小值,那么d(k)是关于k的函数,根据树的递归结构,D(RT)的最小值应该是关于$k-i_0$的函数,D(LT)就是关于$i_0$的函数,即

    \[D(T)_{min}=\min\{d(i_0)+d(k-i_0)\} \]

    而$i_0$的取值范围就是[1,k-1],所以题设得证。

    d>简单但是通用的方法就是用导数证明单调性。由于i<=k-1,所以也可以直接用$a+b \ge 2\sqrt{ab}$当a=b时取得最小值。

    e>将k=n!代入d中结论可得。

    f>在随机化算法中抽取最短路径分支,然后剪掉同一随机化结点的其他分支,得到一个拥有最短结点的确定算法,该算法的复杂度显然不多于对应的随机化算法。

    8-2 线性时间原地排序的可能性

    a>计数排序(桶排序恐怕不行,因为并非均匀分布)

    b>由于只有两个key,所以一轮快排是可以达到目的的。

    c>插入排序

    d>将这n个记录看做2进制数,进行Radix-sort,那么每一列就是只有0和1的情况了。

    e>遍历A[1-n],在C[0-k]中存放A[i]出现的次数;

      遍历C,对于每一个C[i]依次在A中push_back i个C[i];

      以上算法运行时间为O(n+k),该算法显然是不稳定的。

    8-3 长度不同的数据如何使用radix-sort

    a>①将数据分成负数、0、整数三类——O(n);

      ②准备$\lceil n/2 \rceil$个桶,以位数对以上三类分别再分类【因为对正数而言,长度长的总比长度短的大;负数则相反】O(n);

      ③最后对桶中的数据进行线性排序——O(n)。

    b>①以首字母对数据进行分类(计数排序);

      ②以次字母对所有分类进行再分类;

      ③递归以上过程直至完成排序。

    由于总的字母是n个,因此需要的总时间也就是O(n+m),m是字母的种类(26个+空)

    8-4 配对水壶

    a> simple,对于每一个red,遍历blue得到pair

    b>使用决策树,每次比较的结果是3ge(< > =),所以是三叉树;总的结果是n!个(第一个可能和n个中任一个,第二个则是剩下n-1任一个,依次类推);假设决策树的高度是h,那么有$3^h \ge n!$,解得T(n)=Ω(nlgn)

    c>代码来自参考答案:

    Match-Jugs(R,B)

    if |R|=0

      then return

    if |R|=1

      then let R={r} and B={b}

      output "(r,b)"

      return

    else r←R[Random(1,n)]

      compare r to every jug of B

      $B_<$ ← the set of jugs in B that are smaller than r

      $B_>$ ← the set of jugs in B that are greater than r

      b ← the pair

      compare b to ever jug of A

      $A_<$ ← the set of jugs in A that are smaller than b

      $B_<$ ← the set of jugs in A that are larger than b

      Match-Jugs($A_<,B_<$)

      Match-Jugs($A_>,B_>$)

    显然,算法与插入排序的思想是一致的,其期望运行时间也是Θ(nlgn)

    8-5 如果数组中的以任意元素i开始的k个元素的和小于等于任意从i+x开始的k个元素的和(x>0)那么称数组是k排序的

    a>1排序就是指数组严格递增(严格来说是非递减)

    b>1 2 3 4 5 6 7 8 10 9【随意调换两个相邻的元素貌似都可以】

    c>展开题设中的不等式,消去等式两边的共有项就能得到A[i]<=A[i+k]

    d>根据c中的结论,只需对A[1,k+1,2k+1...],A[2,k+2,2k+2...]...A[k,2k,3k...]分别进行严格排序就可以了

    共k组,每组共n/k个元素,k*(n/k)lgn/k=nlg(n/k)

    e>显然只需对n/k个长度为k个小数组进行排序即:(n/k)*klgk=nlgk

    f>如果k是常数,根据d的结论…

    8-6 合并两个已排序的序列

    a>对于2n个数,划分成两个长度为n列表的方法显然就是$\binom{2n}{n}$,至于排序,和划分并无关系(一旦确定划分,排序是唯一的)

    b>决策树应该是三叉树,叶子数计算相当于在n个数中插入另外n个数的问题,共有n+1个位置,而待插入的数又有不同的分类法,总数应该是

    \[ \binom{n-1}{0}\binom{n+1}{1}+\binom{n-1}{1}\binom{n+1}{2}+\cdots+\binom{n-1}{n-1}\binom{n+1}{n} \]

    也就是$\sum_{i=0}^{n-1}\binom{n-1}{i}\binom{n+1}{i+1}$,这个计算的结果是…抱歉我不会算= =,总之得出叶子的数目m然后用

    $3^h \ge m$是可以算出这个下界的。不严格的推证:决策树最短路径应该是

    \[ <a1:b1> \xrightarrow{\rm =}<a2:b1>\xrightarrow{\rm =}<a2:b2>\rightarrow\cdots<an:bn>\]

    该路径的长度显然是2n-1,符合2n-o(n)的结论

    c>如果两个数无须比较就能确定大小关系,必须利用关系操作的传递性,但是这与两者相邻是矛盾的(除非相等)

    d>长度为2n,所有相邻的元素都必须比较过,即最少需要2n-1次比较才能确定。

  • 相关阅读:
    1.27
    1.25
    Representation Learning with Contrastive Predictive Coding
    Learning a Similarity Metric Discriminatively, with Application to Face Verification
    噪声对比估计(负样本采样)
    Certified Adversarial Robustness via Randomized Smoothing
    Certified Robustness to Adversarial Examples with Differential Privacy
    Dynamic Routing Between Capsules
    Defending Adversarial Attacks by Correcting logits
    Visualizing Data using t-SNE
  • 原文地址:https://www.cnblogs.com/livewithnorest/p/2659962.html
Copyright © 2011-2022 走看看