类似斐波那契数列的递归 与 蓄水池问题

彩蛋题

一个棋盘,10行9列,一个马棋子(走日),走到(x,y)走k步有多少种走法

递归写法
//马从(0,0)出发,还有k步,并且一定要走完,最终到达x,y位置的方法数有多少
	public static int f(int x,int y,int k)
	{
		//k步用完时还没到(0,0)则返回0,k步用完正好到(0,0)代表走到
		if(k==0)
		{
			return x==0&&y==0?1:0;
		}
		//base case 越界情况
		if(x<0||x>9||y<0||y>8)
		{
			return 0;
		}
		
		return  f(x-2, y-1, k-1)
				+f(x-1, y-2, k-1)
				+f(x+2, y+1, k)
				+f(x+1,y+2,k-1)
				+f(x+2, y-1, k-1)
				+f(x+1, y-2, k-1)
				+f(x-1, y+2, k-1)
				+f(x-2, y+1, k-1);
	}

//看递归算法,有三个参数,代表是用三维表
//动态规划本质是

//初始化
dp[0][0][0]=1
dp[…][…][0]=0

其他全部为0

//状态转移方程
dp[i][j][k]=dp[i-1][j-2][k-1]+dp[i-2][j-1][k-1]+dp[i+2][j+1][k-1]+dp[i+1][j+2][k-1]
+dp[i-1][j+2][k-1]+dp[i-2][j+1][k-1]+dp[i+1][j-2][k-1]+dp[i+2][j-1][k-1]

//每个dp都要边界检测,越界则为0

//更新上,就是一个正方体,由最底面,逐层向上递推,每一层只依赖下面的一层。凭借此,可以写出循环,并且明确应该初始化的值

动态规划写法
public static int ways2(int x,int y,int k)
	{
		int[][][] dp=new int[10][9][k+1];//0~k
		for(int level=1;level<=k;level++)
		{//x,y怎么变化都没问题,其只依赖下面的一层
			for(int i=0;i<10;i++)
			{
				for(int j=0;j<9;j++)
				{
					dp[i][j][k]=getvalue(i-1, j-2, k-1, dp)
					           +getvalue(i-2, j-1, k-1, dp)
					           +getvalue(i+2, j+1, k-1, dp)
					           +getvalue(i+1, j+2, k-1, dp)
					           +getvalue(i-2, j+1, k-1, dp)
					           +getvalue(i+2, j-1, k-1, dp)
					           +getvalue(i+1, j-2, k-1, dp)
					           +getvalue(i-1, j+2, k-1, dp);
				}
			}
		}
		return dp[x][y][k];
	}
	public static int getvalue(int x,int y,int k,int[][][] dp)
	{
		//如果x,y越界,则我给你0,如果没越界,我给你表里正常值
		if(x<0||x>9||y<0||y>8)
			return 0;
		else
			return dp[x][y][k];
	}

斐波那契数列

1,1,2,3,5,8…
一般就是O(n),一项项列。
但可以优化成O(LOGN)

F ( N ) = F ( N − 1 ) + F ( N − 2 ) F(N)=F(N-1)+F(N-2) F(N)=F(N1)+F(N2)
利用线性代数的方法:矩阵分解

∣ F ( 2 ) , F ( 1 ) ∣ |F(2),F(1)| F(2),F(1)
∣ F ( 3 ) , F ( 2 ) ∣ = ∣ F ( 2 ) + F ( 1 ) , F ( 1 ) ∣ = ∣ F ( 2 ) , F ( 1 ) ∣ ∗ ∣ 1 0 ∣ ∣ 1 1 ∣ |F(3),F(2)|=|F(2)+F(1),F(1)|\\ =|F(2),F(1)|*\begin{matrix} |1&0|\\ |1&1|\\ \end{matrix} F(3),F(2)=F(2)+F(1),F(1)=F(2),F(1)1101

