小猪,信息论与生活

1,095 阅读9分钟

前言

动态规划是大家都熟悉与陌生的知识,非常灵活多变,我自己也不敢说自己掌握了,今天给大家介绍一道题,不仅局限于动态规划做题,还会涉及到信息论,并探讨自己认知世界的角度 因为比较难,本文不会详细介绍动态规划方法,所以需要读者有一定基础,否则可能理解有困难

题目链接:可怜的小猪

buckets 桶液体,其中 正好有一桶 含有毒药,其余装的都是水。它们从外观看起来都一样。为了弄清楚哪只水桶含有毒药,你可以喂一些猪喝,通过观察猪是否会死进行判断。不幸的是,你只有 minutesToTest 分钟时间来确定哪桶液体是有毒的。

喂猪的规则如下:

  1. 选择若干活猪进行喂养
  2. 可以允许小猪同时饮用任意数量的桶中的水,并且该过程不需要时间。
  3. 小猪喝完水后,必须有 minutesToDie 分钟的冷却时间。在这段时间里,你只能观察,而不允许继续喂猪。
  4. 过了 minutesToDie 分钟后,所有喝到毒药的猪都会死去,其他所有猪都会活下来。
  5. 重复这一过程,直到时间用完。

给你桶的数目 bucketsminutesToDieminutesToTest ,返回 在规定时间内判断哪个桶有毒所需的 最小 猪数

示例 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解析

一般来说,动态规划就是三步骤

  1. 定义数组含义,多数情况是int值, dp[]或者dp
  2. 赋予初始值
  3. 找到状态转移方程,开始递推

推到最后,大部分情况下,dp数组最后一个值就是答案

这个题,

题中有buckets,minutesToTest,minutesToDie三个变量,由于正面角度比较难思考,可以反过来, 全问题为n只小猪,能够有限的轮次测试中测出毒药,一轮耗时minutesToDie,总轮数为minutesToTest/minutesToDie

所以,其子问题为i只小猪测试j轮的结果,不影响后续测试,而后续测试需要子问题的结果来递推 满足动态规划的重叠子问题和无后效性原则,同时又是求最值(简称n),所以确定可以用动态规划(简称dp)

  • 首先定义数组含义 f(i, j)表示i只小猪,测试j轮后最多可以在多少butkets中找到毒药,显然,这是一个二维数组dp[i] [j]

  • 然后赋予初始值 dp[0] , dp[i]都为1,相当于没有测试,因为知道必有一桶毒,所以bucket = 1时,可以肯定为有毒,dp[0]没有猪,也全为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=0if(k,j1)×Cikf(i,j) = \sum_{k=0}^i f(k, j-1) \times C_{i}^{k}

image.png

image.png

到了这步,已经掌握了解题钥匙,更多细节可以参考题解, 本文并非想详细介绍题解,更多的是探讨思想

信息论

抛开dp的过程,只看开始和结尾,buckets,minutesToTest,minutesToDie都限定后,通过递推或者某种方式,我们就能得到最少的猪数量n,也就是说,当相关信息量确认好后,答案就确定了

这给我们一个很大提示,那就是,面对一个问题的时候,在耗费时间去做之前,仅仅凭借已经掌握的信息,就能判断出能不做成。注意,这里不是靠经验或者直觉,而是真真实实的科学,若是吸收这一思想,并勤加练习,相当多的难题可以得到解决

那现在我们就来仔细了解下这一理论吧

我们知道,计算机的很多数据看不见摸不着,我们将其统一设置为二进制,使用bit来记录数据,创造纷繁的信息世界。而我们,不知道三维世界的造物主用的几进制,在这个世界的我们无法用自己的信息来精确表达信息本身

不过没关系,我们可以一个模糊的“熵”暂时代替,表示混乱度,越混乱,熵越高

熵增定律:在一个孤立系统里,如果没有外力做功,其总混乱度(熵)会不断增大

对于理科生很好理解,毕竟学过 对于保洁也很好理解,毕竟一个房间如果长期不打扫,肯定会变脏 对于老师也很好理解,毕竟他如果长时间不来教室,教室肯定乱套 对于宅男更好理解,长期不外出不和人交流,一定……

信息熵与信息

在信息论中,信息熵表示信息的不确定程度

比如,我们都知道,同样的内容,中文写的往往比英文薄一些,实际上,就是因为中文的信息不确定程度高,也就是信息熵大,所以所需的信息量就会少,字数也会少,往往掌握1000个汉字就能应付日常说话,很多汉字在不同的组词下有大量不同的含义。而英文,掌握5000单词都未必能说清楚

image.png

但是信息熵大未必是好事,信息熵越大,不确定程度越高,其实代表信息量越少,而减少信息熵的方式,就是增加含信息量的信息,不含信息量的信息,可以归类为废话(说到这,我都不认识信息两字了)

所以方向来了!对于一件富含信息熵的事情,我只要掌握足够信息量的信息,与信息熵等价,那么就可以做!

