刚刷到这个帖,我第一反应就是,互联网这行真能把人拧巴坏。40+,以前P8年薪130万,歇了快两年,现在再回来拿70万offer,还在纠结接不接,外人一看可能会说这还犹豫啥,我倒觉得太正常了。

打开网易新闻 查看精彩图片

人不是只看绝对数字的,之前站过高处,再往下走,心里那个坎真不是一句“先上车再说”能过去的。更别说40+这个节点,本来就敏感,接了怕以后封顶,不接又怕市场继续往下掉,左右都别扭。

最扎心的是,很多人看到的是70万,只有当事人脑子里循环播放的是“我是不是回不去了”。这玩意儿吧,钱是钱,落差也是实打实的。

今日算法题

半夜写压测脚本的时候,这种题我一般不信“看起来对”的写法。 很多人上来就是先在 ([-r,r]) 里随便取个 xy ,然后直接塞进结果集。代码能跑,分布一点都不均匀。圆心附近和边缘附近的密度,肉眼一看就不对。

这题麻烦不在“生成随机数”,麻烦在“生成得像回事”。

先说最容易写、也最稳的一种:拒绝采样。 做法很直,先在外接正方形里随机打点,也就是 xy 都落在 [-r, r] 之间。再判断这个点是不是在圆内,只要满足 x*x + y*y <= r*r ,就收下;不满足就丢掉重来。

这种写法的好处是不用跟三角函数较劲,也不容易把概率分布写歪。

import java.util.concurrent.ThreadLocalRandom;

publicclassCirclePointGenerator{

publicstaticdouble randomPoint(double radius) {
ThreadLocalRandom random = ThreadLocalRandom.current;
while (true) {
double x = random.nextDouble(-radius, radius);
double y = random.nextDouble(-radius, radius);
if (x * x + y * y <= radius * radius) {
returnnewdouble{x, y};
}
}
}

publicstaticvoidmain(String[] args){
double point = randomPoint(10.0);
System.out.println("x=" + point[0] + ", y=" + point[1]);
}
}

这段代码看着土一点,但现场里我反而更愿意先写这个。原因很简单:不容易错。

有些同学会换一种思路,先随机角度,再随机半径:

double angle = random.nextDouble(0, 2 * Math.PI);
double len = random.nextDouble(0, radius);
double x = len * Math.cos(angle);
double y = len * Math.sin(angle);

这段代码最大的问题是: len 如果直接均匀随机,点会偏向圆心。 为什么?因为圆越往外,环带面积越大。你半径按线性均匀取,等于把小圆和大圆给了差不多的概率,这分布肯定不对。

要修正也不复杂,半径不能直接取,得开方:

import java.util.concurrent.ThreadLocalRandom;

publicclassCirclePointGenerator2{

publicstaticdouble randomPoint(double radius) {
ThreadLocalRandom random = ThreadLocalRandom.current;
double angle = random.nextDouble(0, 2 * Math.PI);
double len = Math.sqrt(random.nextDouble) * radius;
double x = len * Math.cos(angle);
double y = len * Math.sin(angle);
returnnewdouble{x, y};
}
}

这里的关键就一行:

double len = Math.sqrt(random.nextDouble) * radius;

别小看这个 sqrt ,少了它,分布基本就废了。

所以这题真要我在面试里写,我通常看场景选方案。 要的是稳、快、不容易翻车,我写拒绝采样。 要的是体现你真懂概率分布,我会补一句极坐标解法,并点明半径为什么要开方。