Azure Infrastructure · VO Coding / Data Processing

Microsoft Azure 大文件地址校验:最少调用昂贵地理编码 API

流式读取商铺地址与经纬度,按规范化地址去重调用 API,再逐行比较坐标;分析缓存规模、歧义地址与错误重试。

题目重点:流式读取商铺地址与经纬度,按规范化地址去重调用 API,再逐行比较坐标;分析缓存规模、歧义地址与错误重试。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练版 API 对规范化的 (address,city,state,zip) 返回唯一可信 (lat,lon);同一规范化键可跨记录复用一次结果。
  • 比较采用球面距离≤0.1km 的训练阈值;题目未明确精度标准,应在真实面试先问。
  • 商铺名称不影响地理编码地址键;但地址规范化只做空白折叠和大小写归一,不擅自把 Ave/St 等别名视为同址。
  • 训练代码流式输出 (businessName,isValid);API 失败如何标记、重试与断点续跑作为工程追问。

解题路线

  1. 把地址四字段做保守规范化形成 cache key;每条记录先查结果缓存,只有新键才调用昂贵 API。相同地址下多个商铺或重复行只支付一次调用成本。
  2. 将 API 坐标与当前行经纬度用 Haversine 距离比较,避免直接比较浮点值或把经纬度度数差当公里。
  3. 大文件不能假设不同地址结果全在内存:若 distinct 地址缓存超预算,可先做外部分区/排序,相同 key 聚到一起,一次查 API 后验证该组全部记录,再合并输出原序。
  4. 若 API 速率受限,按 key 去重后用受控并发与重试;失败不应当‘坐标正确’,需要 unknown 状态和重跑队列。
  5. 在没有第三方权威缓存、也不能从一个地址推导另一地址的前提下,每个不同规范化地址至少需要一次调用,故此去重达到该模型下的最少调用数。

手动走一遍

两行商铺 A/B 地址分别写 '1 Main St' 与 ' 1 MAIN ST ',其余城市/州/邮编一致。规范化后同键,仅一次 lookup;若 A 坐标贴近返回值而 B 偏离 5km,逐行判断仍分别给 True/False。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

from collections.abc import Callable, Iterable, Iterator
from math import asin, cos, radians, sin, sqrt


def _normalize(*parts: str) -> tuple[str, ...]:
    return tuple(" ".join(part.split()).casefold() for part in parts)


def _distance_km(a: tuple[float, float], b: tuple[float, float]) -> float:
    lat1, lon1 = map(radians, a)
    lat2, lon2 = map(radians, b)
    x = sin((lat2 - lat1) / 2) ** 2 + cos(lat1) * cos(lat2) * sin((lon2 - lon1) / 2) ** 2
    return 6371.0 * 2 * asin(min(1.0, sqrt(x)))


def validate_coordinates(
    rows: Iterable[tuple[str, str, str, str, str, float, float]],
    lookup: Callable[[str, str, str, str], tuple[float, float]],
    tolerance_km: float = 0.1,
) -> Iterator[tuple[str, bool]]:
    """Yield (business_name, within_tolerance); memoize canonical address lookups."""
    if tolerance_km < 0:
        raise ValueError("negative tolerance")
    cache: dict[tuple[str, ...], tuple[float, float]] = {}
    for name, address, city, state, postal, lat, lon in rows:
        key = _normalize(address, city, state, postal)
        if key not in cache:
            cache[key] = lookup(*key)
        yield name, _distance_km((lat, lon), cache[key]) <= tolerance_km


if __name__ == "__main__":
    records = [("A", "1 Main St", "X", "CA", "90000", 34.0, -118.0),
               ("B", " 1  MAIN ST ", "x", "ca", "90000", 34.0, -118.0)]
    assert list(validate_coordinates(records, lambda *key: (34.0, -118.0))) == [("A", True), ("B", True)]

复杂度

N 行、D 个规范化地址时内存哈希方案 O(N) 本地处理与恰好 D 次 API 调用,缓存 O(D);Haversine 每行 O(1)。外部排序/分区会增加磁盘 I/O,但把内存限制在单分区键量。API 费用主导时,应同时估计 D、重复率和失败重试率。

易错点与追问

容易失分的边界

  • 用整行或商铺名当缓存键,同一地址多次调用。
  • 把经纬度完全相等当正确性规则,忽略浮点误差与地理编码代表点差异。
  • 过度规范化把不同门牌、套间或邮编错误合并。
  • API 超时即判该行无效,混淆‘验证失败’与‘无法验证’。
  • 未给 N/D 就声称内存 cache 必定足够。

继续深挖

  • API 对同址返回多个候选时,如何定义验证通过?
  • 缓存需要多久失效,地址数据更新如何处理?
  • 外部排序后如何按原文件顺序输出结果?
  • 如果只能做抽样审计而非全量,最少调用目标怎么改?
✓已复制微信号!