有1到10000共10000個數,如果我從中隨機拿走一個數,你如何知道我拿走了哪個? |
相信很多人見過這道題,我作爲一個學生,在不久前的騰訊宣講會上聽到了這道題,當時被相加再相減的方法折服了,覺得十分精妙,但是今天意外看到了這位博主的做法,真是眼前一亮,所以在這裏轉載給大家看一看。以下就是該博主的博文(源地址:http://www.cnblogs.com/baiyanhuang/archive/2010/06/23/1763981.html):
一道邏輯題 - 我拿走了哪個數
有1到10000共10000個數,如果我從中隨機拿走一個數,你如何知道我拿走了哪個?
相信很多人看過這道題,並知道答案,這幾天和同事聊天時聽到了這個問題,因爲有過自己的思考過程,不妨記錄下來。說是邏輯題,其實也算是一道算法題,同事先講了下他被面試中的思維過程:
- 先把10000個數相乘,然後再將拿走一個數之後的9999個數相乘,兩者相除即可。
這個算法是正確的,但是會有兩個潛在的問題:- 如此多的數相乘,其範圍必然會超出系統提供的數據類型支持,當然你可以實現自己的大數表示的算法,但那樣性能必然有影響。
- 假設擴展一下題目,提供的數組中有0的話,乘法就不可用了。
- 針對前面提出的問題,同事想到了使用加法,先求出10000個數的和,再減去9999個數的和。
這樣數據不會溢出,而且加法的效率比乘法也要高很多,即使數據中包含0,也沒有任何問題。
我用a,b,c,d4個數來做演示,因爲異或符合結合律和交換律(你可以用0,1試一下),於是:
d = (a^b^c^d)^(a^b^c)
此處用異或的好處在於
- 不會溢出
- 異或的速度要快於加法
再擴展一下題目,如果提供的數本身就超出了內置類型的表示範圍,如在1到2^128,該如何處理?這個問題是在寫這篇文章的過程中想到的,暫時沒有好的辦法。
(異或的確是挺神奇的一個操作,之後打算寫一個memory-transaction管理的系列文章,也會提到用這個神奇的異或來實現undo-redo算法。)