前几天接到一个 Java 开发岗位的电话面试,接电话后对方就直接问我现在方便吗,简历什么时候收到,能不能电话面试,如果可以的话,可以马上开始。emmm,太突然了,我都没勇气问公司名字,也不知道招的是中高级还是高级。算了,直接进去了,想着面试完再问公司名字,然后去招聘网站上看看招聘信息。整个面试大概一个半小时,快结束的时候,我们又聊了些别的,才知道他们招的是 Java 高级开发人员。
面试问的问题很多,从数据结构到算法,从框架源码到系统设计,从基础到多线程,解决某些业务场景下的高并发等等....这里就不一一列举了,只在这里分析一道有意思的智商题——盒子分割问题。
问题:
有1000个苹果要分到10个箱子里,每个箱子里苹果的数量可以相等也可以不等,没有限制。要求10个箱子能装下全部1000个苹果,不能有多余的。那么我想要任意数量的苹果(1~1000),我可以移动箱子来满足我的需求,而不用拆箱。这10个箱子里的苹果数量应该如何分配?
其实网上已经有很多人对这个问题给出了答案,但是很少有人给出详细的分析,下面介绍两种分析方法。
①仿真分析方法
其实单从这道题出发,我们就可以模拟出这个问题场景。比如我想要一个苹果,那么肯定有一个盒子里面只有一个苹果,这个场景只有一种情况。如果我想要两个苹果,这个场景有不止一种解,我可以搬一个盒子,每个盒子里放一个苹果,也可以只搬一个盒子,里面有两个苹果。但是我们可以猜测,如果采用第一种解法,两个盒子,每个盒子里放一个苹果,这个解可能就不能满足题目的要求——我们需要把1000个苹果全部装进去。同样的,如果我想要三个苹果,如果我们采用三个或者两个盒子,每个盒子里放一个苹果,那么这个方法肯定不能把1000个苹果全部装进去,所以是不可行的。所以我们猜测(先猜测,再验证),一般可行的解应该是装苹果最多的那个解。 比如我想要两个苹果,我就不用两个箱子各装一个,而是直接拿出一个箱子装两个。如果我需要三个苹果,那我只要把一个苹果的箱子和两个苹果的箱子搬过来就行了。如果我想要四个苹果,现在就只剩下一个苹果的箱子和两个苹果的箱子不能满足我的要求,那我就再拿出一个箱子装四个苹果,以此类推。只要不能满足我的要求,我就拿出一个新的箱子来装我想要的。
下面是一个图表:
从图中不难看出,只要我想要 511 以内的苹果,我根本不需要移动第十个箱子,前面九个箱子已经可以满足我的需求了。比如我想要 286 个苹果,首先我们按照上图的排列方式找到第一个小于 286 的数字,也就是 256,把 256 这个箱子搬出来。然后我们需要找到 30 个苹果,再按照上图的排列方式找到第一个小于 30 的数字,也就是 16,把 16 这个箱子搬出来,然后我们还需要 14 个苹果。以此类推找到 8、4、2。好了,现在所有的箱子都搬完了,也就是 256+16+8+4+2=286,刚好满足了我们的需求。
②二元分析法
其实从第一种方法我们可以很清楚的看出,这组数列是有规律的,它们都是2的多次连续乘法的结果,而2的x次方其实就是二进制的形式。当然,对于这道题,我们在已经有了第一个结果的情况下,是这么想的。 如果我们是新手的话,其实会以为这道题的目的是用十个数(和为1000)去枚举1000以内的任意数。而对于枚举任意数,只有奇数和偶数两种。而奇偶数正好有这样的一个规律:任何偶数都可以拆成若干个2的幂然后相加,任何奇数都可以拆成若干个2的幂然后相加再加1(其实就是加2^0),比如12=8+4=1000+100,18=16+2=10000+10,17=16+1=10000+2^0=10000+1。有了这样的一个规律,就意味着我们可以用一系列的2的幂也就是二进制去枚举一个范围内的数据。 比如我们可以使用下面的二进制直接取出1到16之间的数据或者将其中的几个相加得到结果。
10
100
1000
其实不难发现,1、2、4、8 正好对应第一种方式中 8 个苹果的盒子能打中的苹果数量,对于这道题,有 1000 个苹果,10 个盒子,二进制模拟大致如下:
10
100
1000
10000
489
1~511 能枚举的数字范围就是 1~511,只要大于 511 的我们就从 489 中补上,然后剩下的数字就一直在 1~511 这个范围内了。比如我们需要 600 个数字,就先从 489 中取,还剩下 111 个,111 已经在 1~511 的范围内了,肯定会命中。当我们需要 1000 个数字的时候,其实就是一个临界点,我们先取 489,还需要 511,1~511 这个右边界刚好能保证能得到解。
类似分框的问题其实并不罕见,原谅我知识有限,二进制异或,移位计数等二进制常见测试也有,慢慢学习积累就好。
更多内容持续更新中,感兴趣的朋友请关注我的个人公众号,谢谢支持...