【interview——Ali】project interview_18 summer

彻底没有准备的一次面试……意外面试

两部分: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)

相关文章
相关标签/搜索