程式員編程藝術:第六章、求解500萬以內的親和數

來源:互聯網
上載者:User

                      第六章、親和數問題--求解500萬以內的親和數

作者:上善若水、July、yansha。
出處:http://blog.csdn.net/v_JULY_v 。

前奏
    本章陸續開始,除了繼續保持原有的字串、數組等面試題之外,會有意識的間斷性節選一些有關數字趣味小而巧的面試題目,重在突出思路的“巧”,和“妙”。本章親和數問題之關鍵字,“500萬”,“線性複雜度”。

第一節、親和數問題
題目描述:
求500萬以內的所有親和數
如果兩個數a和b,a的所有真因數之和等於b,b的所有真因數之和等於a,則稱a,b是一對親和數。
例如220和284,1184和1210,2620和2924。

分析:
    首先得明確到底是什麼是親和數?

親和數問題最早是由畢達哥拉斯學派發現和研究的。他們在研究數位規律的時候發現有以下性質特點的兩個數:
220的真因子是:1、2、4、5、10、11、20、22、44、55、110;
284的真因子是:1、2、4、71、142。
而這兩個數恰恰等於對方的真因子各自加起來的和(sum[i]表示數i 的各個真因子的和),即
220=1+2+4+71+142=sum[284],
284=1+2+4+5+10+11+20+22+44+55+110=sum[220]。
得284的真因子之和sum[284]=220,且220的真因子之和sum[220]=284,即有sum[220]=sum[sum[284]]=284。

如此,是否已看出絲毫端倪?

