主题切换
量化面试中脑筋急转弯(Brain Teasers)和行为面试(Behavioral Interview)是两个截然不同但都至关重要的环节。脑筋急转弯考察逻辑推理和量化直觉;行为面试则评估沟通能力、团队协作和职业动机。两者都需要针对性的准备策略。
问题:12球版本
有 12 个外观相同的球,其中 1 个是次品(重量与其他球不同,但不知轻重)。使用天平称三次,找出次品并确定它是重了还是轻了。
解题策略:信息论视角——每次称量有 3 种可能结果(左重、右重、平衡),3 次称量提供 33=27 种信息。问题需要区分 24 种状态(12 个球 x 2 种可能),所以理论上是可能的(27 > 24)。
第一次称量(1-4 vs 5-8):
情况1:平衡 → 次品在 9-12 中(已知标准球,简单) 情况2:左重/右重 → 次品在 1-8 中,且获得了方向信息
称球问题的通用公式:k 次称量最多能在 Nmax=3k−32 个球中找出轻重未知的次品。对于 3 次称量:33−32=12。
from itertools import combinations def generate_weighing_plan(n_balls: int, n_weighings: int) -> list: """ 生成n次天平称量的信息最大化的称量方案 思路:每次称量将剩余候选尽可能三等分 """ # 状态空间:每个球可以是正常(0)、偏重(+1)、偏轻(-1) # 这是一个简化框架,完整的实现涉及搜索和剪枝 plan = [] remaining = list(range(n_balls)) for round_idx in range(n_weighings): n = len(remaining) # 尽量三等分 left_size = n // 3 right_size = n // 3 left = remaining[:left_size] right = remaining[left_size:left_size + right_size] unweighed = remaining[left_size + right_size:] plan.append({ 'round': round_idx + 1, 'left': left, 'right': right, 'unweighed': unweighed }) return plan
问题:经典四人过桥
四个人在夜里过一座桥。他们只有一个手电筒,过桥必须有手电筒。四人过桥分别需要 1 分钟、2 分钟、5 分钟和 10 分钟。每次最多两人一起过桥,速度以慢者为准。最短需要多少分钟?
最优策略:
核心思路:让最快的两个人充当"摆渡人",每次把最慢的两人送过桥后,由最快的一个人或两人回来。
def bridge_crossing_optimizer(times: list) -> tuple: """ 四人过桥问题的最优方案 Parameters ---------- times : list 四个人过桥所需时间,已经排序 """ times = sorted(times) a, b, c, d = times # a最快, d最慢 # 策略1:最快的两人轮流做摆渡人 # a+b过, a回, c+d过, b回, a+b过 strategy1 = b + a + d + b + b # 策略2:最快的一个人往返 # a+d过, a回, a+c过, a回, a+b过 strategy2 = d + a + c + a + b # 策略3:分两组(b+c过河也是一种方案) strategy3 = b + a + c + a + d best = min(strategy1, strategy2, strategy3) return best, { 'strategy1 (最快两人摆渡)': strategy1, 'strategy2 (最快者往返)': strategy2, 'strategy3 (混合)': strategy3, } for group in ([1, 2, 5, 10], [1, 2, 5, 8], [1, 3, 4, 5], [2, 4, 6, 8]): best, detail = bridge_crossing_optimizer(group) detail_str = ' '.join(f"{k}={v}" for k, v in detail.items()) print(f"{str(group):<16} 最优 {best:>3} 分钟 {detail_str}") print("\n规律:a+b 摆渡(策略1)在最慢两人差距大时更优(如 1,2,5,10 → 17 < 19);") print(" 最快者往返(策略2)在时间接近时更优(如 1,3,4,5 → 13 < 14)。面试要说清切换条件:") print(" 比较 b+b 与 a+c —— 前者小则用摆渡法,后者小则用最快者往返。")
[1, 2, 5, 10] 最优 17 分钟 strategy1 (最快两人摆渡)=17 strategy2 (最快者往返)=19 strategy3 (混合)=19 [1, 2, 5, 8] 最优 15 分钟 strategy1 (最快两人摆渡)=15 strategy2 (最快者往返)=17 strategy3 (混合)=17 [1, 3, 4, 5] 最优 14 分钟 strategy1 (最快两人摆渡)=15 strategy2 (最快者往返)=14 strategy3 (混合)=14 [2, 4, 6, 8] 最优 22 分钟 strategy1 (最快两人摆渡)=22 strategy2 (最快者往返)=22 strategy3 (混合)=22 规律:a+b 摆渡(策略1)在最慢两人差距大时更优(如 1,2,5,10 → 17 < 19); 最快者往返(策略2)在时间接近时更优(如 1,3,4,5 → 13 < 14)。面试要说清切换条件: 比较 b+b 与 a+c —— 前者小则用摆渡法,后者小则用最快者往返。
问题:你怎么用一枚不公平的硬币(正反概率未知)生成一个公平的 50/50 随机结果?
答案(Von Neumann 算法):
证明:设硬币正面概率为 p,则 正反反正P(正, 反)=p(1−p)=P(反, 正)=(1−p)p,两者相等。
import numpy as np def fair_coin_from_biased(p: float = 0.7, n_samples: int = 10000) -> dict: """ Von Neumann 算法:用正面概率为 p 的偏心硬币产出公平的 0/1 Parameters ---------- p : float 不公平硬币的正面概率 n_samples : int 需要产出的公平样本数 Returns ------- dict: mean(公平样本均值)、tosses(总投掷次数)、 tosses_per_bit(每产出 1 bit 的平均投掷次数) """ fair_results = [] tosses = 0 while len(fair_results) < n_samples: toss1 = np.random.random() < p toss2 = np.random.random() < p tosses += 2 if toss1 and not toss2: # (正, 反) → 输出 1 fair_results.append(1) elif not toss1 and toss2: # (反, 正) → 输出 0 fair_results.append(0) # (正,正) / (反,反) → 丢弃重掷 return { 'mean': float(np.mean(fair_results)), 'tosses': tosses, 'tosses_per_bit': tosses / n_samples, } np.random.seed(42) # 1) 重复 1000 轮,检验均值是否无偏 means = [fair_coin_from_biased(p=0.7, n_samples=500)['mean'] for _ in range(1000)] print(f"p=0.70,每轮 500 个公平样本,重复 1000 轮:") print(f" 均值的均值: {np.mean(means):.4f} (理论 0.5)") print(f" 均值的标准差: {np.std(means):.4f} (理论 0.5/√500 = {0.5 / np.sqrt(500):.4f})") # 2) 效率随偏心程度的变化:接受概率 2p(1-p),每 bit 期望投掷 1/(p(1-p)) print(f"\n{'p':>6}{'实测均值':>12}{'实测投掷/bit':>16}{'理论 1/(p(1-p))':>18}") print("-" * 54) for p in [0.5, 0.6, 0.7, 0.9, 0.99]: r = fair_coin_from_biased(p=p, n_samples=4000) print(f"{p:>6.2f}{r['mean']:>12.4f}{r['tosses_per_bit']:>16.2f}" f"{1 / (p * (1 - p)):>18.2f}") print("\n结论:无论 p 多偏,输出都严格是 50/50(无偏性与 p 无关);") print(" 但代价是效率——p 越极端,期望投掷次数 1/(p(1-p)) 越高,p=0.99 时约需 101 次/bit。")
p=0.70,每轮 500 个公平样本,重复 1000 轮: 均值的均值: 0.5001 (理论 0.5) 均值的标准差: 0.0223 (理论 0.5/√500 = 0.0224) p 实测均值 实测投掷/bit 理论 1/(p(1-p)) ------------------------------------------------------ 0.50 0.5102 4.01 4.00 0.60 0.4988 4.23 4.17 0.70 0.5048 4.81 4.76 0.90 0.5070 11.07 11.11 0.99 0.5032 103.68 101.01 结论:无论 p 多偏,输出都严格是 50/50(无偏性与 p 无关); 但代价是效率——p 越极端,期望投掷次数 1/(p(1-p)) 越高,p=0.99 时约需 101 次/bit。
费米估算是量化面试中考察"数量级直觉"的经典题型。
分解法(Decomposition):将复杂问题分解为可估算的子问题。
常见题目与估算框架:
纽约有多少个加油站?
1. 纽约人口 ≈ 800万 2. 平均每户 2.5人 → 约 320万户 3. 假设每户有 1.2辆车 → 约 384万辆车 4. 一辆车每周加油 1次 → 每天约 55万次加油 5. 每个加油站平均 8个加油位,每辆车加油 5分钟 → 每加油位每小时服务 12辆车 → 每天约 288辆车/加油位 → 每个加油站每天服务 约 2300辆车 6. 加油站数量 ≈ 55万 / 2300 ≈ 239个 (还需考虑出租车、卡车等商业车辆,调整为约 500-800个)
A股市场一天的交易量有多少股?
1. A股总市值 ≈ 80万亿元 2. 日均换手率 ≈ 1.5%(近期水平) 3. 自由流通市值 ≈ 总市值 × 30% ≈ 24万亿元 4. 日均交易量 ≈ 24万亿 × 1.5% ≈ 3600亿元 5. 平均股价 ≈ 15元 6. 日均交易股数 ≈ 3600亿 / 15 ≈ 240亿股
行为面试在量化岗位中的权重在不断提升——技术能力出色的候选人很多,但能够有效沟通、融入团队、推动项目落地的候选人稀缺。
STAR 是行为面试回答的标准框架:
S (Situation) - 情境:背景是什么?当时面临什么问题? T (Task) - 任务:你的职责和目标是什么? A (Action) - 行动:你具体做了什么?为什么这么做? R (Result) - 结果:取得了什么成效?量化结果!
示例回答框架:
问:"描述一个你发现回测结果不可靠的经历,你是如何处理的?"S(情境):在开发一个日频量化策略时,回测显示年化收益 35%、夏普比率 2.5,这看起来好得不真实。T(任务):我需要识别回测中可能导致结果虚高的因素,确保策略的样本外表现可靠。A(行动):我系统性地做了三件事:逐日审计信号生成逻辑,发现信号使用了 t+1 日的收盘价数据——一个典型的前视偏差。重构了回测系统的事件驱动框架,在"事件发生时刻"获取数据而非在"数据发布时刻"回溯。引入了一个自动化的偏差检测模块,对每个新策略进行 10 项标准偏差检查。R(结果):修正后年化收益降至 18%(下降了 49%),但这是一个真实的、可投资的策略。更重要的是,偏差检测模块在后续半年中帮助团队捕获了另外 3 个类似的错误。
问:"描述一个你发现回测结果不可靠的经历,你是如何处理的?"
S(情境):在开发一个日频量化策略时,回测显示年化收益 35%、夏普比率 2.5,这看起来好得不真实。
T(任务):我需要识别回测中可能导致结果虚高的因素,确保策略的样本外表现可靠。
A(行动):我系统性地做了三件事:
R(结果):修正后年化收益降至 18%(下降了 49%),但这是一个真实的、可投资的策略。更重要的是,偏差检测模块在后续半年中帮助团队捕获了另外 3 个类似的错误。
动机与自我认知
团队协作与沟通
抗压与失败应对
创新与主动性
量化面试不是一场考试,而是一场双向选择。准备最充分的人不一定是数学最好的,但一定是最了解自己为什么适合这个行业、这个岗位的人。诚实面对自己的优势和局限,展示真实的好奇心和学习能力,比任何精心包装的答案都更有力量。