“这个问题,需要用到高中数学知识。”
推理过程:忽略一胞多胎、不育夫妇以及还没生下男孩就去世的夫妇。
首先要认识到的是,该国的所有家庭,在停止生育之前,都拥有了或即将拥有一个男孩。为什么呢?因为每对夫妇都会不停生孩子,直到生出一个男孩才罢休。除了多胞胎,“一个男孩”就是“只有一个”的意思。因此,有多少家庭,就有多少男孩。
不过,一个家庭可以有任意数量的女孩。对女孩子做一次想象中的人口调查会是个好办法。
邀请全国所有的母亲集中到大房间,通过公共广播系统询问:“请所有第一胎生女孩的母亲举起手来好吗?”自然,有一半的妇女会举手。
如果母亲总数为N,那么N/2会举手,也就代表第一胎生的女孩有N/2个。请在想象中的标示牌上记下来:N/2。
接着你又问:“请第二胎生女孩的母亲举起手来好吗?第一胎、第二胎都是女孩的请别把手放下哦。”有一半的手会放下去,但不会有新的手举起来。第一个问题时就没举手的母亲,不会生第二个孩子,因为她们第一胎就是男孩。
于是,举起来的手是N/4,这意味着第二胎生出的女孩有N/4个。把它记在标示牌上。“请第三胎是女孩的母亲继续举起手好吗?”你明白了吧。继续这么做,直到最后再也没有还举着的手。每一轮问题,举起的手都会放下去一半。
这就带来了一个我们很熟悉的数列:(1/2+1/4+1/8+1/16+1/32+...)×N这一无穷级数之和为1(×N)。女孩的人数等于家庭数N,也等于男孩的人数(或非常接近)。因此,该国的男女比例是1:1。
至于为什么想当然是错的,固然每个家庭都有男孩,有很多家庭没有女孩;但同时所有家庭只会有一个男孩,却有可能有多个女孩。
这两种情况取得了折中。
“完成了前面的开胃菜,下面我们进行更加传统的项目,现在有N个32位整数,需要完成排序。这道题答对双倍奖励,答错双倍惩罚,不允许跳过。”少女在眼前的全息白板上写着,到了“白板编程”环节。
“总之,冒泡排序是错的。”这个***式的笑话,引来一阵大笑。
“这个N是多少呢,我想不同的N结果差异会很大。”墨出尘一眼就识别出了潜在的陷阱。
“我们不妨让N=150,000。”
“那可是相当多的数字了,我想快速排序是个不错的选择。”墨出尘道。
“OK,sounds great,下一步,钟奇正你负责快速排序算法的实现,墨出尘你需要向初中生水平的我,解释清楚什么是快速排序算法。”少女道。
“我该用什么语言来实现?有什么限制吗?”
“为什么我要向初中水平的人解释什么是快速排序算法?”
“我认为你可以在python、c(c++)、java、swift、perl中选择一个,至于为什么要解释给不懂的人,很简单,这测试的是同理心,这与我们设计产品时,切换到用户角度来思考是一个道理。”
钟奇正拿着一支黑色的马克笔,符号开始在白板上涌现,速度快得让人惊讶。
墨出尘开始解释什么是快速排序,他是这样说的:
“快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。首先从数列中挑出一个元素,称为“基准”(pivot);
“重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
“递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序,就完成了快速排序。”
“听起来像是疯子的喃喃自语,我完全不知你在说什么。”少女反馈道。
“我觉得很清晰啊。”
“上面的纯属废话,能看懂你说的是什么的,本身就懂归并排序;看不懂的,依然学不会。这是我对你说的是废话这一观点的逻辑证明。”
“好吧,我承认上面的很晦涩,毕竟自然语言并不是表述算法的合适载体,自然语言本身的歧义性、模糊性使它不具备这种功能,或许你看看钟奇正写的代码就清晰了。”
少女双手抱头,“我不看代码!我不看代码!我不看代码!”
“墨出尘,我认为你举个例子加以说明会好很多。”钟奇正已经完成了白班编码。
“ah,好主意,正好也给你提供一个测试用例,我们拿15个数举个栗子好了,至于15万个数,在逻辑上是一样的。只是运行时间、内存占用会多很多。”
假设有一组数是3、44、38、6、47、15、36、26、27、2、46、4、19、50、48,现在我们对它进行快速排序。
首先我们挑选3作为基准,在过程中我们对小于3的数,进行标记,并替换到前面,当一圈进行完后(到最后一个数48),我们将小于3的数移动到左侧。
3、2、38、6、47、15、36、26、27、44、46、4、19、50、48(2与44替换)
下面让3就这个基准就处于数列的中间位置,即它左边的都比它小,右边的都比它大。数列变为2、(3)、38、6、47、15、36、26、27、44、46、4、19、50、48。
之后以3为分界线,分别对3左边的数列进行快速排序,右边的数列同样进行快速排序。因为左边只有一个数,至此快速排序结束,不再递归。
右边的38、6、47、15、36、26、27、44、46、4、19、50、48数列,以38基准进行快速排序,结果是38、6、15、36、26、27、4、19、47、44、46、50、48
将38基准位置进行调整,6、15、36、26、27、4、19、38、47、44、46、50、48
将38左侧,右侧分别进行快速排序6、15、36、26、27、4、19以6为基准,6、4、36、26、27、15、19,变为4、6、36、26、27、15、19,将36、26、27、15、19进行快速排序,变为26、27、15、19、36,将26、27、15、19、进行快速排序,26,15,19,27,调整位置,15,19,26,27,将15,19进行快速排序得到15,19
所以38左侧快速排序结果为4、6、15、19、26、27、36
38右则为47、44、46、50、48,快速排序得到44、46、47、50、48,对44、46进行快速排序得到44、46,对50、48进行快速排序得到48、50。所以右边结果为44、46、47、48、50
所以最终结果为2、3、4、6、15、19、26、27、36、(38)、44、46、47、48、50。
“这个过程与我的完全一致,我顺便做了个排序可视化,侧面验证了正确性,如果对排序非常感兴趣,可以看这里,
“见识到了结对编程的重要性了吧,我们深入探讨一下,如果数据本身是基本有序的,快速排序算法还有优势吗?”