∣ F ( 4 ) , F ( 3 ) ∣ = ∣ F ( 3 ) + F ( 2 ) , F ( 2 ) ∣ = ∣ F ( 3 ) , F ( 2 ) ∣ ∗ ∣ 1 0 ∣ ∣ 1 1 ∣ = ∣ F ( 2 ) , F ( 1 ) ∣ ∗ ∣ 1 0 ∣ ∣ 1 1 ∣ ∗ ∣ 1 0 ∣ ∣ 1 1 ∣ . . . |F(4),F(3)|=|F(3)+F(2),F(2)|\\ =|F(3),F(2)|*\begin{matrix} |1&0|\\ |1&1|\\ \end{matrix}\\ =|F(2),F(1)|*\begin{matrix} |1&0|\\ |1&1|\\ \end{matrix}*\begin{matrix} |1&0|\\ |1&1|\\ \end{matrix}\\ ... F(4),F(3)=F(3)+F(2),F(2)=F(3),F(2)1101=F(2),F(1)11011101...

∣ F ( n ) , F ( n − 1 ) ∣ = ∣ F ( 2 ) , F ( 1 ) ∣ ∗ D 的 n − 2 次 方 |F(n),F(n-1)|=|F(2),F(1)|*D的n-2次方 F(n),F(n1)=F(2),F(1)Dn2

o(log2n)

运算的复杂度由D的n-2次方来决定了

理解的小tips,如何快速算

1 0 75 = 1 0 100101 1 2 = 1 ∗ 1 0 1 ∗ 1 0 2 ∗ 1 0 8 ∗ 1 0 64 10^{75}=10^{1001011_{2}}\\ =1*10^1*10^2*10^8*10^{64}\\ 1075=1010010112=11011021081064

//每轮更新,t乘以自己本身
long t=10
int n=1;
for(int i=0;i<7;i++)
{
    if(num[i]==1)
    {
        n=n*t;
    }
    t=t*t;
}
//将复杂度简化到O(log2(75))
那么将n-2二进制化,也可以简化了

以下代码实现了O(LOG2N)得到斐波那契第n个

public int f3(int n)
	{
		if(n<1)
		{
			return 0;
		}
		if(n==1||n==2)
		{
			return 1;
		}
		int[][] base=new int[][]{{1,1},
								 {1,0}};
		int[][] res=matrixpower(base,n-2);
		return res[0][0]+res[1][0];
		
	}
	//算出t矩阵的n次方
	public int[][] matrixpower(int[][] t,int n)
	{
		int[][] res=new int[t.length][t[0].length];
		for(int i=0;i<res.length;i++)
		{
			for(int j=0;j<res[0].length;j++)
			{
				res[i][j]=1;
			}
		}
		int[][] tmp=t;
		//注意十进制快速转成二进制的写法
		for(;n!=0;n>>=0)
		{
			if((n&1)!=0)
			{
				res=mulimatrix(res, tmp);
			}
			tmp=mulimatrix(tmp, tmp);
			
		}
		return res;
	}
	//两个矩阵乘完之后的结果返回
	public int[][] mulimatrix(int[][] m1,int[][] m2)
	{
		//m1.length为行数
		//m1[0][...] 为列数
		int[][] res=new int[m1.length][m2[0].length];
		for(int i=0;i<m1.length;i++)
		{
			for(int j=0;j<m2[0].length;j++)
			{//前两层循环对结果赋值
				for(int k=0;k<m2.length;k++)
				{
					res[i][j]=m1[i][k]*m2[k][j];
				}
			}
		}
		return res;
	}

推广

这样的递推式都可以通过O(log2n)的时间复杂度解决
F ( n ) = C 1 F ( n − 1 ) + C 2 F ( n − 2 ) + . . . C k F ( n − k ) F(n)=C_1F(n-1)+C_2F(n-2)+...C_kF(n-k) F(n)=C1F(n1)+C2F(n2)+...CkF(nk)

解决下面这道题:
有一个农场第一年有一只奶牛,每一只奶牛每年会产一只小奶牛,所有牛不会死,而且牛长到第三年即可生牛(全是母牛,不需要公牛)

