我投的是基礎研究,感覺自己比較幸運,好像是順利的走了TST的內推,因為在筆試的時候監考官讓我們在試卷右上角標注TST。而且面試通知的也是直接去銀科大廈,在騰訊的茶水間面的。
接下來說說面試和筆試。
筆試其實挺無語的,和去年的題大量重合,我前一天剛好找了去年的題看,所以很多變態題都答上了。當然,其實還是有3,4道題根本沒記住答案(但是知道是去年的題),所以就亂答的。附加題認真答了一道,另一道基本不會,隨便寫了幾行。后來在一面的時候看到自己卷子的分數是71,面試官跟我說分數還不錯,我無語ing……
然后是面試,面試一共分2次,最后一次是廣州那邊經理的電面,可能在5.1左右才打過來。一來比較晚,二來不怎么問技術了,三來我還沒接到,所以就不說了。需要提一下的是,選事業群根本0效果,最后面試的部門還是騰訊自己定的,就是說每個部門自己從簡歷&筆試結果中挑覺得滿意的,然后通知他們去面試。也就是說,參加面試的時候,你的實習部門就已經定好了。面試我的部門是騰訊微信模式識別組,據面試官介紹微信的總部在廣州,不過他們這個30人的研發小組在北京,是從騰訊研究院分出來的,主要做基於微信的NLP,語音和圖像方面的識別算法。
一面:
面試官態度都很好,一共兩個人。第一個人主要問了大量簡歷上的內容,問的很細,很深。比如我獲過某個獎,那么他就會問我做負責什么部分,做的什么東西,是怎么做的。我的一段實習經歷他也很仔細的問了,基本上就是把我在那個公司做過的所有東西都介紹了,而且是細致到在設計xxx算法時設計和提取了xxx特征,這個xxx特征的具體是怎么提取的,以及物理意義等。總的來說,就是把簡歷里面能問的基本問了個遍。所以說,大家寫簡歷的時候,自己沒把握的最好別寫,因為有些面試官會問的很細,或者就挑那種你提了一句的東西使勁問,看你到底有沒有做過。簡歷大概問了半個小時,然后他說我們做幾道算法題吧,給了我三道題和一疊紙,給我一個小時做。題都不算難,不過要求編程實現,三道題分別是:
1.用最快的方法計算斐波那契數列
我覺得用o(n)非遞歸的方法就行,當然有o(log n)的更快方法,不過這個非遞歸挺難寫的(要是遞歸就達不到o(log n))。
注:有人提到了直接用公式來計算值,時間復雜度可以降低到o(1),真的如此嗎?
對此我進行了深入的調研,首先,這個公式里面包含了兩個求冪算式,也就是說需要pow函數,那么pow函數的原理是什么呢?在VC里面這個函數的源代碼是無法看到的,但是pow函數的原理還是可以查到的,因為在這里n只取整數,那么算一個數的整數次冪,最快的方法也是o(log n),所以,這部分的運算的實際時間復雜度是o(log n),並不是所謂的o(1)。
空口無憑,我編了一小段程序來驗證自己的想法,定義了兩個求斐波那契數列的函數,其中fab1是用公式算,fab2用o(n)的方法計算。
void fab1(double in){ double a=1/sqrt((double)5); double b=pow((0.5+sqrt((double)5)/2),in); double c=pow((0.5-sqrt((double)5)/2),in); //printf("%lf\n",a*(b-c)); }; void fab2(int in){ int a=0,b=1,res,i; for(i=2;i<=in;i++){ res=a; a=b; b+=a; } }; int main(){ LARGE_INTEGER freq,begin,end; QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&begin); for(int i=1;i<=100000;i++) fab1(50);//fab2(50); QueryPerformanceCounter(&end); double time=(double)(end.QuadPart-begin.QuadPart)/(double)freq.QuadPart*1000; printf("%lf\n",time); system("pause"); return 0; }
每次求n=50的值,循環取10000次和100000次,得到的結果如下:
循環10000次時,fab1平均時間在1.9微秒左右,fab2平均時間在2.5微秒左右
循環100000次時,fab1平均時間在18微秒左右,fab2平均時間在25微秒左右
所謂的o(1)時間復雜度無論如何也不可能和o(n)復雜度差距這么小吧?因此,斐波那契數列的最低時間復雜度就是o(log n),另外,由於公式中的數字都是浮點型,如果使用整型的o(log n)算法,理論上應該比這種公式法還要快。
2.給定整數sum,在亂序數組中尋找所有滿足x+y=sum的組合並打印
這個很簡單,就是先排序,然后維護一頭一尾兩個指針,找到所有組合,時間復雜度o(nlog n+n)
這個也可以用哈希做,時間復雜度o(n),不過數據的范圍不能太大。
3.在一個超長數組中尋找第K大數
這個不能用快排的partition做,因為數組超長,所以暗含不能全部裝入內存中。我當時沒太理解題目,所以寫了那個用partition做的算法,后來覺得有可能是考堆排序,又大致寫了堆排序那個算法的思路:讀入前K個數並建立一個大根堆,然后依次讀入數組中剩下的數並調整堆,最后從堆中輸出最小的那個數就行,時間復雜度o(klog k+n)
第二個面試官看了我的代碼和思路之后,覺得大致沒問題,然后說我再考你兩道吧,說思路就行。
1.在2個排序好的數組中找所有元素的第k大
我剛開始說直接調用一次歸並排序的merge就行,o(k)時間復雜度,面試官說讓我再想想,還有沒有更快的。然后我苦苦思索了5分鍾,想了一種o(log k)的,假設兩個數組是降序排列,先比較兩個數組的第k/2個數,如果相等,則這個數必為第k大的,如果不等,不妨假設A[k/2]<B[k/2],那么第k大的數必然在A[1]~A[k/2]和B[k/2]~B[k](假設數組下標從1開始)之中,因此,再比較這兩部分中間的數,即A[k/4]和B[3k/4],然后重復的二分查找,最后就能找到兩個數組中的第k大數。其實這個方法我當時是蒙的,並沒有嚴格的證明,不過后來回學校的時候,我自己又仔細的想了一下,的確是正確的。
2.找到1-10000以內的所有質數
如果之前沒有接觸過算法的,這道題還是有些難度的,因為挨個判斷質數的方法時間復雜度太高,不可行。一般的做法是,開辟一個有10000個元素的bool型數組prime[10000],然后從2開始到根號10000,即100結束,把這些數的倍數都設置為false,例如:取2的時候,prime[4],prime[6],prime[8],prime[10]...都會被設置為false,這樣結束之后,再從頭掃一遍prime,把里面還是true的數組的下標輸出,即為范圍內所有質數。不過,我當時很二的說了一句“這道題我做過”,現在想想實在是大忌啊,如果大家面試的時候遇到做過的題,一定不要馬上承認做過,因為有的面試官會很不爽,面子掛不住,感覺自己出的題簡單了,然后就會來一道巨難的題虐你。還好我遇到的這位面試官人比較隨和,說我還挺誠實,但是,不出意外的提高了題的難度。他說我這個基本已經接近最優的時間復雜度了,但是有些值,比如prime[6],在2和3的時候被重復設置了2次false,所以還有更快一點的,讓我想一想。我當時心里各種無語啊,之前做poj上面題的時候從來沒想過還有更快一點的。然后我冥思苦想了幾分鍾,想到了質數和合數的特點,把設置false的規則修改了一下,如果到第n個數,並且該數是true,那么從n開始,下標為n乘以n之后(包括n)為true的數組元素都被設置為false,例如:2的時候,4,6,8,10...都被設置為false,那么3的時候,3*3,3*5,3*7...都被設置為false,因為2,4..都是false了,所以不會出現3*2,3*4等被重復設置為false的情況,這樣就能保證每個合數僅被設置為false一次。因此,這種方法比原來的快一些(其實就快了一點點)。
答完了上面兩道題,一面基本就結束了,面試官就讓我回去等通知了。這次面試大概有2個小時,半個小時問簡歷,1個半小時問算法。
二面過程
二面是一位女面試官,人很好,完全看不出是做技術研發的,我的第一反應她應該是管理團隊的,不過面試一開始我就發現自己錯了,她的科研基礎實在是太好了。在此,再一次提醒大家,千萬不要把自己一知半解的東西放到簡歷上,遇上較真的面試官,肯定會悲劇的。接着說二面,她首先很有針對性的挑了我簡歷上那種一筆帶過的東西問,看我是不是真的做過。問了大概10分鍾,她開始問我的基礎知識,在模式識別和機器學習領域學習了什么,我跟她說對於常用的分類器都比較熟悉,她讓我對常用的分類器的熟悉程度排一下序,我說自己實現過adaboost,隨機森林沒寫過但是肯定能寫出來,svm用過,知道原理,神經網絡了解原理,最不熟悉。她接着讓我介紹神經網絡的原理(挑我最不熟悉……),我想了1分鍾,大致把節點的構成,怎么連接每一層,用梯度下降訓練之類的說了一通,說實話我好久沒看神經網絡了,的確不算很熟了。她可能不是很滿意吧,然后又追問svm和神經網絡的區別。一個是線性分類器,一個是非線性分類器,這個區別其實還是挺明顯的,接着我又跟她侃了一下最近比較火的深度學習(deep learning),表達了一下自己的看法,深度學習這個東西,其實計算復雜度太高,並不能覆蓋所有的機器學習領域,而且是否一定要依賴於神經網絡,其實也存在爭議,因為有國外的團隊用聚類(K-means)和線性分類器(SVM)組合的方式達到了跟受限波爾茲曼機(RBM)接近甚至超越的效果,大概說了20分鍾,她算是比較滿意我的回答了。接下來她問了一個很簡單但是挺值得注意的問題,在圖像處理方面經常需要讀取圖片,動態申請一個二維數組也很常見,她讓我寫一下我是怎么做的,下面代碼是兩種常見的方式
//方法1 unsigned char** pImg= new unsigned char*[m];
for(int i=0;i<n;i++){
pImg[i]=new unsigned char[n];
} //方法2
unsigned char** pImg= new unsigned char*[m];
unsigned char* arr= new unsigned char[m*n];
for(int i=0;i<m;i++){
pImg[i]=arr[i*n];
}
第一種都能想到,但是速度慢,申請內存是分開申請的,所以總的效率完全趕不上第二種。第二種是一次申請內存,for循環僅僅是對指針的賦值。另一方面,在釋放的時候,第二種容易忘記釋放二維指針的每個元素,也需要循環釋放。而第二種就沒有這種問題,只需要delete[] arr和delete[] pImg即可。
這道題我的答案似乎她很滿意,然后又跟我接着討論了一些搞算法要追求極致的思想等等,接着還給我展示了她們團隊在微信中負責的功能(搖一搖搜歌,這個的確很強大,我自己試過了),接下來又問了我一個牛頓迭代法,一個求方程數值解的方法,這個我的確沒學過……,她說我肯定學過,只不過早就忘了而已,而且她覺得這個方法很有意思,跟梯度下降非常相似。呃,我表示無語,跟她說回去一定看。這樣又過去大概10分鍾。最后,她總結了一下面試,說對我還是挺滿意的,覺得我基礎比較扎實。哦,她在面試中還提到了團隊里面做算法的基本都有acm經歷,我表示自己不是計算機科班出身,所以本科沒做過,她說這方面還是需要學一學的,我說現在已經學習了一部分,回去再繼續學。
總的感覺就是,第二輪面試就輕松了很多,大概只有40分鍾。面試官問的都是跟他們研究內容相關的知識,所以基礎知識很重要,另外,如果有些東西,自己不知道直說就是了,如果不懂裝懂反而讓面試官很反感。
當然,這個是我的面試經歷,不過面試是因人而異,因面試官而異的,所以大家不要照搬,如果能幫助到大家,那是最好了。里面應該還有一些錯誤和問題,望不吝賜教。