简介:面向导弹制导、飞行器轨迹规划与军事仿真学习者的投放域计算示例包,聚焦“投放域求算”这一核心问题,帮助读者理解如何依据导弹射程、发射角、速度等参数,通过遍历发射条件来求取可覆盖目标的投放区域。资源共包含3个文件,均为Matlab脚本(.m),压缩包整体大小仅4KB,代码体量小巧、逻辑清晰,便于逐行解读和二次开发。已有153人浏览学习。三个脚本相辅相成,可覆盖投放域求算中常用的数值计算与结果可视化环节:从设定导弹性能参数与初始发射条件,到建立运动方程并开展轨迹解算,再到根据多组命中轨迹生成投放域边界,帮助读者将理论公式快速转化为可运行程序。对从事航空航天、武器系统分析及相关课题研究的学生与工程师而言,这是一份轻量实用的入门范例,既能用于验证自身算法,也可作为后续拓展投放域优化研究的起点。
1. 投放域求算是广告投放里的哪块硬骨头
投放域求算在广告后台里是被低估的一环,而 chengxu.zip 这类标识符背后挂的往往就是这摊事:广告主说全国可投,素材合规却拿下三个省,预算又只够覆盖华东和华南的头部城市,求算要做的就是把互相打架的条件压成一份带优先级的地域表。它不是在地市列表里挨个打勾,而是把可售范围、合规边界、ROI 预期和一个时间窗口对齐成可执行的数据契约。做程序化投放的系统工程师、接投放底层的地图数据开发,以及策略产品都需要把这条链路理清楚,否则规则一多,线上就会出“预算花在无效地域”或者“本来能投的地域被静默丢掉”这两类事故。下面直接按建模、实现、排错、验证的顺序展开,新手能照做,老手可以直接看参数和边界。
2. 投放域求算的建模:输入、数据依赖与两种求算模式
2.1 先对齐输入输出:投放域求算到底算出什么
很多系统把投放域直接落到一张城市表上,但真正做求算之前,首先要定的是数据契约。请求方至少需要提供四类输入:广告主声明的可售范围、合规审查给出的不可投地域、预算约束下的量级上限,以及业务侧附加的配送或门店覆盖条件。单纯把前三者求交集还不够,因为预算约束不是地域维度上的硬条件,它在逻辑上属于软约束,应该在排序之后才生效。
输出侧我建议按状态位来组织:可投地域(通过全部硬过滤)、拒绝地域(命中任意硬规则)、候选地域(通过硬过滤但需要参与后续打分排序)三张集合,外加每个地域的优先级权重值。这样设计有三个好处:第一,上游预算分配模块只消费候选集,不需要知道过滤逻辑;第二,排查线上问题时能直接对状态位反查规则;第三,后续加规则不需要动表结构,只在规则表里插入新行。
提示:不要把“拒绝原因”和“降权原因”混在一个字段里。前者要给审计看,后者要给算法看,语义不同,存一起后面要么误用要么要拆表。
2.2 求算依赖的三条数据链不能省
投放域求算看着是集合运算,但精度全靠底层数据撑。第一条是行政编码体系,国内一般用 GB/T 2260 的省市区三级码,但广告场景经常出现直辖市没有市级节点、省直辖县这类结构,跨源 join 时最容易断在这里。我一般的做法是先建一张地域映射表,把国标码、平台内部码、显示名称、父子关系和生效时间统一存进去,后面所有原始数据进来第一步先归一化到这张表上。
第二条是网格/商圈映射。很多投放域不按行政区划给,而是按地理网格给,比如某个线下门店覆盖周边 3 公里。此时求算必须支持行政码和网格码的双向映射:输入给的是省份城市,输出要能落到网格;输入给的是网格 ID,输出要能聚合回行政码。这个映射表的粒度决定了投放域的精度上限,不能偷懒只做市级。
第三条是 ROI 样本分布。地域 ROI 预估依赖历史转化数据,但不一定每个地域都有充足样本。常见的处理是对城市做特征聚类,用 GDP、人口、客单价将样本稀疏的地域映射到相似城市上,借用它的 ROI 做初始值。这条链不参与硬过滤,只影响排序,但它直接决定了预算花出去是否有效。
2.3 硬过滤和软排序是两种求算模式,别混在一个循环里
两段式处理是我始终推荐的结构。第一段做硬过滤,把不可售、黑名单、素材限制直接打上 reject 标记,完全不与预算逻辑混合;第二段做软排序,只对候选地域计算得分,主要公式是预测转化率乘客单价再除以 CPM,然后按得分从高到低累加进预算桶。把这两段拆开的根本原因是它们的变更多节奏不同:硬规则变动需要审计和审批,软规则可以随时调参,混在一起会导致一次规则调整触发了全量重算。
常见误区长这样:第一版把过滤规则直接写成 SQL 里的 WHERE 条件,比如“禁止省份 NOT IN (...)”。规则少的时候很直观,但一旦一个地域在不同计划下有不同的状态,SQL 就表达不出来了。规则变更要动查询语句、动索引、动缓存,而且没有任何审计记录,出一次 0 预算地域被查到投放的事故,排查成本极高。我倾向于把规则写入配置表每行一条规则,包含规则类型、命中动作和生效时间窗口。
3. 用最小代码和参数表落地投放域求算
3.1 用 pandas 跑通投放域求算主链路的最小代码
假设手上有三份数据:广告主可售地域表、合规黑名单表、地域 ROI 表,用 Python 加 pandas 就能把求算主链路在本地完整跑通。
import pandas as pd # 三份核心输入 adv = pd.read_csv("advertiser_regions.csv") # region_code, region_name, supply_type blk = pd.read_csv("compliance_blacklist.csv") # region_code, reason, level roi = pd.read_csv("region_roi.csv") # region_code, predict_cvr, price, roi_score # 第一段:硬过滤,可售域内剔除合规黑名单 df = adv.merge(blk, on="region_code", how="left", indicator=True) df = df[df["_merge"] == "left_only"].drop(columns=["_merge"]) # 第二段:关联 ROI 做软打分 scored = df.merge(roi, on="region_code", how="left") # 无样本地域回退到 0,拉到所有有样本地域之后 scored["roi_score"] = scored["roi_score"].fillna(0.0) # 输出带优先级的候选地域集 scored = scored.sort_values("roi_score", ascending=False) scored.to_csv("candidate_region.csv", index=False)这段代码里最关键的决策在 merge 的方向上。可售表和黑名单表用how="left"而不是how="inner",目的是保留广告主可选且不在黑名单里的全部地域,黑名单表只用来淘汰;如果用了 inner join,被拒地域会全部消失,下游就永远看不到它们,排查问题时无从下手。ROI 关联同样用 left join,缺失地域填 0.0 而不是丢行,这样它仍作为低优先级候选保留,由预算模块决定是否分配量。把硬过滤和软打分分两段执行后,后续调整预算策略只需重读候选集,不需要回到原始表重跑过滤逻辑。
3.2 把求算升级成带缓存的可配置服务
单机脚本验证完逻辑后,生产上我一般封装成无状态服务。广告主 ID 和时间版本作为入参,规则表存放在 MySQL 里,求算结果缓存到 Redis 供上游预算模块高速读取。
def compute_delivery_regions(advertiser_id: int, query_time: str) -> dict: # 参数:advertiser_id 决定可售范围;query_time 决定走哪一版规则 cache_key = f"delivery_region:{advertiser_id}:{query_time[:10]}" # 命中同日缓存则直接返回,避免重复计算 cached = redis.get(cache_key) if cached: return json.loads(cached) # 从规则表读取当日生效的所有规则,按生效时间戳过滤 rules = load_effective_rules(query_time) # 执行硬过滤与软排序,得到候选地域集 result = run_pipeline(advertiser_id, rules) # 缓存 10 分钟,规则变更后最多延迟 10 分钟生效 redis.setex(cache_key, 600, json.dumps(result, ensure_ascii=False)) return result这里有个容易踩的坑:缓存键里带上query_time[:10]只是天级粒度,如果同一天内规则发生过变更,比如临时把某个城市拉黑,那么后进来的请求仍然命中旧缓存。解决方法是规则表里维护一个rule_version字段,每次规则变更递增版本号并写进缓存键,保证同一广告主同一天但不同规则版本的数据不会互相污染。缓存存活时间也不需要太长,10 分钟足够应对常规的修改频率。
3.3 投放域求算中必调的 4 个参数
从运营手中接过来时,参数往往需要分成两类。第一类是确定性的硬参数,第二类是影响排序灵敏度的软参数。下面这张表是我常用的初始配置,直接可用:
| 参数名 | 类型 | 建议初始值 | 参数作用与调整方向 |
|---|---|---|---|
| hard_filter_mode | 字符串 | reject | 硬规则命中后的动作,只有 reject 和 drop 两种。drop 意味着不输出,reject 会保留状态方便审计 |
| soft_score_weight | 浮点数 | 1.0 | ROI 得分在最终排序中的权重,调高会让地域集中度上升,量更保守 |
| empty_sample_fallback | 浮点数 | 0.0 | 无样本地域的默认得分,设为 0 排最后;设为均值则会给新地域冷启动机会 |
| rule_version | 字符串 | 日期字符串 | 规则版本号,必须进入缓存键,否则灰度期内新旧逻辑互相污染 |
参数里最具争议的是empty_sample_fallback。如果直接填 0.0,新开的城市因为没样本永远拿不到量,广告主的扩城诉求就落空;如果填全样本均值,又可能让数据稀疏的低质量地域消耗预算。我自己的处理方式是把默认值改成同省份的均值而不是全体均值,这样新城市能沿用到省内成熟地域的 ROI 水平,既不冒进也不至于永远没量。另外建议所有参数都走配置中心下发而不是改代码发版,投放侧的规则变更频率远超普通业务,每次发版带来的回归成本太不划算。
4. 投放域求算的精度陷阱与排错路径
4.1 行政区划粒度错配导致的可投域丢失
投放域求算最经典的线上事故是粒度错配。广告主提交的可售范围是省级编码,合规黑名单却是市级编码,求算时如果用省份编码直接 join 城市编码,会全部匹配不上,硬过滤链路就会把整个省都当成可投域或者直接丢空。必须先把省级可售域向下展开成市级列表,再做黑名单过滤,输出再按请求方的粒度聚合回去。展开和聚合逻辑要写在映射表里,不能写在业务代码中,因为国标码每年都会调整,映射表作为独立数据可以单独更新。
另一个隐蔽的粒度问题是省直辖县。在 GB/T 2260 里,湖北的仙桃、潜江这类城市没有市级节点,直接挂到省下面。如果用省市的父子链做展开,这些地域会被漏掉。处理办法是在映射表里显式维护一个level字段,直辖县按市级对待,保证展开后每个地域都有唯一的投放主体。
4.2 时区不是日期问题,是投放域的边界问题
投放域一旦按天做预算约束,时区处理不当就会在凌晨出现超支。如果用 UTC 日期切分,东八区凌晨 2 点发起的流量会被记到“下一天”,当天的预算池还没重置,继续按上一天的候选地域放量,就会出现华东地区凌晨投放量异常升高的情况。落地标准是:所有投放域判断都基于广告发生地的本地时间,代码里统一把时间戳转到Asia/Shanghai后再取日期,绝不能直接沿用入口服务的 UTC 时间。
4.3 通过反查数据定位求算偏差,比看日志更快
排错时最有效的手段不是看日志,而是做反查。把某天求算结果的候选地域表 dump 出来,对每个 region_code 反查规则表,确认它通过了哪些规则、被哪些规则影响。我通常写一个 diagnose 函数把每个地域的命中规则拼成字符串输出:
def diagnose(region_code: str, rules: list, result_status: str) -> str: # 结果状态:approved 表示可投,rejected 表示被拒,candidate 表示参与打分 hit_rule_ids = [r.rule_id for r in rules if r.matches(region_code)] # 把状态、命中规则、规则动作拼到一行,方便 grep return f"{region_code}, {result_status}, rules={hit_rule_ids or 'NONE'}"反查调试时最重要的验证点是状态是否与规则动作一致。rejected地域必须命中至少一条动作是 reject 的规则;approved地域不允许命中任何硬规则;candidate地域允许命中软规则但不允许命中硬规则。任何一条不一致都说明过滤逻辑里有 bug,优先检查 merge 方向是否用错或者规则生效时间窗口是否覆盖了当前时间。
注意:规则表里的生效时间窗口分为 start_time 和 end_time,end_time 为空表示长期生效。反查前先确认规则数据本身没有脏数据,比如有 end_time 早于 start_time 的配置,这种问题用规则表一查就能发现,别在代码里反复找。
5. 用历史投放数据验证投放域求算结果的三个技巧
5.1 用上个月的数据做离线回测,而不是等线上报错
投放域求算换一版逻辑之后,最直接的验证方式是回放历史请求日志。选取上一个完整月的广告请求数据,每条请求带上当时的可售、黑名单和预算上下文,用新逻辑重新计算候选地域,然后和实际投放结果对比。要看的核心指标是候选地域与实际产生消耗的地域之间的覆盖率,公式是实际消耗地域中存在于新候选集的比例。如果覆盖率低于 95%,说明新逻辑把本来能投的地域误杀了,需要检查过滤条件是否过严。
5.2 对昨日与今日候选集做 diff,分三类确认变化
上线初期每天对候选地域集做 diff,把变化拆成三类单独看:新增地域、消失地域、排序变化超过 20 位的地域。新增地域要确认它是有意放开的还是行政码变动导致的误放;消失地域要对照规则表确认是哪条规则命中;排序大幅波动的地域要检查 ROI 样本是否出现异常波动。把这三类变化分别整理成清单,由运营和算法各确认一次,比在线上看整体指标灵敏得多。
5.3 灰度期对比超预算率和冷启动地域的消耗占比
投放域求算不能只看地域覆盖率,还要和预算模块配合看投放的效果。灰度方案建议按广告主维度切 10% 流量,对比新旧逻辑的超预算率(实际消耗超出预算分配的请求占比)和冷启动地域消耗占比。超预算率升高说明新求算结果里高优先级地域过于集中,需要调低soft_score_weight;冷启动地域消耗占比过低说明empty_sample_fallback设得太保守,没有给新地域留出探索量。把这两个指标的阈值写进监控告警,就能在新逻辑出问题时自动触发回滚。
本文还有配套的精品资源,点击获取