某大厂员工:选leader-定要选有家庭财富相对自由的中年人,如果你的leader是个奋斗b、光棍,那你注定就是他的耗材和垫脚石
刚看到个贴子,说大厂打工人选 leader,一定要挑那种家里底子厚、生活稳定的中年本地人。网友们还补刀:遇到奋斗狂光棍领导,你大概率就是他 KPI 路上的耗材。
说得虽狠,但确实扎心。
网友回复里,有人喊“努力型 leader 会带着全组一起拼”,也有人说“太拼的领导就是把你当发动机烧”。我看了看,嗯……都有点道理。
怎么说呢,一个 leader 本身压力太大、还在为房贷车贷拼命时,他很难顾及团队的感受,就像开车的人自己都刹不住了,还哪管你坐得稳不稳;但如果对方生活相对自由、心态稳,那做决策的节奏也更从容,不容易用团队去填坑。
不过话说回来,选 leader 也没那么玄学,关键还是看对方的价值观和带人方式。生活稳定固然加分,但有些“奋斗型”领导也是真拼团队的未来,而不是拼你的小命。
面试题:换座位
先说题目长什么样子哈,我用一个比较常见、也好讲算法的版本:
有 (n) 个同学,座位从 1 到 (n) 编号,一开始同学 i 坐在座位 i。 老师每次“换座位”的规则是:
第 1、2 号座位上的人互换 第 3、4 号座位上的人互换 第 5、6 号座位上的人互换 ……以此类推 如果 (n) 是奇数,最后一个人不动
给你 (n) 和“换座位”操作执行的次数 (k)(可能特别大,比如 (10^{12}) 这种),问最后每个同学坐在哪个座位上,或者最后每个座位上是谁。
最直接想到的办法就是老老实实模拟:
开一个数组 pos[i]表示“座位 i 上是谁”每次从 1 开始,每两个位置互换一下 把这个过程做 k 轮
伪代码差不多这样:
for step in range(k):
for i in range(1, n, 2):
swap(pos[i], pos[i+1])
问题也很明显: 外层要做 k 轮,内层每轮要扫一遍座位,时间复杂度 (O(nk))。 如果 n = 10^5,k = 10^9,这谁都跑不动,直接超时。
所以,重点就是:这么重复的换座位,其实有没有某种规律?
(顺带说一句,实际工作里你要是按“最笨办法”搞循环,通常就跟数据库、MQ那些默认配置一样,早晚要踩坑的…)
把“换座位”看成一次固定的置换
换个角度看这个操作:
每做一轮换座位,实际上就是把座位重新洗了一遍顺序 这种“把一堆位置重新映射到另一个位置”的东西,在数学上叫置换(permutation)
我们可以先只关心“一个同学会从哪个座位被搬到哪个座位”。
对于一次换座位操作:
如果 i 是奇数,而且 i + 1 <= n → i 和 i + 1 互换,座位 i 的人会去座位 i+1 如果 i 是偶数 → 座位 i 的人会去座位 i-1 如果 i 是最后一个奇数(比如 n 是奇数的 n) → 这个人不动,还是 i
所以我们可以先构造一个数组 P[i]:
P[i]表示:一次换座位之后,原来在座位 i 的人,会跑到座位 P[i]
用 Python 表示就是:
defbuild_perm(n):
P = list(range(n)) # 用 0..n-1 表示座位 1..n
for i in range(0, n, 2):
if i + 1 < n:
# i 和 i+1 互换
P[i], P[i+1] = i+1, i
else:
# 最后一个没人跟他换
P[i] = i
return P
注意我在代码里用的是 0 索引(座位 0..n-1),写起来方便一点。
现在问题就变成了:我们有一个置换 P,要把它应用 k 次,结果是什么?
重复 k 次,其实就是做“置换的快速幂”
你可以把“应用一次 P”理解成一个函数:
f(i) = P[i]:一次换座位后,i 这个位置上的人跑到了 P[i]
那做两次是什么? 第一次:i → P[i]第二次:P[i] → P[P[i]]
所以两次之后,相当于:
f2(i) = P[P[i]]
这其实就是“置换的组合”:
把两个置换 A、B 合成一个置换 C: C[i] = B[A[i]]
那做 k 次呢?就是:
一次: (P) 两次: (P \circ P = P^2) 三次: (P \circ P \circ P = P^3) … k 次: (P^k)
听上去很像什么? 对,就是快速幂,只不过底数不再是数字,而是一个“置换数组”而已。
我们可以像算 (x^k) 那样,按二进制拆解 k:
如果 k 的某一位是 1,就把当前这个“幂次的 P”合并进答案 每次把 P 自己和自己合成一次(就像平方)
置换的组合用代码写一下:
defcompose(A, B):
"""
返回 C = B(A(i)),也就是先应用 A,再应用 B
都是 0..n-1 的置换
"""
n = len(A)
C = [0] * n
for i in range(n):
C[i] = B[A[i]]
return C
然后写个快速幂:
defperm_power(P, k):
n = len(P)
# ans 先是“恒等置换”:谁都不动
ans = list(range(n))
base = P[:] # 当前底数
while k > 0:
if k & 1:
ans = compose(ans, base)
base = compose(base, base)
k >>= 1
return ans
得到的 ans[i] 表示:把“换座位”操作做 k 次之后,原来在座位 i 的人,最后会跑到座位 ans[i]。
根据置换结果还原每个座位是谁
最后一步其实就很机械了:
一开始:同学 i 在座位 i(0 索引就是 i)
final_pos = perm_power(P, k)之后:原本在座位 i 的人,最后去到了座位 final_pos[i]所以我们可以反过来建一个数组:
seat[final_pos[i]] = i
完整代码放一下:
defbuild_perm(n):
P = list(range(n))
for i in range(0, n, 2):
if i + 1 < n:
P[i], P[i+1] = i+1, i
else:
P[i] = i
return P
defcompose(A, B):
n = len(A)
C = [0] * n
for i in range(n):
C[i] = B[A[i]]
return C
defperm_power(P, k):
n = len(P)
ans = list(range(n)) # 恒等置换
base = P[:]
while k > 0:
if k & 1:
ans = compose(ans, base)
base = compose(base, base)
k >>= 1
return ans
defseat_after_k_rounds(n, k):
# 返回:每个座位上是哪位同学(1..n)
P = build_perm(n)
final_pos = perm_power(P, k)
seat = [0] * n
for person in range(n):
dst = final_pos[person]
seat[dst] = person + 1# 转回 1..n
return seat
if __name__ == "__main__":
n, k = 6, 3
print(seat_after_k_rounds(n, k))
# 示例输出:第三轮之后每个座位上的同学编号
时间复杂度分析一下:
构造一次 P是 (O(n))每次组合置换是 (O(n)),一共做 (O(\log k)) 次 总复杂度:(O(n \log k)),就算 k 非常大也能很快跑完
大概就是这样一个“从暴力模拟 → 抽象成置换 → 用快速幂优化”的思路,这种套路在很多“重复很多次某个操作”的题里都挺好用的,你以后再遇到那种“同样的打乱方式重复好多遍”的,都可以试着往“置换 + 快速幂”上想。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB