昨天参加了哈工大 SCIR 实验室的 2024 研究生招生笔试。试题很基础,但题量很大,一个半小时要完成一道逻辑题,一道文献翻译题,两道数学题,两道神经网络相关知识的题,两道编程题。反正我没做完。
本文给出两道数学题和两道编程题的题解,权当复习巩固基础。
这道题完全空着了,忘了 KL 散度怎么求了。究其原因是没有深入理解信息熵、交叉熵、相对熵那一套原理。
信息熵:
Hp(X)=−∫p(x)⋅log(p(x))dx −logb(p(x))=logb(p(x)1)可以理解为一个事件的不确定性程度,那么Hp(X)显然就是整个分布的期望。
交叉熵:
Hpo,ps(X)=−∫po(x)⋅log(ps(x))dx 其中,ps为主观认为的概率分布,或者说机器学习方法预测出来的概率分布,而po为客观的概率分布。可以理解为,我们带着某个主观认知去接触某个客观随机现象的不确定性程度。
相对熵(KL散度):
DKL(po∣∣ps)=Hpo,ps(X)−Hpo(X)=−∫po(x)⋅log(po(x)ps(x))dx 其实就是衡量交叉熵与信息熵的差值。
KL散度的若干条性质:
- KL散度大于等于 0,简单证明:
- 对x∈(0,1],有ln(x)≤x−1,从而
DKL(po∣∣ps)=−∫po(x)⋅ln(po(x)ps(x))dx≥−∫po(x)⋅(po(x)ps(x)−1)dx=−∫po(x)⋅(po(x)ps(x)−1)dx=−(∫ps(x)dx−∫po(x)dx)=0
- 可以理解为两个分布的距离,但是并不满足对称性和三角不等式
回到本题:
DKL(N(μ1,σ12)∣∣N(μ2,σ22))=∫x2πσ11e−2σ12(x−μ1)2log2πσ21e−2σ22(x−μ2)22πσ11e−2σ12(x−μ1)2dx=∫x2πσ11e−2σ12(x−μ1)2[logσ1σ2−2σ12(x−μ1)2+2σ22(x−μ2)2]dx 第一项:
logσ1σ2∫x2πσ11e−2σ12(x−μ1)2dx=logσ1σ2 第二项要看出里面是方差:
−2σ121∫x(x−μ1)22πσ11e−2σ12(x−μ1)2dx=−2σ121σ12=−21 第三项,注意E(x)2=D(x)+E(x2)
2σ221∫x(x−μ2)22πσ11e−2σ12(x−μ1)2dx=2σ221∫x(x2−2μ2x+μ22)2πσ11e−2σ12(x−μ1)2dx=2σ22σ12+μ12−2μ1μ2+μ22=2σ22σ12+(μ1−μ2)2 综上:
DKL(N(μ1,σ12)∣∣N(μ2,σ22))=logσ1σ2−21+2σ22σ12+(μ1−μ2)2 有 n 个人,每次坐成一圈,为了使每次每个人的邻居与之前都不同,则坐法最多有几次?
实际上可以抽象为:完全图Kn中最多有多少个边不重复的哈密顿圈。
很明显,要将每个人都认识一遍且不重复,不会超过⌊2n−1⌋次,主要是考虑如何构造:
奇数:
![](/assets/images/scir_1-a61be791f80213d71792276d1216f189.png)
可以旋转2n−1次
偶数:
![](/assets/images/scir_2-b00c4be794b705ed68eae187bbece73e.png)
可以旋转2n−2次
故,答案为⌊2n−1⌋
原题:72. 编辑距离 - 力扣
经典的字符串 dp 题:
int minDistance(string word1, string word2) {
int m = word1.size(), n = word2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
for (int i = 0; i <= m; i++) dp[i][0] = i;
for (int j = 0; j <= n; j++) dp[0][j] = j;
for (int i = 1; i <= m; i++) {
for(int j = 1; j <= n; j++) {
dp[i][j] = min(dp[i-1][j] + 1, dp[i][j-1] + 1);
if (word1[i - 1] == word2[j - 1]) {
dp[i][j] = min(dp[i][j], dp[i-1][j-1]);
}
else {
dp[i][j] = min(dp[i][j], dp[i-1][j-1] + 1);
}
}
}
return dp[m][n];
}
原题:239. 滑动窗口最大值 - 力扣
主要考虑如果 i > j,并且 nums[i] > nums[j],则 j 就可以丢弃,故维护一个单调队列:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
deque<int> d;
vector<int> ans;
for(int i = 0; i < n; i++) {
while(!d.empty() && nums[d.back()] <= nums[i]) d.pop_back();
d.push_back(i);
if(i >= k - 1) {
while(!d.empty() && d.front() <= i - k) d.pop_front();
ans.push_back(nums[d.front()]);
}
}
return ans;
}