徹底沒有準備的一次面試……意外面試
兩部分:Word2vec + 中位數 (還有聊對科研的想法和本身研究能力的評價?算法
word2vec //解釋模型
本來是one-hot,存在缺點:稀疏和沒法表現語義,詞與詞之間沒法計算距離 //CBOW和skip-gram簡要介紹一下是什麼
CBOW模型的訓練輸入是某一個特徵詞的上下文相關的詞對應的詞向量,而輸出就是這特定的一個詞的詞向量。 Skip-Gram模型和CBOW的思路是反着來的,即輸入是特定的一個詞的詞向量,而輸出是特定詞對應的上下文詞向量。 //這兩種算法如何選擇
CBOW對小型數據比較合適,skip-gram在大型語料中表現更好 //word2vec的損失函數是什麼
NCE-loss: NCE的主要思想是,對於每個樣本,除了他本身的label,同時採樣出N個其餘的label,從而咱們只須要計算樣本在這N+1個label上的機率,而不用計算樣本在全部label上的機率。 峯哥教我是負採樣?使用所謂的「負採樣」(negative sampling)來改進優化對象,這將形成每個訓練的樣本只會更對模型權重的很小一個比例的更新。
//瞎扯了交叉熵
由於上面的問題回答錯了嘛……sigh 信息熵 + 相對熵= 交叉熵 K-L散度:就是相對熵,是一種量化兩種機率分佈P和Q之間差別的方式。 換句話說,KL散度計算的就是數據的原分佈與近似分佈的機率的對數差的指望值。
如何找出一個整數數組的中位數:數組
//最簡單的思路
排序,quicksort而後找中位數【nlogn】 //quicksort的最差狀況
時間複雜度【n^2】就是每次選定的數都是最大或者最小 //改進一點的找中位數的作法
partition法 ,利用快排關鍵字的查找方法 a.隨機選取一個關鍵字key,將序列二分; b.若關鍵字的下標大於N/2,則繼續對序列的左半部分執行partition; c.若關鍵字的下標小於N/2,則繼續對序列的左半部分執行partition; d.若關鍵字的下標等於N/2,則返回key。 這種算法:partition的時間複雜度爲【O(n)】由於是n+n/2+n/4+n/8+…因此是O(n) 取中位數的時間複雜度是【O(1)】 簡單解釋就是:每次會丟掉一半 //堆
原理分析: 中位數無非就是將序列分爲兩個部分,左邊的部分都小於中位數,右邊的序列都大於中位數。這比較符合堆的特性(看看數據結構在算法中的重要性,選擇好的數據結構可以讓算法事半功倍)。能夠將序列分紅兩個部分,左邊的部分夠着大根堆,右邊的部分構造小根堆。 具體實現細節: a.若是堆中元素的個數爲偶數時,將新數字插入小根堆中(插入後堆元素的個數爲奇數,此時結束插入,返回小根堆堆頂元素);若是堆中的元素個數爲奇數時,將新數字插入大根堆中(插入後堆元素的個數爲偶數,此時結束插入,返回兩堆堆頂元素的均值)。 b.若插入小根堆的元素大於大根堆堆頂的元素,說明新元素位於序列的右半部分,應當插入大根堆。而此時大根堆堆頂元素應當位於左半序列(小頂堆)中,所以須要將大根堆堆頂元素插入小根堆。若插入若插入小頂堆的元素不大於大頂堆堆頂的元素,則直接插入小根堆。 c.同理,向大根堆插入元素時也有如上考慮。 調整堆的時間複雜度爲【O(logn)】,取中位數的時間複雜度爲【O(1)】。 參考:https://blog.csdn.net/cpu_12593/article/details/48231213
面試題41
partition函數找中位數的寫法(作遞歸以前要判斷):數據結構
求第k大數的時候,pivot的不知足條件的那一側數據不須要再去處理了,平均時間複雜度爲O(N+N/2+N/4+...)=O(N)。而快排則須要處理,複雜度爲O(nlogn)。less
public int findMiddleByPartition(int[] array, int left, int right) {
int i = left, j = right;
int key = array[left];
while(i < j) {
// j向左走,直到找到一個小於key的數
while(i < j && array[j] >= key) j--;
if(i < j) {
array[i] = array[j];
i++;
}
// i向右走,直到找到一個大於key的數
while(i < j && array[i] <= key) i++;
if(i < j) {
array[j] = array[i];
j--;
}
}
array[i] = key;
if(i == array.length / 2) {
return i;
}else if(i < array.length / 2){
return findMiddleByPartition(array, i + 1, right);
}else {
return findMiddleByPartition(array, left, i - 1);
}
}
堆排序的方法,本身的語言複述:函數
將數據平均分配到構建的最大堆和最小堆中,爲了實現平均分配,能夠在數據的總數目是偶數的時候把新數據插入最小堆,不然插入最大堆。優化
爲了保證最大堆中的全部數據都小於最小堆中的數據,當數據的數目是偶數的時候,先把這個新的數據加入最大堆,而後把最大堆中的最大數字拿出來插入最小堆,反之當數據中的數目是奇數的時候,先把這個新的數據加入最小堆,而後把最小堆中的最小值加入最大堆。ui
template<typename T> class DynamicArray{ public: void Insert (T num){ if ((min.size() + max.size())&1 == 0){ //這是偶數 if (max.size()>0 && num <max[0]){ max.push_back(num); //加入最大堆 push_heap(max.begin(). max.end(), less<T>()); num = max[0]; //取max的最大 pop_heap(max.begin(), max.end(), less<T>()); max.pop_back(); } min.push_back(num); //將max的最大放入min push_heap(min.begin(),min.end(),greater<T>()); } else{ //相似上面的部分,只是加入最小堆,把最小堆的最小取出來,放入最大堆 } } T GetMedian(){ //返回中位數 int size = min.size() + max.size(); if (size == 0) throw exception("No numbers are available"); T median = 0; if ((size &1)==1) median = min[0]; else median = (min[0]+max[0])/2; return median; } private: vector<T> min; vector<T> max; //用vector實現堆!!! }
附加題:若是數據很是多,要肯定準確的中位數,應該怎麼作?spa
這裏,採用基於二進制位比較 和 快速排序算法中的「分割思想」來尋找中位數。具體以下:.net
假設10億個數字保存在一個大文件中,依次讀一部分文件到內存(不超過內存的限制:1GB),將每一個數字用二進制表示,比較二進制的最高位(第32位),若是數字的最高位爲0,則將這個數字寫入 file_0文件中;若是最高位爲 1,則將該數字寫入file_1文件中。【這裏的最高位相似於快速排序中的樞軸元素】
從而將10億個數字分紅了兩個文件(幾乎是二分的),假設 file_0文件中有 6億 個數字,file_1文件中有 4億 個數字。那麼中位數就在 file_0 文件中,而且是 file_0 文件中全部數字排序以後的第 1億 個數字。
【爲何呢?由於10億個數字的中位數是10億個數排序以後的第5億個數。如今file_0有6億個數,file_1有4億個數,file_0中的數都比file_1中的數要大(最高位爲符號位,file_1中的數都是負數,file_0中的數都是正數,也即這裏一共只有4億個負數,排序以後的第5億個數必定是正數,那麼排序以後的第5億個數必定位於file_0中)】。除去4億個負數,中位數就是6億個正數從小到大排序以後 的第 1 億個數。
如今,咱們只須要處理 file_0 文件了(不須要再考慮file_1文件)。對於 file_0 文件,一樣採起上面的措施處理:將file_0文件依次讀一部分到內存(不超內存限制:1GB),將每一個數字用二進制表示,比較二進制的 次高位(第31位),若是數字的次高位爲0,寫入file_0_0文件中;若是次高位爲1,寫入file_0_1文件 中。
現假設 file_0_0文件中有3億個數字,file_0_1中也有3億個數字,則中位數就是:file_0_0文件中的數字從小到大排序以後的第1億個數字。
拋棄file_0_1文件,繼續對 file_0_0文件 根據 次次高位(第30位) 劃分,假設這次劃分的兩個文件爲:file_0_0_0中有0.5億個數字,file_0_0_1中有2.5億個數字,那麼中位數就是 file_0_0_1文件中的全部數字排序以後的 第 0.5億 個數。
......
按照上述思路,直到劃分的文件可直接加載進內存時(好比劃分的文件中只有5KW個數字了),就能夠直接對數字進行快速排序,找出中位數了。固然,你也使用「快排的分割算法」來找出中位數(比使用快速排序要快)
總結:上面的海量數據尋找中位數,其實就是利用了「分割」思想,每次將 問題空間 大約分解成原問題空間的一半左右。(劃分紅兩個文件,直接丟棄其中一個文件),故總的複雜度可視爲O(logN)