Python技术迷

疯了,英伟达北京员工,年薪1688万,光个税就扣了687万

刚看到个贴子,说英伟达北京有员工一年总包1688万,个税都交了687万…我是真人都看傻了,这税额在很多城市都能全款买套房了,人家只是“交个税”。

Image

网友们的回帖有感叹不公平的,也有说“人家能力配得上”,我倒觉得两边都有点道理。确实,普通人一年干到掉头发也拿不到别人一张股票的零头,可这薪资背后是超级稀缺岗位、全球竞争、长年高压博弈,真不是随便谁都能拿。

赚钱这事,从来就是价值越稀缺,溢价越离谱。

换个角度想,这种收入水平离我们很远,但看到这种信息,也不必自我打击。你知道它存在,但不影响你脚下继续走。

还是心态放平,见识一下世界的参差,也提醒自己踏实提升能力,能多一点成长就多一点主动权【备注:文末可领最新资料】

面试题:LFU 缓存

我跟你说个事哈,昨天晚上十一点多,我刚准备关电脑刷会儿视频,我们组那个小李突然在群里喊救命,说面试被问了一道 LFU 缓存的题,当场脑子一片空白,让我帮他恶补一下。正好你也问这个,那我就按当时跟他唠嗑的版本,给你从头捋一遍,用 Python 写。 应用 系统内存就那么点儿,对吧,同时只能“宠幸”有限几个应用,多了就得干掉一个。怎么选?最粗暴的是“随机干掉”,稍微聪明点的是 LRU(最近最少使用),就是谁最久没用谁走人。

LFU 呢,全称 Least Frequently Used,意思是:谁被用得最少次数,谁走。次数相同再比谁最久没用,这样看起来就比 LRU 更“公平”一点。

别急着上代码,先把规则讲清楚:

我们这个 LFU 缓存要支持两个操作:

  • get(key):拿数据
  • put(key, value):塞数据(可能会触发淘汰)

然后得满足几个点:

  1. get 和 put 都要接近 O(1) 时间,不能每次都全表扫描。
  2. 如果容量满了,再 put 新 key,就要踢掉使用频率最低的 key。
  3. 频率相同时,踢掉最早用的那个(相当于对同一频率再做一层 LRU)。

听起来有点绕是吧?小李当时就说:“哥,这不是要同时记住 value、访问次数、最近访问顺序,脑子不够用了啊……”

我当时在公司楼下抽烟,给他画了个很丑的图,大概就是 3 层结构:

  1. key -> value          这层大家都懂

  2. key -> freq           记录这个 key 被用了多少次

  3. freq -> 一堆 key 的有序队列

  • 每个频率下面有一串 key
  • 队列里越靠前的,表示越早被访问(所以需要淘汰的时候,从队头踢人)

用代码表示就是这么几个东西:

  • self.key2val = {}
  • self.key2freq = {}
  • self.freq2keys = {freq: OrderedDict()}  —— 用 OrderedDict 模拟“按访问时间排好队的 key 列表”
  • self.minFreq 记当前最小频率是多少,方便一眼就知道从哪一层删。

关键操作其实就两个词:增频率 和 淘汰。

先说增频率这个事,get(key) 或 put 里更新已有 key 时都要干:

  1. 查出这个 key 当前频率 f = key2freq[key]
  2. 从 freq2keys[f] 里把它删掉
  3. 如果删完后,这个频率下面空了,而且 f == minFreq,那说明最小频率要往上挪一格,minFreq += 1
  4. 再把它放到 freq2keys[f+1] 的队尾(表示刚刚被使用过)
  5. 更新 key2freq[key] = f + 1

淘汰就简单粗暴点:

  • 当容量满了,又来了一个新 key:

    • 找到当前 minFreq
    • 在 freq2keys[minFreq] 这个队列里,从队头 popitem(last=False),就拿到了一个“最不常用&最久不用”的 key
    • 把它从 key2val、key2freq 里删掉
    • 如果那一层队列空了,可以顺手删掉这个 freq 的节点(del freq2keys[minFreq])

然后新 key 一律从频率 1 开始,minFreq 也重置成 1。

行,说了这么多,该上 Python 代码了,小李就是看完这段才恍然大悟的:

from collections import OrderedDict

classLFUCache:
def__init__(self, capacity: int):
        self.capacity = capacity
if capacity <= 0:
# 容量为 0 的时候,直接摆烂,所有操作都无效
return
        self.key2val = {}          # key -> value
        self.key2freq = {}         # key -> freq
        self.freq2keys = {}        # freq -> OrderedDict(key -> None)
        self.minFreq = 0

def_increase_freq(self, key: int):
"""内部函数:把 key 的使用次数 +1,并调整在各个表中的位置"""
        freq = self.key2freq[key]
# 从旧频率的队列里删掉
        keys_at_freq = self.freq2keys[freq]
        keys_at_freq.pop(key)
ifnot keys_at_freq:  # 这个频率下没人了
del self.freq2keys[freq]
if self.minFreq == freq:
                self.minFreq += 1

# 放到新频率的队列末尾
        new_freq = freq + 1
        self.key2freq[key] = new_freq
if new_freq notin self.freq2keys:
            self.freq2keys[new_freq] = OrderedDict()
        self.freq2keys[new_freq][key] = None

defget(self, key: int) -> int:
if self.capacity <= 0:
return-1
if key notin self.key2val:
return-1
# 被访问了一次,频率 +1
        self._increase_freq(key)
return self.key2val[key]

defput(self, key: int, value: int) -> None:
if self.capacity <= 0:
return

if key in self.key2val:
# 已有 key,更新值 + 提频率
            self.key2val[key] = value
            self._increase_freq(key)
return

# 容量满了,先淘汰一个
if len(self.key2val) >= self.capacity:
# 找最小频率那一层
            keys_at_min = self.freq2keys[self.minFreq]
# OrderedDict 按插入顺序排,last=False 表示从最老的那个 pop
            old_key, _ = keys_at_min.popitem(last=False)
ifnot keys_at_min:
del self.freq2keys[self.minFreq]
# 把这个 key 从其他表中删掉
del self.key2val[old_key]
del self.key2freq[old_key]

# 插入新 key,频率从 1 开始
        self.key2val[key] = value
        self.key2freq[key] = 1
if1notin self.freq2keys:
            self.freq2keys[1] = OrderedDict()
        self.freq2keys[1][key] = None
        self.minFreq = 1

你注意看哈,整个实现里面其实就两个比较绕的点:

  • 一个是 freq2keys 这个结构:外层按频率分组,内层用 OrderedDict 再按时间排序,这样既满足“按次数淘汰”,又兼顾“同次数里按时间淘汰”;
  • 另一个是 minFreq 的维护:只要某个频率的桶空了,而且它刚好是当前最小频率,就让它往上加一,这样你随时都知道“从哪一层开始踢人”。

其余的都是搬砖:增频率的时候从一个桶挪到另一个桶,满了就从最小频率的队头踢掉一个。

反正小李看完之后,第二天又去面了同一家,面试官刚说到 LFU,他直接一顿嘴炮加伪代码,整个人挺自信的。你要是再多敲几遍这段代码,顺嘴能说出“key2val、key2freq、freq2keys、minFreq 各自干嘛的”,基本这题就稳了。

行了我先去喝口水,你要是想顺带再聊聊和 LRU 的对比或者怎么用双向链表手撸一版,不用 OrderedDict 的那种,也可以继续问。

-END-

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

🔥虎哥私藏精品🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB