算法基础-->概率组合

来源:互联网 发布:java 定时 编辑:程序博客网 时间:2024/06/07 18:58

本篇博文将总结数学相关的内容,涉及概率组合的一些算法,比较简单。

求1的个数

问题描述

给定一个32 位无符号整数N,求整数N 的二进制数中1 的个数。

问题分析

方法一

显然:可以通过不断的将整数N 右移,判断当前数字的最低位是否为1,直到整数N0 为止。平均情况下,大约需要16 次移位和16 次加法。

int OneNumber(int n){    int c = 0;    while (n!=0)    {        c += (n & 1);//n末尾最后一位不断和1作与        n >>= 1;//n=n>>1,n右移一位    }    return c;}

方法二

每次将n 最后一个10,能清多少次就说明有多少个1,只需要n&=(n1) 即可。例如:n=1010111101n1=1010111100,两数相与,则必可清除n 最后一位1

int OneNumber2(int n){    int c = 0;    while (n!=0)    {        n = n&(n - 1);//将n的最后一个“1”清零        c++;//每清除一个1,c就加一    }    return c;}

方法三:分治

假定能够求出N 的高16 位中1 的个数a 和低16 位中1 的个数b,则a+b 即为所求。

为了节省空间,用一个32 位整数保存ab

  • 16 位记录a,低16 位记录b
  • (0xFFFF0000&N) 筛选得到a
  • (0x0000FFFF&N) 筛选得到b
  • (0xFFFF0000&N)+(0x0000FFFF&N)>>16

如何得到高16 位和低16 位中1 的个数ab 呢?

  • 分治往往伴随着递归

递归过程:

  1. 如果二进制数N16 位,则统计N88各自 1 的数目 ab,而 ab 用某一个16 位数 X 存储,则使用0xFF000x00FF 分别于X与操作,筛选出 ab;原问题中的数据是 32 位,因此分别需要 20xFF00/0x00FF,即0xFF00FF00/0x00FF00FF

  2. 如果二进制数是 8 位,则统计高 4 位和低 4 位各自1 的数目,使用 0xF0/0x0F 分别做与操作,筛选出高 4 位和低 4 位;原问题中的数据是 32 位,则分别需要40xF0/0x0F,即0xF0F0F0F0/0x0F0F0F0F

  3. 如果是 4 位则统计高 2 位和低 2 位各自 1 的数目,用 0xC/0x3 筛选(高21100 十六进制表示 0xC,低 20011 十六进制表示为 0x3);原问题中的数据是 32 位,故各需要需要 80xC/0x3 ,即 0xCCCCCCCC/0x33333333

  4. 如果是 2 位则统计高 1 位和低 1 位各自 1 的数目,用 0x2/0x1 筛选;原问题中的数据是32 位,(因为在十六进制中,以四位为一个单位,则高 1 位为 1010 即为 0xA,需要 80xA,同理低 10101 即为0x5,也需要 8 个 )故各需要 80xA/0x3,即为0xAAAAAAAA/0x33333333

int HammingWeight(unsigned int n){    //(n & 0x55555555)每相邻两位忽略高位保留低位为1的二进制序列,    //(n & 0xaaaaaaaa)>>1每相邻两位忽略低位保留高位为1的二进制序列并右移1位,高位补零。    //上面两个子序列相加,则为每相邻两位高位和对应低位为1的相加,往前一位进1。    //也就是检查每对相邻的2位有几个1    n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1);    //每相邻的四位有几个1    n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2);    //每相邻的八位有几个1    n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4);    //每相邻的十六位有几个1    n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8);    //32位有几个1    n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16);    return n;} int main(){    int c = HammingWeight(16);    cout << c << endl;}

在采用HammingWeight 方法时,对于任何一个32 位无符号整数N 只需要计算5 次运算即可。

总结与应用

  • HammingWeight 使用了分治/递归的思想,将问题巧妙解决,降低了运算次数。
  • 如果定义两个长度相等的0/1 串中对应位不相同的个数为海明距离(即码距),则某0/1 串和全0 串的海明距离即为这个0/1 串中1 的个数。
  • 两个0/1 串的海明距离,即两个串异或值的1 的数目,因此,该问题在信息编码等诸多领域有广泛应用。

跳跃问题

问题描述

给定非负整数数组,初始时在数组起始位置放置一机器人,数组的每个元素表示在当前位置机器人最大能够跳跃的数目。它的目的是用最少的步数到达数组末端。例如:给定数组A=[2,3,1,1,2],最少跳步数目是2,对应的跳法是:2>3>2

如:2,3,1,1,2,4,1,1,6,1,7最少需要几步

问题分析

这里写图片描述

由上图我们可以看出当前跳的范围为蓝色的 1 范围,下一跳的范围为蓝色的 2 范围。

  1. 初始步数step 赋值为0

  2. 记当前步的控制范围是[i,j],则用k 遍历ij
    计算A[k]+k 的最大值,记做j2A[k] 表示当前位置最远能跳的距离。

  3. step++;继续遍历[j+1,j2]

每一个step 都有当前可跳到的范围,而当前的范围又确定下一个step 的可跳到的范围。每在一个step 遍历当前可跳的范围,确定下一跳的范围。这样总可以找到最短的step 跳到终点。每一个step 可跳的范围内,其step 的值都是相同。这个解题过程类似于广度优先搜索。,每一个step 可跳的范围为一层。

实现代码

int Jump(int* a,int size){    if (size == 1)        return 0;    int i = 0;    int j = 0;//初始可跳的范围即为[i,j]    int k,j2;    int step = 0;    while (j<size)    {        step++;        j2 = j;        for (k = i; k <= j; k++)//遍历当前step可跳的范围来确定下一跳的范围        {            j2 = max(j2, k + a[k]);            if (j2 > size - 1)                return step;        }        i = j + 1;//上一跳的终点的下一个格子为下一跳的起点,注意a[k]为最大可跳的距离,最少可跳一步。        j = j2;//下一跳终点        if (j < i)            return -1;    }    return step;}

Jump问题总结

虽然从代码上看有两层循环,但是我们分析执行过程可知只是从序列头跳到序列尾,时间复杂度只有O(n)

该算法在每次跳跃中,都是尽量跳的更远,并记录j2——属于贪心法;也可以认为是从区间[i,j] (若干结点)扩展下一层区间[j+1,j2] (若干子结点)——属于广度优先搜索

错位排列问题

问题描述

1n 的全排列中,第i 个数不是i 的排列共有多少种?

问题分析

  • 假定n 个数的错位排列数目为dp[n]
  • 先考察数字n 的放置方法:显然,n 可以放在从1n1 的某个位置,共n1 种方法;假定放在了第k 位。
  • 对于数字k
    要么放置在第n
    要么不放置在第n 位。

这里写图片描述

数字k放置在第n位

相当于数字k 和数字n 交互位置后,其他n2 个数字做错位排列,因此有dp[n2] 种方法。

这里写图片描述

数字k不放置在第n位

将数字k 暂时更名为n (这是可以做到的:因为真正的n 已经放在第k 位上,真正的n 不再考虑之列),现在需要将1k1 以及k+1nn1 个数放置在相应位置上,要求数字和位置不相同!显然是n1 个数的错位排列,有dp[n1] 种方法。

这里写图片描述

错位排列递推公式

综上,dp[n]=(n1)(dp[n1]+dp[n2])(n1) 表示起始时,数字n(n1) 个可放置的位置。

初值

只有1 个数字,错位排列不存在,dp[1]=0
只有2 个数字,错位排列即交换排列,dp[2]=1

则递推公式为:

这里写图片描述

实现代码

int dislocationSorting(int n){    int* dp = new int[n];    dp--;    dp[1] = 0;    dp[2] = 1;    for (int i = 3; i <= n; i++)        dp[i] = (i - 1)*(dp[i - 1] + dp[i - 2]);    return dp[n];}int main(){    int c = dislocationSorting(2);    cout << c << endl;}
原创粉丝点击