比如,明天是否下雨?,这件事信息熵很大,天气预报的结果,天上的乌云,蚂蚁搬家等等都是多多少少降低信息熵的信息

到了这里,你可以还是会有疑惑,说来说去,最后不还是靠感觉吗?不过是用了一些科学名词归类而已

香农厉害就厉害在这里,讲一个千万年来人说不清的感觉提取出公式,让人类的认知进入了一个全新领域。在信息论提出之后,个人认为能与这一瞬间媲美的只有阿姆斯特朗的那一小步。 公式如下

H(X)=i=1np(xi)logbp(xi)H(X) = -\sum_{i=1}^n p(x_i)\log_b p(x_i)

看起来挺复杂,但是理解很简单,

其中,H(X)表示随机变量X的信息熵,P(xi)表示X取值为xi的概率,logb表示以b为底的对数。

这里的b表示信息单位的基础,如果你想二进制表示,那么b == 2 x表示事件,xi可以理解为x里面的某种元素,公式就是将X事件中,1~n种元素发生的概率求和而来,n可以理解为可能的所有情况

比如扔一枚硬币,只有两种情况,那么其信息熵为

H(X)=i=12p(xi)log2p(xi)=[0.5log2(0.5)+0.5log2(0.5)]=1bitH(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

也就是说,只要有一个含1bit信息量的信息,就可以消灭信息熵,比如我告诉你,硬币为正面,此时,信息熵消失

解题

回到小猪的话题,题目中H(X)信息熵,在minutesToTest,minutesToDie限定下,x为bucket数,x为1000时

H(x)=(1/1000)log2(1/1000)(1/1000)log2(1/1000)...(1/1000)log2(1/1000)H(x) = - (1/1000) * log2(1/1000) - (1/1000) * log2(1/1000) - ... - (1/1000) * log2(1/1000)
=1000(1/1000)log2(1/1000)=log2(1000)9.97= - 1000 * (1/1000) * log2(1/1000) = log2(1000) ≈ 9.97

解释:1/1000表示任何桶水都有1/1000可能是毒水,然后有1000种可能性,需要累加

我们可以对这个结果有些感性认识,看起来信息熵的增长不是线性的,而是log,扔硬币这种五五开的事情为1,而1000桶水找1个有毒居然只有9.97,当有10000桶水时,信息熵为log2(10000) = 13.29

现在我们考虑:多少小猪能够提供大于9.97的信息熵?从示例一中

输入:buckets = 1000, minutesToDie = 15, minutesToTest = 60
输出:5

看出,可以测试 60 / 15 = 4轮,那么共计有5种状态,那就是4种死亡的场景 + 1种存活场景,在这一情况下,“1只小猪的5种状态”可以提供的信息熵为

H=(1/5)log2(1/5)(1/5)log2(1/5)...(1/5)log2(1/5)H = - (1/5) * log2(1/5) - (1/5) * log2(1/5) - ... - (1/5) * log2(1/5)
=5(1/5)log2(1/5)=log2(5)2.3219= - 5 * (1/5) * log2(1/5) = log2(5) ≈ 2.3219

解释:1/5表示小猪等可能进入到5种状态中的一种,有5次统计,需要累加

一只小猪是这么多,那么2只猪呢?会产生 5^2 = 25种可能性,3只就会有125种可能性,计算可得

Hn=2.3219nHn = 2.3219 n

所以

n=H(x)/H(n)=9.97/2.32194.292n = H(x) / H(n) =9.97 /2.3219 ≈ 4.292

对于示例一,需要至少4.3头猪才能提供答案

当buckets变化时,原题的解题代码为

class Solution {
    public int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
        int times = minutesToTest / minutesToDie + 1;
        double time = Math.log(buckets) / Math.log(times); // 信息熵相除
        return (int)Math.ceil(time);
    }
}

后记

信息论现在广泛运用在了压缩编码,通信协议和设计等的底层算法理论,相等于给后人研究算法提供了一个天花板,只要你的算法结果没达到信息论理论极限,就有提升空间,要是超过了,那你肯定是错的

当我们重新审视这一解法,除了感到些许震撼之外,难免会有疑问,比如,

  • 你可能好奇小猪如何喝水才能在5只的情况下测1000桶水

可以参考另一篇文章: 小兔喝水

  • 为什么小猪算出来的信息熵可以和水的信息熵等价相除?

显然,信息熵就是信息熵,他自身是可以比较的,并不存在什么xx的信息熵。很多东西也不存在什么中西之分,只有真假之分

所以,我们应该更加关注的,是跳脱出事情的表面属性,去提取出本质的信息量,生活中无法严格按照公式去计算,但是,当我们做一些比较重要的决策时,可以再想想,我是否已经掌握了足够的信息?

什么叫足够的信息?

比如选取高考志愿,在我看来至少需要的信息有:目标专业知识与未来前景,目标大学的地理位置与名气,自己的喜好等等,更加深层次的由于我并不精通信息论专业,但是希望你读完有所收获~

参考链接:

www.zhihu.com/question/60… zh.wikipedia.org/wiki/%E7%86… leetcode.cn/problems/po…