F ( n ) = F ( n − 3 ) + F ( n − 1 ) = 1 ∗ F ( n − 3 ) + 0 ∗ F ( n − 2 ) + 1 ∗ F ( n − 1 ) F(n)=F(n-3)+F(n-1)\\ =1*F(n-3)+0*F(n-2)+1*F(n-1)\\ F(n)=F(n3)+F(n1)=1F(n3)+0F(n2)+1F(n1)
也就是去年的牛+有生育能力的牛(3年前时间节点就存在的牛)

∣ F ( 4 ) , F ( 3 ) , F ( 2 ) ∣ = ∣ F ( 1 ) + F ( 3 ) , F ( 3 ) , F ( 2 ) ∣ = ∣ F ( 3 ) , F ( 2 ) , F ( 1 ) ∣ ∗ ∣ 1 1 0 ∣ ∣ 0 0 1 ∣ ∣ 1 0 0 ∣ |F(4),F(3),F(2)|=|F(1)+F(3),F(3),F(2)|=|F(3),F(2),F(1)|*\begin{matrix} |1&1&0|\\ |0&0&1|\\ |1&0&0|\\ \end{matrix} F(4),F(3),F(2)=F(1)+F(3),F(3),F(2)=F(3),F(2),F(1)101100010

∣ F ( n ) , F ( n − 1 ) , F ( n − 2 ) ∣ = = ∣ F ( 3 ) , F ( 2 ) , F ( 1 ) ∣ ∗ D 的 n − 3 次 方 |F(n),F(n-1),F(n-2)|==|F(3),F(2),F(1)|*D的n-3次方 F(n),F(n1),F(n2)==F(3),F(2),F(1)Dn3

优化的迈台阶方法

小人迈台阶,可以迈1步或者迈2步,问迈第n层多少种走法(斐波那契数列)

F ( n ) = F ( n − 2 ) + F ( n − 1 ) F ( 1 ) = 1 F ( 2 ) = 2 F(n)=F(n-2)+F(n-1)\\ F(1)=1\\ F(2)=2\\ F(n)=F(n2)+F(n1)F(1)=1F(2)=2
依然可以优化到O(log2n)

补充题目1

给定一个数,想象由0和1两种字符,组成的所有长度为N的字符串,如果某个字符串,任何0字符的左边都有1紧挨着,认为这个字符达标,返回有多少达标的字符串。

d p [ i ] [ 0 ] 表 明 以 0 为 结 尾 长 度 为 i 的 字 符 串 个 数 d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 1 ] d p [ i ] [ 0 ] = d p [ i − 1 ] [ 1 ] dp[i][0]表明以0为结尾长度为i的字符串个数\\ dp[i][1]=dp[i-1][0]+dp[i-1][1]\\ dp[i][0]=dp[i-1][1] dp[i][0]0idp[i][1]=dp[i1][0]+dp[i1][1]dp[i][0]=dp[i1][1]

d p [ 1 ] [ 1 ] = 1 d p [ 1 ] [ 0 ] = 0 dp[1][1]=1\\ dp[1][0]=0 dp[1][1]=1dp[1][0]=0
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nBjHYaAn-1609834931464)(F7B71508C71841069BBF028AE1810505)]

再 仔 细 观 察 后 : d p [ i ] [ 1 ] = d p [ i − 2 ] [ 1 ] + d p [ i − 1 ] [ 1 ] 再仔细观察后:dp[i][1]=dp[i-2][1]+dp[i-1][1]\\ dp[i][1]=dp[i2][1]+dp[i1][1]
石 破 天 惊 : f ( n ) = f ( n − 1 ) + f ( n − 2 ) 又 可 以 优 化 成 o ( L O G 2 n ) 石破天惊:f(n)=f(n-1)+f (n-2)又可以优化成o(LOG2n) f(n)=f(n1)+f(n2)o(LOG2n)

补充题目2

在这里插入图片描述

我 的 思 考 : d p [ 2 ] [ 1 ] = 1 d p [ 1 ] [ 2 ] = 1 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 2 ] + d p [ i − 2 ] [ j − 1 ] 我的思考: dp[2][1]=1\\ dp[1][2]=1\\ dp[i][j]=dp[i-1][j-2]+dp[i-2][j-1]\\ dp[2][1]=1dp[1][2]=1dp[i][j]=dp[i1][j2]+dp[i2][j1]

