进阶数学问题

这里称作进阶数学问题,其实着实是因为将线性方程组和期望的问题混合到一起总结,有些不太好起名字,所以采用了进阶数学问题。说是进阶,我觉得期望可能用的稍微多一点,还算是进阶,线性方程组直接用的应该挺少的,不过这里会给出一道利用线性方程组计算解的问题,个人感觉还是挺厉害的一道题。
一、线性方程组
关于线性方程组的解法,线性代数里面应该学了挺多了,有常见的高斯消元法,还有一些更快的算法,这里就不再多说了,如果有兴趣的话可以详细地去查一查。下面给出一般情况下都够用的高斯消元法:

const double EPS = 1E-8;
typedef vector<double> vec;
typedef vector<vec> mat;

// A是系数矩阵,b是结果向量
vec gauss_jordan(const mat& A,const vec& b){
    int n = A.size();
    mat B(n,vec(n+1));     //用矩阵B来存储系数矩阵和结果向量,方便之后统一消元
    for(int i = 0;i < n;i++){
        for(int j = 0;j < n;j++){
            B[i][j] = A[i][j];
        }
    }
    for(int i = 0;i < n;i++)B[i][n] = b[i];
    for(int i = 0;i < n;i++){
        /*这里pivot是想找到当前要消掉的这一列中,系数绝对值最大的那个,
        一方面保证当前的主元(就是用来消的系数)不为0,另一方面有人证明这
        样可以更稳定。*/
        int pivot = i;
        for(int j = i;j < n;j++){
            if(abs(B[j][i]) > abs(B[pivot][i]))pivot = j;
        }
        swap(B[i],B[pivot])  //如果不放心swap函数,可以自己写一个,不复杂
        
        //<EPS相当于系数是0了,经过上面的pivot,主元找最大的都是0,那么要不
        //就是无解,要么就是无穷个解了
        if(abs(B[i][i]) < EPS)return vec();
        for(int j = i+1;j <= n;j++)B[i][j] /= B[i][i];
        for(int j = 0;j < n;j++){
            if(i != j){
                for(int k = i+1;k <= n;k++)B[j][k] -= B[j][i] * B[i][k];
            }
        }
    }
    vec x(n);
    for(int i = 0;i < n;i++)x[i] = B[i][n];
    return x;
}

一道例题,算是线性方程组向期望过渡的题:
有一个N*M大小的格子,从(0,0)出发,每一步朝着上下左右4个各自中可以移动的格子等概率移动。另外有一些格子中有石头,因此无法移至这些格子。求第一次到达(N-1,M-1)格子的期望步数。题目假定至少存在一条从(0,0)出发到(N-1,M-1)的路径。

