面試官:你知道怎麼求素數嗎?
摘要:面試官:你知道怎麼求素數嗎?我:求素數?
本文分享自華為雲社群《》,原文作者:bigsai 。
前言
現在的面試官,是無數開發者的夢魘,能夠吊打面試官的屬實不多,因為大部分面試官真的有那麼那幾下子。但在面試中,我們這些小生存者不能全盤否定只能單點突破—從某個問題上讓面試官眼前一亮。這不,今天就來分享來了。
這年頭,演算法崗內卷不說,開發崗也有點內卷,對開發者要求越來越高了,而面試官也是處心積慮的 “刁難” 面試者,凡是都喜歡由淺入深,凡是都喜歡問個:你知道為什麼?你知道原理嗎?之類。並且,以前只是大廠面試官喜歡問演算法,大廠員工底子好,很多甚至有ACM經驗或者系統刷題經驗,這很容易理解,但現在一些小公司面試官也是張口閉口 xx演算法、xx資料結構你說說看,這不,真的被問到了。
求一個質數
在這麼一次的過程,面試官問我演算法題我不吃驚,我實現早把十大排序原理、複雜度分析、程式碼手寫實現出來了,也把連結串列、樹的各種操作溫習的滾瓜爛熟,不過突然就是很詫異的面試官來了一道求素數問題,我把場景還原一下:
面試官:你知道怎麼求素數嗎?
我:求素數?
面試官:是的,就是求素數。
我:這很簡單啊,判斷一個數為素數,那麼肯定就沒有兩個數(除了自身和1)相乘等於它,只需要列舉看看有沒有能夠被它整除的數就可以了,如果有那麼就不是素數,如果沒有,那麼就是素數。
面試官露出一種失望的表情,說我說的對,但沒答到點子上,讓我具體說一下。
下面開始開始我的表演:
首先,最笨的方法,判斷n是否為素數,就是列舉[2,n-1]之間有沒有直接能夠被n整除的,如果有,那麼返回false這個就不是素數,否則就是素數,程式碼如下:
boolean isprime(int value){ for(int i=2;i<value;i++) { if(value%i==0) {return false;} } return true; }
這種判斷一個素數的時間複雜度為O(n).
但是其實這種太浪費時間了,完全沒必要這樣,可以最佳化一下 。如果一個數不是質數,那麼必定是兩個數的乘積,而這兩個數通常一個大一個小,並且小的小於等於根號n,大的大於等於根號n,我們只需要列舉小的可能範圍,看看是否能夠被整除,就可以判斷這個數是否為素數啦。例如100=2*50=4*25=5*20=10*10 只需要找2—10這個區間即可。右側的一定有個對應的不需要管它。
boolean isprime(int value) { for(int i=2;i*i<value+1;i++) { if(value%i==0) {return false;} } return true; }
這裡之所以要小於value+1,就是要包含根號的情況,例如 3*3=9.要包含3.這種時間複雜度求單個數是O(sqrt(n))。面試官我給你畫張圖讓你看看其中區別:
說到這裡面試官露出欣慰的笑容。
面試官:不錯不錯,基本點掌握了
我:老哥,其實求素數精髓不在這,這個太低效在很多時候,比如求小於n的所有素數,你看看怎麼搞?
面試官:用個陣列用第二種方法求O(n*sqrt(n))還行啊。
求多個素數
求多個素數的時候(小於n的素數),上面的方法就很繁瑣了,因為有大量重複計算,因為 計算某個數的倍數 是否為素數的時候出現大量的重複計算,如果這個數比較大那麼對空間浪費比較多。
這樣,素數篩的概念就被發明和使用。篩的原理是從前往後進行一種遞推、過濾排序以來統計素數。
埃拉託斯特尼(Eratosthenes)篩法
我們看一個數如果不是為素數,那麼這個數沒有數的乘積能為它,那麼這樣我們可以根據這個思想進行操作啊:
直接從前往後列舉,這個數位置沒被標記的肯定就是素數,如果這個數是素數那麼將這個數的倍數標記一下(下次遍歷到就不需要在計算)。如果不是素數那麼就進行下一步。這樣數值越大後面計算次數越少,在進行具體操作時候可藉助陣列進行判斷。所以埃氏篩的核心思想就是將素數的倍數確定為合數。
假設剛開始全是素數,2為素數,那麼2的倍數均不是素數;然後遍歷到3,3的倍數標記一下;下個是5(因為4已經被標記過);一直到n-1為止。具體流程可以看圖:
具體程式碼為:
boolean isprime[]; long prime[]; void getprime() { prime=new long[100001];//記錄第幾個prime int index=0;//標記prime當前下標 isprime=new boolean [1000001];//判斷是否被標記過 for(int i=2;i<1000001;i++) { if(!isprime[i]) { prime[index++]=i; } for(int j=i+i;j<1000000;j=j+i)//他的所有倍數都over { isprime[j]=true; } } }
這種篩的演算法複雜度為O(nloglogn);別小瞧多的這個logn,資料量大一個log可能少不少個0,那時間也是十倍百倍甚至更多的差距。
尤拉篩
面試官已經開始點頭贊同了,哦哦的叫了起來,可其實還沒完。還有個線性篩—尤拉篩。觀察上述的埃氏篩,有很多重複的計算,尤其是前面的素數,比如2和3的最小公倍數為6,每3次2的計算就也會遇到是3的倍數,而尤拉篩在埃氏篩的基礎上改進,有效的避免了這個重複計算。
具體是何種思路呢?就是埃氏篩是遇到一個質數將它的倍數計算到底,而尤拉篩則是隻用它乘以已知曉的素數的乘積進行標記,如果素數能夠被整除那就停止往後標記。
在實現上同樣也是用兩個陣列,一個儲存真實有效的素數,一個用來作為標記使用。
- 在遍歷到一個數的時候,如果這個數沒被標記,那麼這個數存在素數的陣列中,對應下標加1.
- 不管這個數是不是素數,遍歷已知素數將它和該素數的乘積值標記,如果這個素數能夠被當前值i整除,那麼停止操作進行下一輪。
具體實現的程式碼為:
boolean isprime[]; int prime[]; void getprimeoula()// 尤拉篩 { prime = new int[100001];// 記錄第幾個prime int index = 0; isprime = new boolean[1000001]; for (int i = 2; i < 1000001; i++) { if (!isprime[i]) { prime[index++] = i; } for (int j = 0; j < index && i * prime[j] <= 100000; j++){//已知素數範圍內列舉 isprime[i * prime[j]] = true;// 標記乘積 if (i % prime[j] == 0) break; } } }
你可能會問為啥if (i % prime[j] == 0)就要break。
如果i%prime[j]==0,那麼就說明i=prime[j]*k. k為一個整數。
那麼如果進行下一輪的話
i*prime[j+1]=(prime[j]*k)*prime[j+1]=prime[j]*(k*prime[j+1]) 當i=k*prime[j+1]兩個位置就產生衝突重複計算啦,所以一旦遇到能夠被整除的就停止。
你可以看到這個過程,6只標記12而不標記18,18被9*2標記。詳細理解還需要多看看程式碼想想。過程圖就不畫啦!尤拉的思路就是離我較近的我給它標記。尤拉篩的時間複雜度為O(n),因為每個數只標記一次。
面試官露出一臉欣賞的表情,說了句不錯,下面就是聊聊家常,讓我等待下一次面試!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4550/viewspace-2796003/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 面試官:你知道Callback Hell(回撥地獄)嗎?面試
- 面試官:你連RESTful都不知道我怎麼敢要你?面試REST
- 面試官本拿求素數搞我,但被我優雅的“回擊“了(素數篩)面試
- 面試官問:多執行緒同步內部如何實現的,你知道怎麼回答嗎?面試執行緒
- 面試官本拿求素數搞我,但被我用素數篩優雅的“回擊“了面試
- 面試官問:你知道Redis能做什麼?不能做什麼?面試Redis
- 面試:你知道為什麼會有 Generator 嗎面試
- 面試官:你瞭解Webpack嗎?面試Web
- 面試官帶你學Android——面試中Handler 這些必備知識點你都知道嗎?面試Android
- 面試官:素有Java鎖王稱號的‘StampedLock’你知道嗎?我:這什麼鬼?面試Java
- 你知道著名的3畫素Bug指的是什麼嗎?怎麼解決呢?
- 面試官: 你瞭解前端路由嗎?面試前端路由
- 面試官問你基本型別時他想知道什麼面試型別
- 【面試官問】你懂函數語言程式設計嗎?面試函數程式設計
- 面試官:Java中物件都存放在堆中嗎?你知道逃逸分析?面試Java物件
- 面試官:前端跨頁面通訊,你知道哪些方法?面試前端
- 面試官問你陣列和ArrayList怎麼答?面試陣列
- 面試官:你分析過mybatis工作原理嗎?面試MyBatis
- 面試官:註解五問你怕了嗎?面試
- 技術面試中,當面試官「套路」你時,怎麼「反套路」回去?面試
- 假如面試官問你Babel的原理該怎麼回答面試Babel
- 面試官:網站的SEO你怎麼處理啊?面試網站
- 面試官:你知道哪些事務失效的場景?面試
- 面試官問:你瞭解HTTP2.0嗎?面試HTTP
- 面試官:你分析過SpringMVC的原始碼嗎?面試SpringMVC原始碼
- 面試官:你瞭解git cherry-pick嗎?面試Git
- 面試官:雙親委派模型你瞭解嗎?面試模型
- 面試官:小夥子知道synchronized的最佳化過程嗎?我:嘚吧嘚吧嘚,面試官:出去!面試synchronized
- 面試官:一千萬的資料,你是怎麼查詢的?面試
- 面試官:知道Java1.8中新加的StampedLock嗎?面試Java
- 當面試官問執行緒池時,你應該知道些什麼?面試執行緒
- 你知道併發使用者數應該怎麼算嗎?
- 面試官:十問泛型,你能扛住嗎?面試泛型
- 面試官問:Mybatis中的TypeHandler你用過嗎?面試MyBatis
- 面試官: 你平時用過讀寫鎖嗎?面試
- 面試官問,你使用過命令模式嗎?我笑了!面試模式
- 面試官:你真的瞭解Redis分散式鎖嗎?面試Redis分散式
- 面試官:Zookeeper叢集怎麼搭建?面試