Python技术迷

研发7年,目前年包80左右。是外企继续苟着还是回老家国企?

刚看到个贴子,说一位34岁的研发,干了7年,年包80万,现在纠结要不要回老家国企,月薪7k,正式合同但无编制。说实话,这问题戳中了很多打工人的痛点。

Image

我觉得吧,这不是简单的“钱多还是钱少”的选择,而是“你想过什么样的生活”。外企80万听着香,但焦虑、加班、随时被优化;回国企虽然稳,却得接受节奏慢、收入差距大的落差。网友有说“国企养老”,也有人说“回去就是废掉”,但我更认同:人得明白自己要什么。

如果真是累了想躺平,那就别怕低工资,能换来心安;可要是心里还不甘平庸,那就继续卷几年,把底子打厚。生活没有绝对轻松的路,关键是选自己能扛得住的那条。【备注:文末可领最新资料】

面试题:按权重随机选择

昨天晚上十一点多,在公司楼下抽烟…不对我不抽烟,反正就楼下吹风的时候,小李问我“按权重随机怎么写,random那个啥…choices?”我说有,但你别全信,咱把底层思路捋一遍,写两种版本,心里才踏实。就是那个…嗯,按权重抽签,权重大的人被抽到的概率更大,对吧。

先说最顺手的思路:把权重累加成一条“尺子”,随便在0到总长之间戳一下,落在哪段就选谁。这个靠谱,直观,也好调试。注意两点,一个是权重可能是浮点数,别强行转整;另一个是全是0就完蛋了,要提前校验。代码我用 bisect,二分查找定位区间,复杂度 O(log n),日常够用了。

import random
import bisect
from typing import List, Tuple, Any

defweighted_choice(items: List[Tuple[Any, float]]):
# items: [(对象, 权重)]
    weights = [w for _, w in items]
if any(w < 0for w in weights):
raise ValueError("权重不能是负数")
    total = sum(weights)
if total <= 0:
raise ValueError("总权重必须>0")
# 前缀和
    prefix = []
    s = 0.0
for w in weights:
        s += w
        prefix.append(s)
    r = random.random() * total
    idx = bisect.bisect_left(prefix, r)
return items[idx][0]

# 小测一把
if __name__ == "__main__":
    items = [("A", 1), ("B", 3), ("C", 6)]
    counts = {"A":0,"B":0,"C":0}
for _ in range(10000):
        counts[weighted_choice(items)] += 1
    print(counts)

刚打到这儿,手机响了…等等我接个电话…好了继续。上面那套在 n 很大、抽很多次的时候,其实构建一次前缀和,然后每次 O(log n)。要是你要在热路径里怼上百万次,还是想要“近似 O(1)”的感觉,那就上别名法(Alias Method),准备期 O(n),采样期 O(1)。思想是把每个格子都“填满”成平均概率,矮的借高的的剩余,采样时先选格子再抛一次硬币。实现别怕,几十行。

classAliasSampler:
def__init__(self, weights: List[float]):
if any(w < 0for w in weights):
raise ValueError("权重不能是负数")
        total = sum(weights)
if total <= 0:
raise ValueError("总权重必须>0")
        n = len(weights)
        probs = [w * n / total for w in weights]
        self.alias = [0]*n
        self.prob  = [0.0]*n
        small, large = [], []
for i, p in enumerate(probs):
            (small if p < 1.0else large).append(i)
while small and large:
            s = small.pop()
            l = large.pop()
            self.prob[s] = probs[s]
            self.alias[s] = l
            probs[l] = probs[l] - (1.0 - probs[s])
            (small if probs[l] < 1.0else large).append(l)
# 收尾
for i in large + small:
            self.prob[i] = 1.0
            self.alias[i] = i

defsample(self) -> int:
        i = random.randrange(len(self.prob))
return i if random.random() < self.prob[i] else self.alias[i]

# 用法
if __name__ == "__main__":
    items = ["A","B","C"]
    sampler = AliasSampler([1,3,6])
from collections import Counter
    c = Counter(items[sampler.sample()] for _ in range(10000))
    print(c)

有人问细节对吧:权重特别大怎么办?别紧张,前缀和用 float,或者先把权重归一化(除以总和),但要注意累计误差,比较时尽量用二分别直接等值;另外“按权重不放回抽多个”的场景,简单暴力的是每次把被抽到的权重设为0再来一发,如果次数接近 n,干脆用洗牌+加权重排个序(Gumbel trick 也行,不过这就…算了不展开)。我现在有点困,回头把那个性能对比图再贴,你们先把这俩函数抄到工具库里,够用了。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

🔥虎哥私藏精品🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领