上午九点五十分,林晨坐在t厂大楼三十二层的一间小型会议室里。
会议室不大,约十平米,一张长方形会议桌,四把椅子,墙上挂着白板。窗户朝东,晨光透过百叶窗在地板上投下斑驳的光影。空气里有淡淡的消毒水味和咖啡香——典型的互联网公司气味。
林晨提前二十分钟到达。前台核对信息后,一位年轻的hR实习生将他领到这里,递上一瓶矿泉水,礼貌地说面试官马上就到。
他打开笔记本电脑,连接电源,再次检查网络。视频面试链接已经发到邮箱,但他被通知第一轮是现场面试。也好,面对面更能展现状态。
九点五十五分,门被推开。
进来的是个三十出头的男性,短发,戴黑框眼镜,穿着深灰色t恤,胸口印着t厂的logo。他手里拿着ipad和一支笔,步伐轻快。
“林晨是吧?我是王磊,AI平台部的算法工程师,今天由我来做技术一面”。他伸出手,笑容温和但透着专业感。
林晨起身握手:“王老师好,我是林晨”。
“坐坐,别客气”。王磊在对面坐下,打开ipad,“简历我看过了,十年跨境电商经验,最近半年自学AI,还做了个量化投资系统”?
“是的”。林晨点头,心里快速调整状态——苏婉说得对,不是接受考核,是展示价值。
“那我们直接开始吧”。王磊推了推眼镜,“先做两道算法题,可以吗”?
“没问题”。
王磊在ipad上操作几下,将屏幕转向林晨。是t厂内部的在线编程平台界面,题目已经加载。
第一题:寻找旋转排序数组中的最小值 II
题目描述:假设一个按升序排列的数组在某个未知点上进行了旋转(例如,数组 [0,1,2,4,5,6,7] 可能变成 [4,5,6,7,0,1,2])。数组中可能包含重复元素。请编写一个函数,找出其中最小的元素。
难度标签:hard。
林晨扫了一眼题目。这是二分查找的变种,经典难题。他曾在Leetcode上刷过类似的题,但包含重复元素会增加复杂度。
“可以用这个平台写,也可以在白板上写思路”。王磊说。
“我直接写代码吧”。林晨将笔记本电脑转向自己,打开编程界面。
手指落在键盘上时,他深吸一口气。这半年,他刷了超过三百道算法题,从Easy到hard,从数组到图论。失业的压力转化为刷题的动力,常常一天八小时坐在电脑前,直到眼睛发酸。
此刻,那些深夜的坚持开始兑现。
他先写下函数签名,然后快速分析:因为有重复元素,当 nums[mid] == nums[right] 时,无法判断最小值在左侧还是右侧,此时只能将右指针减一。时间复杂度最坏会退化到o(n),但平均仍是o(log n)。
指尖在键盘上飞舞:
def findmin(nums):
left, right = 0, len(nums) - 1
while left < right:
mid = left + (right - left) // 2
if nums[mid] > nums[right]:
left = mid + 1
elif nums[mid] < nums[right]:
right = mid
else: # nums[mid] == nums[right]
right -= 1
return nums[left]
写完,他检查了一遍边界条件,点击运行。
测试用例通过。
“Ac了”。王磊看着ipad上的反馈,点点头,“时间三分十二秒,很快。能解释一下为什么 nums[mid] == nums[right] 时要 right -= 1 吗”?
林晨转向白板,画了一个数组示意图:“比如 [3,3,1,3],初始时 left=0, right=3, mid=1。nums[1]=3, nums[3]=3,相等。此时我们无法判断最小值在左边还是右边,但可以确定的是,去掉最右边的元素不会丢掉最小值,因为 nums[right] 和 nums[mid] 相等,所以可以安全地将 right 减一”。
“正确”。王磊在ipad上做了记录,“下一题”。
第二题:接雨水 II
题目描述:给定一个 m x n 的矩阵,其中的值均为正整数,代表二维高度图。请计算这个形状可以接多少雨水。
难度标签:hard。
二维接雨水,比一维难得多。林晨记得这道题要用最小堆(优先队列)加bFS的思路。他从边界开始,将最低的柱子作为“水桶”的短板,向内推进。
思路清晰后,他再次开始编码。这次花了些时间,约八分钟。
import heapq
def trapRainwater(heightmap):
if not heightmap:
return 0
m, n = len(heightmap), len(heightmap[0])
if m < 3 or n < 3:
return 0
visited = [[False] * n for _ in range(m)]
heap = []
# 将边界加入堆
for i in range(m):
heapq.heappush(heap, (heightmap[i][0], i, 0))
heapq.heappush(heap, (heightmap[i][n-1], i, n-1))
visited[i][0] = visited[i][n-1] = true
for j in range(1, n-1):
heapq.heappush(heap, (heightmap[0][j], 0, j))
heapq.heappush(heap, (heightmap[m-1][j], m-1, j))
visited[0][j] = visited[m-1][j] = true
directions = [(0,1),(0,-1),(1,0),(-1,0)]
water = 0
while heap:
height, x, y = heapq.heappop(heap)
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < m and 0 <= ny < n and not visited[nx][ny]:
visited[nx][ny] = true
# 如果相邻格子比当前边界矮,可以接水
water += max(0, height - heightmap[nx][ny])
# 新边界的高度是 max(当前边界高度, 相邻格子高度)
heapq.heappush(heap, (max(height, heightmap[nx][ny]), nx, ny))
return water
运行,再次Ac。
王磊看着结果,沉默了几秒。然后他抬起头,笑容更明显了些:“两道hard题,全部一次Ac,而且时间控制得很好。林先生,你的算法功底比很多科班出身的候选人还要扎实”。
“谢谢”。林晨松了口气,但不敢放松——他知道,算法只是门槛,真正的重点在后面。
果然,王磊将ipad放到一边,身体前倾:“那我们聊聊项目吧。你简历上写的这个‘智能量化投资系统’,我挺感兴趣的。一个做了十年跨境电商的人,怎么想到做这个”?
林晨知道,展示价值的时刻到了。
他打开自己带来的笔记本电脑,调出系统的架构图:“其实逻辑是相通的。跨境电商要做库存预测、价格优化、用户行为分析,本质也是数据驱动决策。我失业后开始学AI,就想能不能把机器学习用在投资上”。
他切换到系统界面——这是他自己开发的web端,简洁的深色主题,左侧是菜单栏,中间是K线图和策略信号。
“系统分为几个模块”。林晨开始讲解,“数据层,我从多个数据源获取A股、基金、期货、外汇的实时和历史数据,用pySpark做清洗和预处理。特征工程部分,我提取了上百个技术指标,包括均线、mAcd、RSI、布林带,还有一些自己设计的因子”。
王磊认真听着,偶尔点头。
“模型层,我用了集成学习。基础模型包括LStm、GRU这些时序模型,还有xGboost和LightGbm。但不是简单堆叠,我设计了一个元学习器,动态评估每个子模型在不同市场状态下的表现,分配权重”。
“怎么评估市场状态”?王磊问。
“我用无监督学习做了聚类”。林晨切换到另一张图,“根据波动率、成交量、行业轮动速度等维度,把市场分为震荡市、单边上涨、单边下跌、高波动四种状态。每种状态下,不同策略的有效性差异很大”。
“有意思”。王磊眼睛亮了,“那风控呢”?
“三层风控”。林晨点开风控模块,“第一层,策略层面,每个策略都有最大回撤阈值,触发就暂停。第二层,账户层面,单日亏损超过2%或周亏损超过5%,全仓降杠杆。第三层,人工干预接口,我可以随时手动平仓”。
他顿了顿,补充道:“系统运行半年,累计收益率86%,最大回撤7.2%”。
王磊挑了挑眉:“实盘”?
“实盘”。林晨点头,“我用的是自己的钱。代码可以骗人,但账户净值不会”。
这句话让王磊笑了:“说得对。那你在开发过程中遇到的最大技术挑战是什么”?
林晨想了想:“数据一致性。金融市场数据来源多,格式不一,还有停牌、除权除息、合约换月这些特殊情况。我花了大量时间构建数据管道,确保输入模型的数据是干净、连续、可复现的。另外就是实盘交易时的延迟和滑点问题,我接入了券商ApI,但中小券商的接口稳定性一般,需要做重试和容错”。
两人就这样聊了四十多分钟。从模型细节到工程实现,从投资理念到技术选型。王磊问得很细,有时甚至会追问某个参数为什么设成0.3而不是0.25。林晨一一作答,有些地方他承认还在优化,但思路是清晰的。
十点五十分,王磊看了眼时间:“差不多了。我这边技术评估没问题,算法和项目都超出预期。接下来会有二面,应该是我们总监陈博士亲自面,他可能会问得更深,尤其是AI底层的一些东西”。
“谢谢王老师”。林晨起身。
“别客气”。王磊也站起来,再次握手,“说实话,你这个转型案例挺励志的。期待和你做同事”。
走出会议室时,林晨感觉脚步轻快了许多。
走廊里人来人往,年轻的程序员们端着咖啡匆匆走过,讨论着模型训练和线上问题。阳光透过落地窗洒进来,整个楼层明亮而充满活力。
他走到窗边,望向楼下。深南大道车流如织,远处腾讯大厦的玻璃幕墙反射着耀眼光芒。这座城市的脉搏,他曾经以为自己已经脱节,但现在,他又一次站在了核心地带的建筑里,用代码证明了自己的价值。
手机震动,是苏婉发来的微信:“面试怎么样”?
林晨打字回复:“一面过了,聊得很好。面试官说期待做同事”。
几乎秒回:“太好了!我就知道你可以!乐乐说晚上要吃大餐庆祝”!
看着屏幕上的字,林晨嘴角扬起。他想起昨晚苏婉说的话——你不是在求职,是在寻找能发挥价值的平台。
也许,t厂就是这个平台。
但下一关,是陈博士。
林晨听说过这位技术总监的名声:严谨、深刻、喜欢追问本质。他会问什么?会如何看待一个半路出家的AI工程师?
电梯下行时,林晨看着楼层数字跳动,心里那个问题逐渐清晰:
当一位追求算法极限的科学家,遇见一个用代码解决实际问题的工程师,会碰撞出怎样的火花?
他不知道答案。
但他已经准备好了。