CS106B的教授办公室门开着,沈清辞站在门口,犹豫着要不要敲门。德雷克教授正在电脑前打字,眼镜滑到了鼻尖,花白的头发像一团被风吹乱的云朵。
“教授?”
德雷克抬起头,透过眼镜上方看他:“沈。进来吧。正好要找你。”
沈清辞走进办公室。空间不大,堆满了书和论文,墙上的白板写满了算法推导。空气里有咖啡和旧书的混合气味——典型教授办公室的味道。
“你上周交的作业,”德雷克从一堆文件中抽出一份打印件,“关于图着色问题的算法优化。你的解法很有创意。特别是你用马尔可夫链蒙特卡洛方法近似求解的那部分——这不是课程要求的内容。”
沈清辞的心跳微微加快。这是批评还是赞扬?
“我自己看了一些额外资料,”他说,“觉得可能有用。”
“确实有用,”德雷克摘下眼镜,用衬衫下摆擦了擦,“你把时间复杂度从指数级降到了多项式级,虽然牺牲了精确解,但在实际应用中足够好。这个思路很好。”
“谢谢教授。”
德雷克重新戴上眼镜,仔细打量着沈清辞:“你是大一?”
“是的。”
“以前有编程经验?”
“在国内参加过信息学竞赛。”
“怪不得,”德雷克点头,“我认识一个创业公司的CTO,他们在做社交网络分析平台,需要处理大规模图数据。他们最近在找一个兼职的算法实习生,时薪不错,还能远程工作。你感兴趣吗?”
沈清辞几乎要脱口而出“我感兴趣”,但他忍住了,先问:“时薪多少?”
“25美元一小时,每周最多20小时。但考虑到你是大一,可能先定10小时比较合适。而且,”德雷克顿了顿,“这是正规公司,需要报税,你要申请CPT(课程实践培训)。”
25美元一小时。
沈清辞快速计算:25美元×10小时=250美元一周。一个月四周就是1000美元。这比他洗盘子(每月约700美元,如果每天工作)和图书馆工作(每月约400美元)加起来还多。而且——最重要的是——这是技术工作,对简历有帮助,还能学到东西。
“我感兴趣,”他说,努力保持声音平稳。
“好,”德雷克在便签纸上写下一个邮箱和名字,“这是CTO的联系方式。你发邮件给他,附上你的简历和这个作业的代码。我会提前跟他打个招呼。”
沈清辞接过便签纸。上面写着:“马克·罗森伯格,SociaLyze联合创始人兼CTO”。
“谢谢教授,”他说,这次声音里有了真实的感激。
“不客气。你有潜力,但需要机会。这就是一个机会。”
沈清辞离开办公室,走在走廊上时,感觉脚步都轻快了。25美元一小时。技术工作。创业公司。
这是他来美国后,第一次不是因为“需要钱”而找到工作,而是因为“有能力”而得到机会。
---
三天后,2014年3月13日,周四下午3点
SociaLyze的办公室在帕洛阿尔托市中心的一栋三层小楼里。外墙是红砖的,爬满了常春藤,看起来不像科技公司,更像是什么律师事务所或设计工作室。
沈清辞提前十五分钟到达。他穿着最好的衣服——一件在旧货店淘到的深蓝色衬衫(5美元,几乎全新),配上黑色裤子(10美元,裤脚有点长,他自己用针线改过)。他背着双肩包,里面装着笔记本电脑、简历打印件,还有一瓶水。
前台接待他的是一位年轻女生,金发,笑容灿烂:“你是沈清辞?马克在二楼等你。”
二楼是开放办公区,十几张办公桌,白板上写满了架构图和数学公式。空气里有咖啡和年轻科技公司特有的活力感。
马克·罗森伯格坐在角落的一张办公桌后,四十岁左右,光头,穿着灰色T恤和牛仔裤,正在和两个人讨论什么。看到沈清辞,他挥手示意:“稍等两分钟。”
沈清辞站在旁边,观察着这家公司。员工都很年轻,大多数在二三十岁。有人在写代码,有人在讨论产品设计,有人戴着耳机专注工作。墙上贴着公司的标语:“理解连接,预测行为”。
“沈?”马克结束了讨论,走过来,“我是马克。德雷克教授说你很厉害。”
“谢谢,”沈清辞和他握手。马克的手很有力。
“我们到会议室谈,”马克带他走进一间玻璃隔断的小会议室,“坐。要喝什么吗?水?咖啡?”
“水就好。”
马克从冰箱里拿了一瓶水给他,然后坐在对面:“德雷克教授把你的作业发给我看了。确实不错。特别是马尔可夫链蒙特卡洛的应用——很多大三学生都不懂这个。”
“我在国内学过一些概率论和随机过程。”
“很好。那么,我想先问你几个技术问题,看看你的实际能力。可以吗?”
“当然。”
接下来的半小时,马克问了沈清辞一系列问题:关于图算法、时间复杂度分析、分布式系统基础、Python和C++的区别。沈清辞大多能回答,有些问题他没学过,就诚实地说“这个我不了解,但我会去学”。
马克一边听一边在笔记本上记着什么。最后,他说:“OK,基本的技术能力没问题。现在,我想给你一个实际问题,看你如何解决。”
他从白板架上拿起一支马克笔:“假设我们有一个社交网络图,有1亿个节点,平均每个节点有150个连接。我们需要快速找出所有三角形关系——也就是A连接B,B连接C,C连接A的三元组。传统算法需要O(n^3),在这么大图上不现实。你有什么想法?”
沈清辞看着白板上的问题。大规模图,三角形计数。这是社交网络分析中的经典问题,确实计算复杂度很高。
他思考着。传统方法是三重循环,肯定不行。那有什么优化方法?
他想起了高中时,陆星衍教过他的一种数学思维:当问题太大无法直接解决时,尝试分解、近似或转换问题。
“也许我们可以用抽样方法,”沈清辞说,“不用计算所有三角形,而是随机抽样一部分节点,计算这些节点形成的三角形,然后根据抽样比例估算总数。”
“思路不错,”马克点头,“但抽样误差可能很大,尤其是当三角形分布不均匀时。”
“那……也许可以用邻接矩阵的谱方法?”沈清辞说,但这个想法一出口他就知道不对——1亿节点的邻接矩阵太大了,内存放不下。
马克微笑:“放轻松,这不是考试。你可以思考一下。”
沈清辞闭上眼睛。他需要转换思路。三角形计数……本质上是在图中找到闭合的三元环。这和图论中的什么概念相关?
突然,他想到了什么。
“我们可以用节点度数的信息来优化,”他说,“在社交网络中,度数高的节点(大V)参与的三角形更多。我们可以按度数排序节点,优先处理高度数节点。同时,我们可以把图分成多个社区,社区内的三角形密度更高,社区间的三角形很少。这样我们可以先识别社区,然后在每个社区内计算三角形,最后合并结果。”
马克的眼睛亮了一下:“继续说。”
沈清辞越来越有信心:“具体来说,我们可以用Louvain社区发现算法先对图进行社区划分,时间复杂度接近线性。然后在每个社区内,我们可以用更精细的算法,比如把邻接表转换成比特位图,用位运算加速三角形检测。因为社区规模远小于整个图,所以内存和计算都可行。”
“那社区间的三角形呢?”
“社交网络的社区结构通常很明显,社区间的连接很少。我们可以单独处理这些跨社区的边,数量不会太多。”
马克沉默了几秒,然后笑了:“很好。非常实际的思路。你提到的Louvain算法确实适合大规模图,位运算加速也是常用技巧。最重要的是,你考虑到了实际系统的限制——内存、计算时间、数据分布。”
沈清辞松了口气。
“但还有一个问题,”马克在白板上画了个示意图,“如果我们实时更新图——不断有新的连接产生,旧的连接消失——我们需要增量更新三角形计数。怎么办?”
这个问题更难。沈清辞皱眉思考。增量更新……意味着每次图的微小变化,都要快速更新三角形总数,而不是重新计算整个图。
他想起了分布式系统中的流处理概念。
“我们可以维护一个流处理系统,”他说,“当新的边(A,B)加入时,我们找出A和B的共同邻居集合C。对于每个C,就形成了一个新的三角形(A,B,C)。删除边时同理。这样每次更新的时间复杂度是O(d),其中d是平均度数。”
“实时性呢?”
“我们可以用内存数据库存储最近的图数据,比如过去24小时的。更久的数据可以归档到磁盘。三角形计数也分层:实时计数、近线计数、历史计数。”
马克放下马克笔,鼓掌:“非常好。沈,你被录用了。”
沈清辞愣住了:“真的?”
“真的。时薪25美元,每周10小时起,根据项目需要可以增加到20小时。工作内容主要是算法实现和优化,可以远程,但每周至少来办公室一次开会。你需要申请CPT,公司会提供文件。有问题吗?”
“没有,”沈清辞说,努力控制住笑容,“没有问题。谢谢。”
“不客气。你值得这个机会。”马克看了看表,“我还有会,你先跟HR签文件。下周一开始工作,可以吗?”
“可以。”
周一
沈清辞收到第一笔工资的银行转账通知:两周工作20小时,税前500美元。税后大约420美元。
他盯着手机屏幕上的数字,看了很久。
420美元。相当于他在“福满楼”洗52.5小时的盘子(按8美元时薪算)。相当于他在图书馆工作35小时(按12美元时薪算)。而现在,他只需要坐在电脑前写代码,做自己擅长且喜欢的事情。