书中给出的期望公式:E(x,y) = \tfrac{1}{4}E(x-1,y) + \tfrac{1}{4}E(x+1,y) + \tfrac{1}{4}E(x,y-1) + \tfrac{1}{4}E(x,y+1) + 1
这里的公式其实来的很奇怪,这里的(x,y)与(x-1,y)等周围四个格子并没有明显的等式关系,甚至这里是两个变量的期望,为什么可以这样求?一种想法可能是,E(x,y)用p1(x1,y1)+p2(x2,y2)...这种,概率乘取值这种方法来求,但是这里显然不是,因为E(x-1,y)必然不是取值,它也是一个期望。上面也说了,它们之间也没有明显的加和关系,所以也不能直接拆开。综上,这个公式究竟是哪儿来的?
我翻了很多博客,没有人对这里有过讲解,仿佛二维图表中这样算期望已经被默认了。我后来也想了很多,只能有一个不敢保证对的方法,帮助自己和大家理解,说明这种方法确实是一个可以推广的方法。
对于这里,上下左右是等可能的,所以各是\tfrac{1}{4},就算到不了,它也占着\tfrac{1}{4}的概率。接下来,设X11,X12...都是代表可以到达X1的路径,X1代表实际的(x-1,y),然后 X2等也有相应的含义。那么,E(X)= [p11 * (X11+1) + p12 * (X12+1)+...+p1n * (X1+1)+p21 * (X21+1)+...+p2n * (X2n+1)+...p31 * (X31+1)+...+p4n * (X4n+1)。

上面的公式确实挺乱。。所以我也不打算再敲公式了,不如这里口糊。。首先,这里肯定可以整体提个\tfrac{1}{4}出来,于情,它们最后都要从各自的位置,转移到(x,y),这一步转移的概率为\tfrac{1}{4};于理,任何数都可以提个\tfrac{1}{4}出来啊。。因为提个\tfrac{1}{4}就相当于乘了个4。接下来,提完以后的部分,先把+1都拿出来,可以凑出4(p11+...+p1n)这种式子,把4补进去,其实就是从起点出发,通过各条路径到达(x-1,y)的概率和,而概率和这个东西一定是等于1的,不管怎么计算,概率之和为1这一条铁定不会变,所以这一块凑起来是1;然后剩下的部分,其实很明显就是到达(x-1,y)的期望了,(概率*取值的形式)所以就变成了公式的样子。

上面的口糊也比较严重,不过我相信是可以理解的,并且最终是可以得到在格子上运动时,期望的公式的。所以这时我们把这个公式当作已知条件,接着往下探索。此时没有任何一个点的期望是已知的,所以直接求期望也不太现实,在这里,如果将每个E(x,y)都当成一个变量,也就是有N * M个变量的话,其实每一个变量都能列出一个方程。

对于是石头的格子,E(x,y) = 0。可以到达终点的格子,可以列出E(x,y) = E(x-1,y) + E(x+1,y) + E(x,y-1) + E(x,y+1) + 1的等式。如果想要优化的话,其实还可以提前把无法到达终点的格子找出来,然后它们的E(x,y)其实也是0,保险起见最好提前求一下。列出线性方程组之后,交给前面的gauss_jordan来处理就好了,返回值就是每个点的期望。完结撒花,接下来上代码:

char grid[max_n][max_m+1];    //+1是为了存字符串末尾的'\0'
int n,m;

bool can_goal[max_n][max_m];      //记录每个点是否可以到达终点
int dx[4] = {-1,1,0,0}, dy[4] = {0,0,-1,1};

// 搜索可以到达终点的点,记录
void dfs(int x,int y){
    can_goal[x][y] = true; //这里敢直接标成true,其实是因为搜索是从终点开始的逆向搜索
    for(int i = 0;i < 4;i++){
        int nx = x+dx[i],ny = y+dy[i];
        if(0 <= nx && nx < N && 0 <= ny && ny < M && !can_goal[nx][nyh] && grid[nx][ny] != '#'){
            dfs(nx,ny);
        }
    }
}

void solve(){
    mat A(N*M, vec(N*M, 0));     //mat是gauss_jordan定义的类型
    vec b(N*M, 0);
    for(int x = 0;x < N;x++){
        for(int y = 0;y < M;y++){
            can_goal[x][y] = false;      //初始化数组
        }
    }
    dfs(N-1,M-1);
    
    /* A是一个(N*M,N*M)的矩阵,也就是系数矩阵。第x*M+y行的方程代表的是(x,y)
    位置建立的方程。如果E(x,y) = 0,那么该行第x*M+y个变量t,t=0即可。如果E(x,y)
    不是0,就用期望的那个等式,将该行相邻位置的变量系数都设置一下,并且更新B向量.*/
    for(int x = 0;x < N;x++){
        for(int y = 0;y < M;y++){
            if((x == N-1 && y == M-1) || !can_goal[x][y]){
                A[x*M+y][x*M+y] = 0;
                continue;
            }
            int move_grid = 0;
            for(int k = 0;k < 4;k++){
                int nx = x+dx[k],ny = y+dy[k];
                if(0 <= nx && nx < N && 0 <= ny && ny < M && !can_goal[nx][nyh] && grid[nx][ny] == '.'){
                    A[x*M+y][nx*M+ny] = -1; //4E(x,y) - E(x-1,y) - E(x+1,y) - E(x,y-1) - E(x,y+1) = 4
                    move_grid++;
                }
            }
            b[x*M+y] = A[x*M+y][x*M+Y] = move_grid;
        }
    }
    vec res = gauss_jordan(A,b);
    printf("%.8f\n",res[0]);
}

这道题最难的,其实我个人觉得就是推导二维格子上的期望公式。如果有了这个,想到了列线性方程组求解,那么这个题就可以做了。

二、期望总结
在一些考数学的算法题目中,还比较喜欢考一些期望问题。这里我将期望问题,按照自己的想法分为三类。第一类是很偏数学的考点,就是考察对于期望的理解,以及期望公式的运用,比如E(x) = p1 * x1 + p2 * x2 + ... + pn * xn,这是期望最基本的定义;再比如说E(x+y) = E(x) + E(y),两个随机变量独立时,E(xy) = E(x) * E(y)。第二类就是上面那道题目的类型,在二维格子中的期望公式,这个真的是需要有见过这类型的题目,知道这里的期望公式到底是怎么用的,才能做出来,一般情况下,都是列出线性方程组来求解的。第三类,是这里想要重点说一下的类型,是算法导论上的指示器随机变量,用它来求解很多期望问题,真的有种神兵利器的感觉。

首先先来一个问题,来感受一下指示器随机变量是什么:
抛n次硬币,求正面向上的次数?
ok这道题并不难,甚至可以说非常简单。求法也比较通俗易懂,用定义的话,正面向上i次的概率pi = \tfrac{1}{2^n} * Cin,所以E(x) = \tfrac{1}{2^n} * sum_{i=1}^n Cin * i,右边的累加,打开看一下的话,很容易发现其实就是 sum_{i=1}^nCin-1,也就是2n-1,与左边相乘,最后的结果是\tfrac{n}{2}

如果再换一种思路呢?这里其实很容易想到,每次抛掷硬币,其实是个独立重复的过程,相互之间并不影响,那么把每一次的期望加起来,不就是n次的期望了嘛。接下来,每一个抛掷就是一个两点分布,所以E(x) = p * 1 + (1-p) * 0 = p。所以最终E(X) = n*p = \tfrac{n}{2}

其实分析到这里,指示器随机变量的两个核心问题已经都分析出来了,1. 将一个分n次进行的随机变量,分解为n个进行一次的随机变量。2. 每一个进行一次的随机变量服从两点分布,所以E(x) = p。 合起来,E(X) = n*p。

到此,指示器随机变量的核心已经说完了。不过还不能完结撒花,我们需要了解为什么可以这样用,以及究竟怎么用,才能真正的在需要的时候用出来。如果只是用它来解决抛硬币的问题,这个方法有点过于鸡肋。。

关于指示器随机变量的第一个问题,为什么E(x) = n * E(x)。这里其实是需要看X这个随机变量,是否可以拆成n个子过程的。如果X本身可以拆成n个子过程,那么X = n*x。(这样表达不准确,其实应该是X = sum_{i=1}^nxi。然后代入期望的公式中,E(X) = E(sum_{i=1}^nxi) = sum_{i=1}^nE(xi)。其实我认为这才是指示器随机变量真正的核心,后面的两点分布,并不是每一次都能转化的出来的,但是一旦有了它,很多问题的难度就可以直接降一个level。

下一步的两点分布,通常定义为,发生的话,取值为1,未发生,取值为0,这里其实也没有太多要说的。好了,接下来看一些例题,这是比较有意思的地方。(例题参考了人家的博客,如有冒犯,请联系我,我会马上整改)

例一.
抛掷一枚骰子n次,求骰子和的期望。
因为这n次,其实可以拆成n个一次,与抛硬币一样,所以E(X) = sum_{i=1}^nE(xi),每一次投掷骰子,很容易计算期望是3.5,所以结果是3.5n。这里就是一个,E(xi)不是两点分布的例子。

例二.
一个随机的n排列,求所有排列中,满足i = a[i]的元素数目的期望。

这道题就没那么简单了,首先第一个问题,能不能把X拆分?这里的拆分方案,可能是Ann种排列,计算每一种排列的期望。但是其实拿出每一次排列的话,这个时候就不是在算期望了,而是能确切地得出有几个i = a[i]了,而且这样计算也挺难的,我们研究指示器随机变量的做法。

i = a[i],无非有n种可能,第一个位置上的值是1,第二个位置上的值是2...第n个位置上的值是n。这样可以顺利地将X拆成n个小的随机变量。根据公式,求出每个子随即变量的期望,就可以累加出X的期望。接下来的问题是,求每个位置上a[i] = i的期望。很明显这个问题要比前面的问题都难很多,这个问题的n个位置之间,是有相关性的,而不像前面的抛硬币,掷骰子这些,是重复独立实验。

计算各个位置a[i] = i的相关性,如果开始考虑位置之间的相关性,那这个题基本就做出来没啥希望了。。这里要用期望的眼光来分析这件事,我们这里算的,是当前位置E(a[i] = i),并没有说求E(x|y)这种考虑条件的东西(事实上这种东西我也没见过),所以我们完全可以单独拿出i这个位置来分析,a[i] = i时,有n-1的全排列种情况,一共有n的全排列种情况,所以期望 = 1*(n-1)! / n!,也就是1/n。然后把n个位置累加起来,其实最后的结果就是1.(这里真的挺绕的)

例三.
有n种礼券,每次发一张,拿到每张的概率都是一样的,设X次可以领到n种不同礼券的话,求X的期望。

如果按照上面那道题的想法的话,有一个直接的猜测,计算每一张礼券的期望,然后再合起来,就是所有礼券的期望(还是相当于n张礼券是没有任何关联的)。但是这样行不通!这是这道题最大的特点,它事件的总数是不确定的。(这里拿一次礼券就算一次事件)第一次拿礼券,肯定是一张没拿到过的礼券;然后第二次拿,拿到之前拿到过的,这次相当于是没用的;理论上来说,是存在一直拿不完n张不同的礼券,所以要一直拿的情况的。所以这里说,事件的总数是不确定的。

上面那道题,可以不考虑任何顺序的问题,分析到i时,就只考虑i的情况,本质上是因为总共只有n!种情况,然后可以直接拿出来处理;但是这里是不可能直接拿出当前的情况与总的情况来处理的,所以一定不能单独分析第i张礼券拿的时候究竟是什么情况的,这里一定需要考虑之前的影响,来一步步地计算到n的。

这里说一下正确的思路,要拿到第一张不同的礼券,必然只需要1次;然后拿第二张,E(x2) = 1 * \tfrac{n-1}{n} + 2 * \tfrac{1}{n} * \tfrac{n-1}{n} + 3 * (\tfrac{1}{n})^2 * \tfrac{n-1}{n} + m * (\tfrac{1}{n})m-1 * \tfrac{n-1}{n}。要求解这个式子,其实只要知道一下几何分布的结论:1 * (1-p)0 * p + 2 * (1-p)1 * p + ... + m * (1-p)m-1 * p = \tfrac{1}{p}。上面那个式子其实就是一个几何分布,所以E(x2) = \tfrac{n}{n-1},...E(xn) = \tfrac{n}{1},综上E(X) = E(x1) + E(x2) + ... + E(xn) = n(\tfrac{1}{1} + \tfrac{1}{2} + ... + \tfrac{1}{n}),这个式子,如果是10^8以内的话,可以直接遍历一遍得到结果。如果n是1018这个数量级的话,那肯定不能遍历了。这个问题一般是用\tfrac{1}{1} + \tfrac{1}{2} + ... + \tfrac{1}{n} = ln(n+1) + r来实现。r是欧拉常数,一般取0.5772156649够了。

其他还有指示器随机变量的例题,比如说雇佣问题,上楼梯问题,坐公交车问题等,花样很多,这里就不一一说明了。个人认为,如果考期望问题,逃不过上面这三类;如果考指示器随机变量,大体就上面两类,即总方案数确定和总方案数不确定两种。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354