一道邏輯題 - 我拿走了哪個數

有1到10000共10000個數,如果我從中隨機拿走一個數,你如何知道我拿走了哪個?

相信很多人見過這道題,我作爲一個學生,在不久前的騰訊宣講會上聽到了這道題,當時被相加再相減的方法折服了,覺得十分精妙,但是今天意外看到了這位博主的做法,真是眼前一亮,所以在這裏轉載給大家看一看。以下就是該博主的博文(源地址:http://www.cnblogs.com/baiyanhuang/archive/2010/06/23/1763981.html):

一道邏輯題 - 我拿走了哪個數

有1到10000共10000個數,如果我從中隨機拿走一個數,你如何知道我拿走了哪個?

     相信很多人看過這道題,並知道答案,這幾天和同事聊天時聽到了這個問題,因爲有過自己的思考過程,不妨記錄下來。說是邏輯題,其實也算是一道算法題,同事先講了下他被面試中的思維過程:

  1. 先把10000個數相乘,然後再將拿走一個數之後的9999個數相乘,兩者相除即可。
    這個算法是正確的,但是會有兩個潛在的問題:
    • 如此多的數相乘,其範圍必然會超出系統提供的數據類型支持,當然你可以實現自己的大數表示的算法,但那樣性能必然有影響。
    • 假設擴展一下題目,提供的數組中有0的話,乘法就不可用了。
  2. 針對前面提出的問題,同事想到了使用加法,先求出10000個數的和,再減去9999個數的和。
    這樣數據不會溢出,而且加法的效率比乘法也要高很多,即使數據中包含0,也沒有任何問題。
然後就過關了,自己回去之後思考了一下,覺得還可以擴展,假設所有的數加起來之後仍然會溢出,那該如何處理,比如從1到(2^64-1),於是想到了位操作,與、或,異或中,要數異或最爲神奇,代入一看,果然合適: 先將所有的數異或起來,然後將拿走一個數之後的數異或起來,兩者結果再異或,便是拿走的那個數。

我用a,b,c,d4個數來做演示,因爲異或符合結合律和交換律(你可以用0,1試一下),於是:

a^b^c^d = (a^b^c)^d
d = (a^b^c^d)^(a^b^c)

 此處用異或的好處在於

  • 不會溢出
  • 異或的速度要快於加法
擴展一下題目,如果提供的不是整數,而是浮點數,會有問題嗎?當然沒有,因爲是在位級別上操作,無論是整數還是浮點數,在這個算法看來,都是一堆位,處理起來沒有什麼差別。

再擴展一下題目,如果提供的數本身就超出了內置類型的表示範圍,如在1到2^128,該如何處理?這個問題是在寫這篇文章的過程中想到的,暫時沒有好的辦法。

(異或的確是挺神奇的一個操作,之後打算寫一個memory-transaction管理的系列文章,也會提到用這個神奇的異或來實現undo-redo算法。)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章