「反応のよい施策に予算を寄せる」。販促会議では通りやすい話です。ただ、最初の1万円で8件集まっても、次の1万円で同じ8件が増えるとは限りません。僕なら、追加でいくら使うと何件増えるかを表にしてから配分を決めます。
今回は6万円を3施策に分ける架空の例です。Pythonで予算内の54通りを比べると、通常は36件の案が首位。不振時を優先すると、別の配分が残りました。生成AIと販促案を練るとき、この2案があるだけで依頼を具体的に書けます。
件数は検証用の仮定です。広告実績や生成AIの予測値ではありません。検証はローカルのPythonで完結します。
1万円追加するたびに、同じ件数が増える?
集めたいのは翌月の相談申込。広告、ウェビナー、紹介施策に、それぞれ最大3万円まで使える設定にします。制作費も含めた想定です。
| 投入額 | 広告 | ウェビナー | 紹介施策 |
|---|---|---|---|
| 0万円 | 0件 | 0件 | 0件 |
| 1万円 | 8件 | 4件 | 7件 |
| 2万円 | 13件 | 10件 | 11件 |
| 3万円 | 15件 | 16件 | 13件 |
各セルは、その額まで使ったときの合計件数です。広告なら1万円追加するごとの増分は8件、5件、2件。2万円の13件に、1万円の8件を足すと二重計上になります。
広告は追加分の反応が鈍る設定。ウェビナーは開催準備を済ませてから集客を増やせる想定で、増分を4件、6件、6件にしています。最初の8件だけを見ると、ウェビナーの後半を取り逃がします。
表の「件数」は先に揃えたいところです。広告だけクリック数で、紹介だけ相談申込数なら足せません。ウェビナーの参加人数も今回は使いません。会議資料にある「反応数」、あの列名は便利すぎます。
Pythonで配分候補を全部並べる
1万円単位なら、候補は各施策で0、1、2、3の4個。組み合わせは4×4×4で64通りです。合計が6を超える候補を外せば、予算内の54通りになります。
ウェビナーの相談申込が伸びなかった場合も置きます。ウェビナーの件数だけを0、3、6、9件に差し替え、他の施策は同じにしました。
同じ配分を、通常時とウェビナー不振時の両方で計算します。
次を allocation.py として保存します。追加ライブラリは不要です。手元ではPython 3.14.6で実行しました。
from itertools import product
# 位置は広告、ウェビナー、紹介施策。値は合計件数。
normal = ([0, 8, 13, 15], [0, 4, 10, 16], [0, 7, 11, 13])
low = (normal[0], [0, 3, 6, 9], normal[2])
def compare(budget, normal, low):
rows = []
for plan in product(*(range(len(v)) for v in normal)):
if sum(plan) > budget:
continue
base = sum(v[n] for v, n in zip(normal, plan))
weak = sum(v[n] for v, n in zip(low, plan))
rows.append((plan, base, weak))
# 同点なら支出が少ない案、その次に配分の辞書順。
usual = sorted(rows, key=lambda r: (-r[1], sum(r[0]), r[0]))
cautious = sorted(
rows, key=lambda r: (-min(r[1:]), sum(r[0]), r[0])
)
return usual, cautious
usual, cautious = compare(6, normal, low)
print(f"予算内: {len(usual)}通り")
print("配分は万円。順に広告、ウェビナー、紹介施策")
for plan, base, weak in usual[:3]:
print(f"通常上位: {plan} / 通常{base}件 / 不振{weak}件")
plan, base, weak = cautious[0]
print(f"下振れ優先: {plan} / 通常{base}件 / 不振{weak}件")
ターミナルで実行します。
python3 allocation.py
実行結果です。
予算内: 54通り
配分は万円。順に広告、ウェビナー、紹介施策
通常上位: (2, 3, 1) / 通常36件 / 不振29件
通常上位: (1, 3, 2) / 通常35件 / 不振28件
通常上位: (2, 2, 2) / 通常34件 / 不振30件
下振れ優先: (2, 2, 2) / 通常34件 / 不振30件
product は複数の候補から全組み合わせを作ります。検証環境に同梱された itertools.product の説明でも、入れ子のforループに相当すると確認できます。
min(r[1:]) は、その配分の通常時と不振時のうち、少ないほうの件数です。それを大きい順に並べています。不振になる確率は置いていません。2つの想定だけを比べる方法で、あらゆる不振に耐える保証もありません。
予算を使い切る制約は入れていません。合計が6以下なら候補に残り、同点なら支出の少ない案を先に出します。検証では予算0のときに配分が (0, 0, 0) だけ残ることと、全件数を0にすると支出0の案が先頭になることも確かめました。
36件の案を、そのまま採用する前に
出力では、通常1位と3位が入れ替わりました。
広告2万円・ウェビナー3万円・紹介1万円なら、通常は36件。不振時は29件です。ウェビナーから紹介へ1万円移すと、通常は34件へ減りますが、不振時は30件になります。
通常の2件を取るか、不振時の1件を取るか。
下振れ優先がいつも正解とは言いません。ウェビナーの見込みに十分な根拠があるなら、通常36件の案にも理由があります。
僕はまず、この2案を残します。ここ、地味に効きます。配分を丸ごと考え直す話から、「ウェビナーへの最後の1万円をどうするか」まで絞れるので。
ただし、計算には割り切りがあります。同じ人が広告とウェビナーの両方から申し込む重複や、広告を見て紹介につながる相乗効果は含めていません。足し算するには、施策別の件数を重複しない基準で数えるか、重複分を除いた仮定が必要です。粗い元データなら、1件差で決める精度はありません。
施策を10個、各11候補に増やすと11の10乗、259億3742万4601通り。この全探索をそのまま広げるのは厳しいです。まずは今回のように少数の施策と粗い金額刻みで使います。
生成AIには、比較する材料ごと渡す
次は会議用の説明を作る依頼文の記入例です。生成AIへの送信や、回答品質の検証はしていません。
販促会議で使う比較メモを作成してください。
以下は架空データによる試算です。実績として書かないでください。
予算6万円、各施策の上限3万円。金額は広告/ウェビナー/紹介施策の順。
A案: 2/3/1万円。通常36件、ウェビナー不振時29件。
B案: 2/2/2万円。通常34件、ウェビナー不振時30件。
件数は相談申込数。重複と相乗効果は計算に含めていません。
A案からB案への配分変更と件数差を説明し、各案が合う条件を書いてください。
不振の発生確率、売上、費用対効果は不明です。数字を補わないでください。
最後に、採用を決めるため担当者に確認したいことを2つ挙げてください。
配分の数字はコードの出力と照合できます。生成AIの説明を読んで「売上が増える」と書かれていたら、今回の表には売上の根拠がないと戻せます。
会議へ持っていくのは通常36件の案と、不振時30件を残す案です。配分差はウェビナーから紹介へ移す1万円。その1万円分の相談申込を見込める根拠を、担当者に確認します。次月に実績が出たら表の件数を更新して、同じコードでもう一度比べられます。