很 棒 的 递 归 想 法 : F ( n ) = F ( n − 1 ) + F ( n − 2 ) 很棒的递归想法:\\ F(n)=F(n-1)+F(n-2)\\ F(n)=F(n1)+F(n2)

在这里插入图片描述

没条件转移 就用斐波那契!!!超级优化

蓄水池问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9PBbxUZS-1609834931481)(EA9517E9870840AB9DE77683AC25292D)]

问题场景:
左边在不断掉有编号的球,但不知道总共有多少个。
我们有一个包,它的容量为10,不断拿小球,我们要求当停止掉下小球时,包里取了10个小球,要求每个球被取的概率为
10 n \frac{10}{n} n10

规则
  1. 前10个小球,直接入袋
  2. 后面的第i个小球,以 10 i \frac{10}{i} i10的概率入袋
  3. 从袋中以 1 10 \frac{1}{10} 101的概率随机扔出去一个小球
证明

对 于 前 10 个 小 球 , 当 第 11 个 球 来 临 时 , 被 保 留 下 的 概 率 是 多 少 ? = 1 − 10 11 ∗ 1 10 = 10 11 对 于 前 10 个 小 球 , 当 第 12 个 球 来 临 时 , 被 保 留 下 的 概 率 是 多 少 ? = 10 11 ∗ ( 1 − 10 12 ∗ 1 10 ) = 10 12 . . . 对 于 前 10 个 小 球 , 当 第 n 个 球 来 临 时 , 被 保 留 下 的 概 率 是 多 少 ? = 10 n 对于前10个小球,当第11个球来临时,被保留下的概率是多少?\\ =1-\frac{10}{11}*\frac{1}{10}=\frac{10}{11}\\ 对于前10个小球,当第12个球来临时,被保留下的概率是多少?\\ =\frac{10}{11}*(1-\frac{10}{12}*\frac{1}{10})=\frac{10}{12}\\ ...\\ 对于前10个小球,当第n个球来临时,被保留下的概率是多少?\\ =\frac{10}{n}\\ 1011=11110101=11101012=1110(11210101)=1210...10n=n10

对 于 后 面 的 球 来 说 ( 比 如 第 11 个 球 ) : 刚 来 时 = 10 11 对 于 后 面 的 球 来 说 ( 比 如 第 11 个 球 ) : 第 12 球 来 时 = 10 11 ∗ ( 1 − 1 10 ∗ 10 12 ) = 10 12 . . . 对 于 后 面 的 球 来 说 ( 比 如 第 11 个 球 ) : 第 n 球 来 时 = 10 n 对于后面的球来说(比如第11个球): 刚来时 \\ =\frac{10}{11}\\ 对于后面的球来说(比如第11个球): 第12球来时 \\ =\frac{10}{11}*(1-\frac{1}{10}*\frac{10}{12})=\frac{10}{12}\\ ...\\ 对于后面的球来说(比如第11个球): 第n球来时 \\ =\frac{10}{n}\\ 11=11101112=111011011210=1210...11n=n10

适用很多场景哦

1.本质:给定一个数据流,数据流长度N很大,且N直到处理完所有数据之前都不可知,请问如何在只遍历一遍数据(O(N))的情况下,能够随机选取出m个不重复的数据。

彩蛋题2哦

如何用等概率的1~ 7生成等概率的1~10?

二 进 制 形 式 转 化 : 123 代 表 0 456 代 表 1 7 扔 掉 这 样 就 得 到 了 等 概 率 生 成 0 , 1 的 随 机 源 然 后 1 − 10 可 以 转 化 为 二 进 制 四 位 二 进 制 可 以 等 概 率 表 示 0 − 15 扔 掉 不 属 于 1 − 10 的 数 即 可 二进制形式转化: 1 2 3代表0 \\ 4 5 6代表1 \\ 7扔掉\\ 这样就得到了等概率生成0,1的随机源 \\ 然后 1-10 可以转化为二进制 \\ 四位二进制可以等概率表示0-15 \\ 扔掉不属于1-10的数即可 12304561701110015110

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值