如上所示,考慮到1是每個整數的因子,把出去整數本身之外的所有因子叫做這個數的“真因子”。如果兩個整數,其中每一個真因子的和都恰好等於另一個數,那麼這兩個數,就構成一對“親和數”(有關親和數的更多討論,可參考這:http://t.cn/hesH09)。

求解:
    瞭解了什麼是親和數,接下來咱們一步一步來解決上面提出的問題(以下內容大部引自水的原話,同時水哥有一句原話,“在你真正弄弄懂這個範例之前,你不配說你懂資料結構和演算法”)。

  1. 看到這個問題後,第一想法是什嗎?類比搜尋+剪枝?回溯?時間複雜度有多大?其中bn為an的偽親和數,即bn是an的真因數之和大約是多少?至少是10^13(@iicup:N^1.5 對於5*10^6 , 次數大致 10^10 而不是 10^13.)的數量級的。那麼對於每秒千萬次運算的電腦來說,大概在1000多天也就是3年內就可以搞定了(iicup的計算: 10^13 / 10^7 =1000000(秒) 大約 278 小時. )。如果是基於這個基數在最佳化,你無法在一天內得到結果的。
  2. 一個不錯的演算法應該在半小時之內搞定這個問題,當然這樣的演算法有很多。節約時間的做法是可以產生伴隨數組,也就是空間換時間,但是那樣,空間代價太大,因為資料規模龐大。
  3. 在稍後的演算法中,依然使用的伴隨數組,只不過,因為題目的特殊性,只是它方便和巧妙地利用了下標作為伴隨數組,來節約時間。同時,將回溯的思想換成遞推的思想(預先處理數組的時間複雜度為logN(調和級數)*N,掃描數組的時間複雜度為線性O(N)。所以,總的時間複雜度為O(N*logN+N)(其中logN為調和級數)  )。

第二節、伴隨數組線性遍曆
依據上文中的第3點思路,編寫如下代碼:

//求解親和數問題</p><p>//第一個for和第二個for迴圈是logn(調和級數)*N次遍曆,第三個for迴圈掃描O(N)。<br />//所以總的時間複雜度為 O(n*logn)+O(n)=O(N*logN)(其中logN為調和級數)。</p><p>//關於第一個for和第二個for尋找中,調和級數的說明:<br />//比如給2的倍數加2,那麼應該是 n/2次,3的倍數加3 應該是 n/3次,...<br />//那麼其實就是n*(1+1/2+1/3+1/4+...1/(n/2))=n*(調和級數)=n*logn。</p><p>//copyright@ 上善若水<br />//July、updated,2011.05.24。<br />#include<stdio.h></p><p>int sum[5000010]; //為防越界</p><p>int main()<br />{<br /> int i, j;<br />for (i = 0; i <= 5000000; i++)<br />sum[i] = 1; //1是所有數的真因數所以全部置1</p><p>for (i = 2; i + i <= 5000000; i++) //預先處理,預先處理是logN(調和級數)*N。<br />//@litaoye:調和級數1/2 + 1/3 + 1/4......的和近似為ln(n),<br />//因此O(n *(1/2 + 1/3 + 1/4......)) = O(n * ln(n)) = O(N*log(N))。<br />{<br />//5000000以下最大的真因數是不超過它的一半的<br /> j = i + i; //因為真因數,所以不能算本身,所以從它的2倍開始<br /> while (j <= 5000000)<br />{<br />//將所有i的倍數的位置上加i<br /> sum[j] += i;<br /> j += i;<br /> }<br /> }</p><p>for (i = 220; i <= 5000000; i++) //掃描,O(N)。<br />{<br />// 一次遍曆,因為知道最小是220和284因此從220開始<br /> if (sum[i] > i && sum[i] <= 5000000 && sum[sum[i]] == i)<br />{<br />//去重,不越界,滿足親和<br />printf("%d %d\n",i,sum[i]);<br /> }<br /> }<br />return 0;<br />}

運行結果:

    @上善若水:

    1、可能大家理解的還不是很清晰,我們建立一個5 000 000 的數組,從1到2 500 000 開始,在每一個下標是i的倍數的位置上加上i,那麼在迴圈結束之後,我們得到的是什嗎?是 類似埃斯托拉曬求素數的數組(當然裡面有真的親和數),然後只需要一次遍曆就可以輕鬆找到所有的親和數了。時間複雜度,線性。

    2、我們可以清晰的發現連續資料的映射可以通過數組結構本身的特點替代,用來節約空間,這是資料結構的藝術。在大規模連續資料的回溯處理上,可以通過轉化為遞推產生的方法,逆向思維操作,這是演算法的藝術。

    3、把最簡單的東西運用的最巧妙的人,要比用複雜方法解決複雜問題的人要頭腦清晰。

第三節、程式的構造與解釋
    我再來具體解釋下上述程式的原理,ok,舉個例子,假設是求10以內的親和數,求解步驟如下:

因為所有數的真因數都包含1,所以,先在各個數的下方全部置1

  1. 然後取i=2,3,4,5(i<=10/2),j依次對應的位置為j=(4、6、8、10),(6、9),(8),(10)各數所對應的位置。
  2. 依據j所找到的位置,在j所指的各個數的下面加上各個真因子i(i=2、3、4、5)。
    整個過程,即如所示(如sum[6]=1+2+3=6,sum[10]=1+2+5=8.):
    1  2  3  4  5  6  7  8  9  10
    1  1  1  1  1  1  1  1  1  1
               2      2      2      2
                       3          3 
                               4
                                       5
  3. 然後一次遍曆i從220開始到5000000,i每遍曆一個數後,
    將i對應的數下面的各個真因子加起來得到一個和sum[i],如果這個和sum[i]==某個i’,且sum[i‘]=i,
    那麼這兩個數i和i’,即為一對親和數。
  4. i=2;sum[4]+=2,sum[6]+=2,sum[8]+=2,sum[10]+=2,sum[12]+=2...
    i=3,sum[6]+=3,sum[9]+=3...
    ......
  5. i=220時,sum[220]=284,i=284時,sum[284]=220;即sum[220]=sum[sum[284]]=284,
    得出220與284是一對親和數。所以,最終輸出220、284,...

特別鳴謝

      litaoye專門為本親和數問題開文章繼續闡述,有興趣的朋友可繼續參見:http://topic.csdn.net/u/20110526/21/129c2235-1f44-42e9-a55f-878920c21e19.html。同時,任何人對本親和數問題有任何問題,也可以回複到上述文章上。

//求解親和數問題<br />//copyright@ litaoye<br />//July、胡濱,updated,2011.05.26。<br />using System;<br />using System.Collections.Generic;</p><p>namespace CSharpTest<br />{<br /> class Program<br /> {<br />public static void Main()<br />{<br />int max = 5000000;<br />DateTime start = DateTime.Now;<br />int[] counter = CreateCounter(max);</p><p>for (int i = 0; i < counter.Length; i++)<br />{<br />int num = counter[i] - i;<br />//if (num < counter.Length && num > i && counter[num] == counter[i])<br />// Console.WriteLine("{0} {1}", i, num);<br />}<br />Console.WriteLine((DateTime.Now - start).TotalSeconds);</p><p>Console.ReadKey();<br />}</p><p> static int[] CreateCounter(int n)<br /> {<br /> List<int> primes = new List<int>();<br /> int[] counter = new int[n + 1];<br /> counter[1] = 1;</p><p> for (int i = 2; i <= n; i++)<br /> {<br /> if (counter[i] == 0)<br /> {<br /> counter[i] = i + 1;<br /> primes.Add(i);<br /> }</p><p> for (int j = 0; j < primes.Count; j++)<br /> {<br /> if (primes[j] * i > n)<br /> break;</p><p> if (i % primes[j] == 0)<br /> {<br /> int k = i;<br /> int l = primes[j] * primes[j];</p><p> while (k % primes[j] == 0)<br /> {<br /> l *= primes[j];<br /> k /= primes[j];<br /> }</p><p> counter[primes[j] * i] = counter[k] * (l - 1) / (primes[j] - 1);<br /> break;<br /> }<br /> else<br /> counter[primes[j] * i] = counter[i] * (primes[j] + 1);<br /> }<br /> }</p><p> return counter;<br /> }<br /> }<br />}</p><p>/*<br />測試結果:<br />0.484375<br />0.484375<br />0.46875<br />單位second。<br />*/

本章完。

3.3續、求給定區間內的第K小(大)元素
第九章、閑話鏈表追趕問題
第十章、如何給10^7個資料量的磁碟檔案排序

面試題徵集令

  1. 十三個經典演算法研究系列+附、紅/黑樹狀結構系列(國內有史以來最為經典的紅/黑樹狀結構教程),共計20+6=26篇文章,帶目錄+標籤的PDF文檔,耗時近一個星期,足足346頁(夠一本書的分量了),已在花明月暗的協助下,正式製作完成。
  2. 想要的,發一道你自認為較好的面試題(c,c++,資料結構,演算法,智力題,數字邏輯或運算題)至我的郵箱:zhoulei0907@yahoo.cn,即可。我收到後,三天之內傳送此PDF檔案。July、20110.5.24.此聲明永久有效。

著作權,本人對本blog內所有任何內容享有著作權及著作權。實要轉載,請以連結形式註明出處。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.