某字节员工直言,做程序员久了,发现大家都有个共同之处,就是家庭条件真的都不太好,不绝对,但是不少人都是这种情况
刚看到个贴子,说是某字节员工直言:程序员干久了,会发现一个共性——很多人家庭条件一般,甚至可以说不太好。
我觉得这话吧,其实挺真实。编程这条路,说白了就是门“技术逆袭”的赛道。家境优渥的孩子,可能更愿意去投身金融、创业或者有资源的行业;而很多程序员,是靠一台电脑、一堆代码硬啃出来的,算是最能“用实力换饭吃”的职业。
网友们有人调侃“怪不得卷成这样”,也有人说“这才是普通家庭孩子最公平的赛道”。我比较认同后者,毕竟写代码是个拼逻辑和执行的事,背景影响小,努力和天赋能直接转化为收入。换句话说,这个行业对普通家庭孩子来说,反而是一条最靠谱的上升通道。【备注:文末可领最新资料】
面试题:统计实验的数量
昨天晚上十一点多,我在公司楼下嚼着冷掉的煎饼,小李蹭过来一句“东哥,咱那实验日志乱成一锅粥,怎么快速数今天到底做了几次实验啊?”我脑子里“咔”一下就有数了——别慌,先把问题说清楚再下手,对吧。
我们有一堆事件日志,像这样三列:timestamp, action, exp_id。action 只有两种:start / end。麻烦点在于: 1)事件可能乱序;2)同一个 exp_id 可能被重试,多次开始多次结束;3)有些开始没结束(崩了),有些结束没开始(补写的脏数据)。 目标很朴素:统计“有效实验次数”——也就是能被一对 start-end 正常配对的次数。另外顺手算下并发峰值、平均耗时,值了。
乱序最大的敌人,用排序解决。按 timestamp 升序把日志排好,接着给每个 exp_id 准备一个“小栈”(其实就是列表):
来一条 start,把开始时间压进去;来一条 end,如果栈里有对应开始时间,就弹出一条,记一次有效实验,顺便算时长;如果栈是空的,说明这是“孤儿 end”,直接忽略。 同时我们维护一个全局计数running,遇到start+1,匹配成功的end-1,用它滚动记录并发峰值。
from typing import List, Tuple, Dict
from collections import defaultdict
Log = Tuple[int, str, str] # (timestamp, action, exp_id)
defcount_experiments(logs: List[Log]):
# 1) 排序:时间升序
logs_sorted = sorted(logs, key=lambda x: x[0])
# 2) 每个实验ID一个“开始时间栈”
starts: Dict[str, List[int]] = defaultdict(list)
valid_cnt = 0
durations = [] # 收集每次有效实验的耗时(秒)
running = 0
peak_concurrency = 0
for ts, action, eid in logs_sorted:
if action == "start":
starts[eid].append(ts)
running += 1
if running > peak_concurrency:
peak_concurrency = running
elif action == "end":
if starts[eid]: # 有可配对的开始
t0 = starts[eid].pop()
valid_cnt += 1
durations.append(ts - t0 if ts >= t0 else0)
running = max(0, running - 1)
else:
# 孤儿 end,忽略即可
pass
else:
# 未知动作,按脏数据忽略
pass
avg_duration = (sum(durations) / len(durations)) if durations else0.0
# 还留在 starts 里的开始事件就是未完成实验,按需求这里不记入有效次数
return {
"valid_experiments": valid_cnt,
"peak_concurrency": peak_concurrency,
"avg_duration": avg_duration,
"unfinished_by_id": {eid: len(stk) for eid, stk in starts.items() if stk}
}
# 小例子,昨天中午抓的那点样本
if __name__ == "__main__":
sample = [
(12, "start", "A"),
(15, "start", "A"), # A 第二次重试
(16, "end", "A"), # 配到 15
(20, "start", "B"),
(25, "end", "A"), # 配到 12
(26, "end", "B"),
(30, "end", "C"), # 孤儿 end
(35, "start", "C") # 未完成
]
print(count_experiments(sample))
跑这段,你会拿到四个关键信息:
valid_experiments:有效实验总数(配对成功的次数);peak_concurrency:最高并发有多少个实验在跑;avg_duration:成功实验的平均耗时;unfinished_by_id:哪些exp_id还残留未完成(可能是挂了)。
时间复杂度其实挺朴素:排序是 O(n log n),后面一趟扫 O(n),整体就 O(n log n);空间就是给每个 exp_id 开个小栈,O(k)。 异常数据怎么处置?这玩意儿别纠结:孤儿 end 忽略,孤儿 start 视为未完成;如果你们审计严一点,也可以把这些都记到告警里。 还有人问“能不能只看今天?”那就在进函数前把 timestamp 用一天的范围先 filter 一下,别把夜里两点的脏数据算进来,容易挨骂。
行了我先去泡杯茶,谁要是要把它接到 Kafka 消费里头,直接在回调里喂 logs 就行,别忘了开窗口聚合,不然内存顶不住,嗯就这样先。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领