前言
动态规划是大家都熟悉与陌生的知识,非常灵活多变,我自己也不敢说自己掌握了,今天给大家介绍一道题,不仅局限于动态规划做题,还会上升到信息论,乃至于启发自己认知世界的角度
因为比较难,本文不会详细介绍动态规划方法,所以需要读者有一定基础,否则可能理解有困难
题目链接:可怜的小猪
有 buckets
桶液体,其中 正好有一桶 含有毒药,其余装的都是水。它们从外观看起来都一样。为了弄清楚哪只水桶含有毒药,你可以喂一些猪喝,通过观察猪是否会死进行判断。不幸的是,你只有 minutesToTest
分钟时间来确定哪桶液体是有毒的。
喂猪的规则如下:
- 选择若干活猪进行喂养
- 可以允许小猪同时饮用任意数量的桶中的水,并且该过程不需要时间。
- 小猪喝完水后,必须有
minutesToDie
分钟的冷却时间。在这段时间里,你只能观察,而不允许继续喂猪。 - 过了
minutesToDie
分钟后,所有喝到毒药的猪都会死去,其他所有猪都会活下来。 - 重复这一过程,直到时间用完。
给你桶的数目 buckets
,minutesToDie
和 minutesToTest
,返回 在规定时间内判断哪个桶有毒所需的 最小 猪数 。
示例 1:
输入: buckets = 1000, minutesToDie = 15, minutesToTest = 60
输出: 5
示例 2:
输入: buckets = 4, minutesToDie = 15, minutesToTest = 15
输出: 2
示例 3:
输入: buckets = 4, minutesToDie = 15, minutesToTest = 30
输出: 2
dp解析
一般来说,动态规划就是三步骤
- 定义数组含义,多数情况是int值, dp[]或者dp[][]
- 赋予初始值
- 找到状态转移方程,开始递推
推到最后,大部分情况下,dp数组最后一个值就是答案
这个题,
题中有buckets,minutesToTest,minutesToDie三个变量,由于正面角度比较难思考,可以反过来,
全问题为n只小猪,能够有限的轮次测试中测出毒药,一轮耗时minutesToDie,总轮数为minutesToTest/minutesToDie
所以,其子问题为i只小猪测试j轮的结果,不影响后续测试,而后续测试需要子问题的结果来递推
满足动态规划的重叠子问题和无后效性原则,同时又是求最值(简称n),所以确定可以用动态规划(简称dp)
- 首先定义数组含义
f(i, j)表示i只小猪,测试j轮后最多可以在多少butkets中找到毒药,显然,这是一个二维数组dp[i][j] - 然后赋予初始值
dp[0][0] , dp[i][0]都为1,相当于没有测试,因为知道必有一桶毒,所以bucket = 1时,可以肯定为有毒,dp[0][j]没有猪,也全为1 - 状态转移
假设现在状态要算f(i, j),一轮测试后还剩下k只猪存活,而测试剩下j - 1次,可以确定,f(k, j - 1)就是f(i, j )的前一个状态,并将递推出f(i, j)
从i变为k的可能组合数为 C(i, k),因此f(i, j) = C(i, k) * f(k, j - 1),其中k的取值为0~i,所以最后的计算如下
f ( i , j ) = ∑ k = 0 i f ( k , j − 1 ) × C i k f(i,j) = \sum_{k=0}^i f(k, j-1) \times C_{i}^{k} f(i,j)=k=0∑if(k,j−1)×Cik
到了这步,已经掌握了解题钥匙,更多细节可以参考题解
本文并非想详细介绍题解,更多的是探讨思想
信息论
抛开dp的过程,只看开始和结尾,buckets,minutesToTest,minutesToDie都限定后,通过递推或者某种方式,我们就能得到最少的猪数量n,也就是说,当相关信息量确认好后,答案就确定了
这给我们一个很大提示,那就是,面对一个问题的时候,在耗费时间去做之前,仅仅凭借已经掌握的信息,就能判断出能不做成。注意,这里不是靠经验或者直觉,而是真真实实的科学,若是吸收这一思想,并勤加练习,相当多的难题可以得到解决
那现在我们就来仔细了解下这一理论吧
我们知道,计算机的很多数据看不见摸不着,我们将其统一设置为二进制,使用bit来记录数据,创造纷繁的信息世界。而我们,不知道三维世界的造物主用的几进制,在这个世界的我们无法用自己的信息来精确表达信息本身
熵
不过没关系,我们可以一个模糊的“熵”暂时代替,表示混乱度,越混乱,熵越高
熵增定律:在一个孤立系统里,如果没有外力做功,其总混乱度(熵)会不断增大
对于理科生很好理解,毕竟学过
对于保洁也很好理解,毕竟一个房间如果长期不打扫,肯定会变脏
对于老师也很好理解,毕竟他如果长时间不来教室,教室肯定乱套
对于宅男更好理解,长期不外出不和人交流,一定……
信息熵与信息
在信息论中,信息熵表示信息的不确定程度
比如,我们都知道,同样的内容,中文写的往往比英文薄一些,实际上,就是因为中文的信息不确定程度高,也就是信息熵大,所以所需的信息量就会少,字数也会少,往往掌握1000个汉字就能应付日常说话,很多汉字在不同的组词下有大量不同的含义。而英文,掌握5000单词都未必能说清楚
但是信息熵大未必是好事,信息熵越大,不确定程度越高,其实代表信息量越少,而减少信息熵的方式,就是增加含信息量的信息,不含信息量的信息,可以归类为废话(说到这,我都不认识信息两字了)
所以方向来了!对于一件富含信息熵的事情,我只要掌握足够信息量的信息,与信息熵等价,那么就可以做!
比如,明天是否下雨?,这件事信息熵很大,天气预报的结果,天上的乌云,蚂蚁搬家等等都是多多少少降低信息熵的信息
到了这里,你可以还是会有疑惑,说来说去,最后不还是靠感觉吗?不过是用了一些科学名词归类而已
香农厉害就厉害在这里,讲一个千万年来人说不清的感觉提取出公式,让人类的认知进入了一个全新领域。在信息论提出之后,个人认为能与这一瞬间媲美的只有阿姆斯特朗的那一小步。
公式如下
H ( X ) = − ∑ i = 1 n p ( x i ) log b p ( x i ) H(X) = -\sum_{i=1}^n p(x_i)\log_b p(x_i) H(X)=−i=1∑np(xi)logbp(xi)
看起来挺复杂,但是理解很简单,
其中,H(X)表示随机变量X的信息熵,P(xi)表示X取值为xi的概率,logb表示以b为底的对数。
这里的b表示信息单位的基础,如果你想二进制表示,那么b == 2
x表示事件,xi可以理解为x里面的某种元素,公式就是将X事件中,1~n种元素发生的概率求和而来,n可以理解为可能的所有情况
比如扔一枚硬币,只有两种情况,那么其信息熵为
H ( X ) = − ∑ i = 1 2 p ( x i ) log 2 p ( x i ) = − [ 0.5 log 2 ( 0.5 ) + 0.5 log 2 ( 0.5 ) ] = 1 b i t H(X) = -\sum_{i=1}^2 p(x_i)\log_2 p(x_i) = -\left[0.5\log_2(0.5) + 0.5\log_2(0.5)\right] = 1 bit H(X)=−i=1∑2p(xi)log2p(xi)=−[0.5log2(0.5)+0.5log2(0.5)]=1bit
也就是说,只要有一个含1bit信息量的信息,就可以消灭信息熵,比如我告诉你,硬币为正面,此时,信息熵消失
解题
回到小猪的话题,题目充满了H(X)信息熵,在minutesToTest,minutesToDie限定下,X为bucket数,x为1000时
H = − ( 1 / 1000 ) ∗ l o g 2 ( 1 / 1000 ) − ( 1 / 1000 ) ∗ l o g 2 ( 1 / 1000 ) − . . . − ( 1 / 1000 ) ∗ l o g 2 ( 1 / 1000 ) H = - (1/1000) * log2(1/1000) - (1/1000) * log2(1/1000) - ... - (1/1000) * log2(1/1000) H=−(1/1000)∗log2(1/1000)−(1/1000)∗log2(1/1000)−...−(1/1000)∗log2(1/1000)
= − 1000 ∗ ( 1 / 1000 ) ∗ l o g 2 ( 1 / 1000 ) = l o g 2 ( 1000 ) ≈ 9.97 = - 1000 * (1/1000) * log2(1/1000) = log2(1000) ≈ 9.97 =−1000∗(1/1000)∗log2(1/1000)=log2(1000)≈9.97
我们可以对这个结果有些感性认识,看起来信息熵的增长不是线性的,而是log,扔硬币这种五五开的事情为1,而1000桶水找1个有毒居然只有9.97,当有10000桶水时,信息熵为log2(10000) = 13.29
现在我们就要考虑,多少小猪能提供超过9.97
参考链接:https://www.zhihu.com/question/60227816/answer/1274071217
https://zh.wikipedia.org/wiki/%E7%86%B5_(%E4%BF%A1%E6%81%AF%E8%AE%BA)