補(bǔ)貨決策建模實(shí)戰(zhàn))
1. 從“拍腦袋”到“算數(shù)據(jù)”為什么蔬菜定價(jià)補(bǔ)貨值得建模每年國(guó)賽C題一出總能引起一陣熱議。2023年的這道“蔬菜類商品的自動(dòng)定價(jià)與補(bǔ)貨決策”乍一看似乎是個(gè)超市運(yùn)營(yíng)的日常問(wèn)題離我們熟悉的微分方程、物理仿真有點(diǎn)遠(yuǎn)。很多同學(xué)的第一反應(yīng)可能是“這不就是個(gè)簡(jiǎn)單的預(yù)測(cè)和優(yōu)化嗎用個(gè)時(shí)間序列預(yù)測(cè)銷量再套個(gè)庫(kù)存模型不就行了” 我當(dāng)年剛開(kāi)始接觸建模時(shí)也這么想過(guò)結(jié)果被現(xiàn)實(shí)狠狠教育了一番。這道題的魅力恰恰在于它用看似平凡的商業(yè)場(chǎng)景包裹了從數(shù)據(jù)處理、不確定性建模、多目標(biāo)決策到方案落地的完整鏈條非??简?yàn)綜合能力。超市里綠葉菜今天賣不完明天就可能蔫掉報(bào)廢土豆洋蔥這類耐儲(chǔ)的囤多了又占?jí)嘿Y金。價(jià)格定高了顧客不買賬定低了毛利覆蓋不了損耗。補(bǔ)貨更是門(mén)學(xué)問(wèn)補(bǔ)多了損耗和倉(cāng)儲(chǔ)成本飆升補(bǔ)少了貨架空空錯(cuò)過(guò)銷售機(jī)會(huì)顧客滿意度下降。傳統(tǒng)上這些決策嚴(yán)重依賴店長(zhǎng)的個(gè)人經(jīng)驗(yàn)也就是我們常說(shuō)的“拍腦袋”。今天下雨了感覺(jué)菜不好賣就少進(jìn)點(diǎn)明天有促銷就多備點(diǎn)貨。這種方式在小規(guī)模經(jīng)營(yíng)時(shí)或許有效但在連鎖化、數(shù)字化的當(dāng)下它的粗放和低效就成了致命傷。因此這道題的核心價(jià)值是讓我們用數(shù)學(xué)和算法的“望遠(yuǎn)鏡”與“顯微鏡”去透視并優(yōu)化這個(gè)充滿不確定性的日常商業(yè)過(guò)程。它要求我們不再是簡(jiǎn)單的“調(diào)包俠”把數(shù)據(jù)扔進(jìn)模型等結(jié)果而是要深入思考數(shù)據(jù)反映了什么規(guī)律模型背后的假設(shè)是否成立我們的決策建議在真實(shí)的、嘈雜的、充滿約束的商業(yè)環(huán)境中是否真的可行接下來(lái)我將結(jié)合常見(jiàn)的思路誤區(qū)拆解這道題的幾個(gè)核心關(guān)卡并分享一套經(jīng)過(guò)實(shí)戰(zhàn)檢驗(yàn)的、可擴(kuò)展的參考實(shí)現(xiàn)框架。2. 破題第一步別急著找算法先理解數(shù)據(jù)和問(wèn)題邊界看到題目很多人的習(xí)慣是立刻去想“我用什么模型”——LSTM預(yù)測(cè)ARIMA還是隨機(jī)森林這種思路往往會(huì)導(dǎo)致后期陷入僵局。正確的打開(kāi)方式是像偵探一樣審視題目給出的所有信息定義清楚我們要解決的到底是什么問(wèn)題。2.1 問(wèn)題本質(zhì)拆解這不是一個(gè)題而是三個(gè)題的組合仔細(xì)閱讀賽題描述這里基于常見(jiàn)賽題結(jié)構(gòu)進(jìn)行還原你會(huì)發(fā)現(xiàn)它通常包含幾個(gè)環(huán)環(huán)相扣的子問(wèn)題銷量與需求預(yù)測(cè)這是所有決策的基礎(chǔ)。給定歷史銷售數(shù)據(jù)、成本、定價(jià)等信息預(yù)測(cè)未來(lái)一段時(shí)間如每天、每周各種蔬菜的需求量。注意這里預(yù)測(cè)的是“需求”而不是“銷量”。銷量是需求在庫(kù)存約束下的實(shí)現(xiàn)值。如果貨備少了銷量就等于需求如果貨備多了沒(méi)賣完銷量就小于需求。預(yù)測(cè)模型必須能區(qū)分這個(gè)概念。定價(jià)決策在預(yù)測(cè)需求的基礎(chǔ)上考慮蔬菜的成本、新鮮度損耗率、市場(chǎng)競(jìng)爭(zhēng)、顧客價(jià)格敏感度等因素制定一個(gè)能最大化利潤(rùn)或兼顧其他目標(biāo)的價(jià)格。蔬菜定價(jià)的特殊性在于其“易腐性”今天的定價(jià)直接影響今天的銷量和明天的剩余庫(kù)存質(zhì)量進(jìn)而影響明天的定價(jià)決策這是一個(gè)動(dòng)態(tài)問(wèn)題。補(bǔ)貨決策在給定定價(jià)策略和需求預(yù)測(cè)下決定每天或每次的補(bǔ)貨量。這需要平衡多個(gè)矛盾的目標(biāo)滿足預(yù)測(cè)需求、控制庫(kù)存持有成本、最小化因腐爛造成的損耗、利用供應(yīng)商的批量折扣、遵守倉(cāng)庫(kù)容量和貨架空間限制等。這三個(gè)問(wèn)題不是獨(dú)立的而是強(qiáng)耦合的。定價(jià)影響需求需求預(yù)測(cè)指導(dǎo)補(bǔ)貨補(bǔ)貨量又反過(guò)來(lái)影響庫(kù)存成本和損耗進(jìn)而影響未來(lái)的定價(jià)空間。因此一個(gè)割裂的、先預(yù)測(cè)、再定價(jià)、最后補(bǔ)貨的流水線式解法往往得不到全局優(yōu)解。2.2 數(shù)據(jù)預(yù)處理清洗與特征工程是模型的“地基”題目通常會(huì)提供一段時(shí)間內(nèi)如過(guò)去一年的詳細(xì)數(shù)據(jù)可能包括銷售流水日期、商品編碼、銷售量、銷售單價(jià)、銷售金額。商品信息商品編碼、品類葉菜、根莖、茄果等、成本價(jià)、供應(yīng)商、保質(zhì)期/損耗特性。庫(kù)存記錄每日期初庫(kù)存、入庫(kù)量、損耗報(bào)損量。外部信息可能包含節(jié)假日、天氣溫度、降水量、促銷活動(dòng)標(biāo)記等。第一步數(shù)據(jù)清洗。檢查并處理缺失值、異常值如某天銷量為0但庫(kù)存充足可能是數(shù)據(jù)記錄缺失某天銷量奇高可能是促銷或數(shù)據(jù)錯(cuò)誤。對(duì)于異常值不能簡(jiǎn)單刪除要結(jié)合業(yè)務(wù)邏輯判斷。例如國(guó)慶節(jié)當(dāng)天銷量暴增是合理的應(yīng)予以保留并作為節(jié)假日特征。第二步構(gòu)建“真實(shí)需求”。這是最關(guān)鍵的一步。由于存在缺貨歷史銷量數(shù)據(jù)是“被截?cái)唷钡男枨蟆N覀兛梢杂靡恍┓椒▉?lái)估算真實(shí)需求簡(jiǎn)單方法對(duì)于缺貨日當(dāng)日銷量等于期初庫(kù)存可以將該日需求估算為“銷量 × 一個(gè)大于1的系數(shù)”這個(gè)系數(shù)可以根據(jù)不缺貨日期的平均銷量與庫(kù)存比例來(lái)估計(jì)。進(jìn)階方法使用生存分析Survival Analysis中的概念將缺貨視為“刪失”數(shù)據(jù)利用不缺貨日期的數(shù)據(jù)分布來(lái)估計(jì)整個(gè)需求分布。第三步特征工程。將原始數(shù)據(jù)轉(zhuǎn)化為模型能理解的特征。這比選擇模型更重要。對(duì)于時(shí)間序列預(yù)測(cè)有效的特征可能包括時(shí)間特征年、月、日、星期幾、是否為周末、是否為節(jié)假日及其前后幾天、季度。滯后特征過(guò)去1天、3天、7天、14天、30天的銷量、價(jià)格。滾動(dòng)統(tǒng)計(jì)特征過(guò)去7天的平均銷量、標(biāo)準(zhǔn)差、最大值、最小值。商品屬性特征品類獨(dú)熱編碼、成本價(jià)、是否易腐如葉菜為1土豆為0。交互特征價(jià)格與品類的交互項(xiàng)不同品類對(duì)價(jià)格敏感度不同、節(jié)假日與品類的交互項(xiàng)。外部特征天氣數(shù)據(jù)如最高溫、降水量、是否有促銷。注意特征不是越多越好。要防止過(guò)擬合可以使用特征重要性評(píng)估如基于樹(shù)模型或遞歸特征消除來(lái)選擇關(guān)鍵特征。3. 模型構(gòu)建預(yù)測(cè)、定價(jià)與補(bǔ)貨的“鐵三角”理解了問(wèn)題構(gòu)建了特征我們才進(jìn)入模型選擇階段。這里提供一個(gè)分層、可迭代的建模框架。3.1 需求預(yù)測(cè)模型融合時(shí)序與回歸的混合思路單純用時(shí)間序列模型如ARIMA、Prophet可能忽略了價(jià)格、促銷等外部因素。單純用回歸模型如線性回歸、XGBoost可能捕捉不到復(fù)雜的周期性和趨勢(shì)。因此混合模型是更穩(wěn)健的選擇。一種有效的架構(gòu)是“殘差學(xué)習(xí)”框架基準(zhǔn)時(shí)序預(yù)測(cè)首先使用一個(gè)穩(wěn)健的時(shí)序模型如季節(jié)性分解的STL或Prophet對(duì)每個(gè)單品的歷史銷量進(jìn)行擬合得到僅基于時(shí)間規(guī)律的預(yù)測(cè)值y_temporal。這個(gè)模型只使用時(shí)間特征。殘差建模計(jì)算歷史真實(shí)銷量或估算需求與y_temporal的殘差residual y_true - y_temporal。這個(gè)殘差包含了時(shí)間趨勢(shì)無(wú)法解釋的部分比如價(jià)格變動(dòng)、促銷、異常天氣的影響。機(jī)器學(xué)習(xí)模型擬合殘差使用XGBoost或LightGBM等模型以價(jià)格、促銷、天氣、品類等特征為輸入預(yù)測(cè)這個(gè)殘差值residual_pred。最終預(yù)測(cè)最終的預(yù)測(cè)需求y_pred y_temporal residual_pred。這種方法的優(yōu)點(diǎn)是時(shí)序模型抓住了主體規(guī)律機(jī)器學(xué)習(xí)模型負(fù)責(zé)精細(xì)化修正兩者互補(bǔ)且解釋性較強(qiáng)。在編程實(shí)現(xiàn)上我們可以用statsmodels或prophet做時(shí)序部分用scikit-learn或xgboost做殘差回歸部分。# 偽代碼示例混合預(yù)測(cè)模型框架 import pandas as pd from prophet import Prophet import xgboost as xgb from sklearn.model_selection import train_test_split # 假設(shè) df 是包含日期‘ds’、銷量‘y’、價(jià)格‘price’、是否促銷‘promo’等特征的DataFrame df pd.read_csv(sales_data.csv) # 步驟1使用Prophet進(jìn)行基準(zhǔn)時(shí)序預(yù)測(cè) model_prophet Prophet(yearly_seasonalityTrue, weekly_seasonalityTrue, daily_seasonalityFalse) model_prophet.fit(df[[ds, y]]) future model_prophet.make_future_dataframe(periods30) # 預(yù)測(cè)未來(lái)30天 forecast model_prophet.predict(future) df[y_temporal] forecast[yhat].values[:len(df)] # 將擬合值對(duì)齊到歷史數(shù)據(jù) # 步驟2計(jì)算殘差 df[residual] df[y] - df[y_temporal] # 步驟3準(zhǔn)備特征預(yù)測(cè)殘差 features [price, promo, temperature, is_holiday, day_of_week] # 示例特征 X df[features] y_residual df[residual] X_train, X_test, y_train, y_test train_test_split(X, y_residual, test_size0.2, shuffleFalse) # 注意時(shí)間序列避免隨機(jī)打亂 model_xgb xgb.XGBRegressor(objectivereg:squarederror, n_estimators100) model_xgb.fit(X_train, y_train) # 步驟4對(duì)于未來(lái)預(yù)測(cè)先有時(shí)序預(yù)測(cè)再用未來(lái)特征預(yù)測(cè)殘差 future_features ... # 需要構(gòu)建未來(lái)日期的特征如計(jì)劃價(jià)格、已知節(jié)假日 future_temporal forecast[yhat].values[-30:] # 未來(lái)30天的時(shí)序預(yù)測(cè) future_residual_pred model_xgb.predict(future_features) final_demand_pred future_temporal future_residual_pred3.2 定價(jià)模型在需求彈性與損耗間尋找平衡點(diǎn)定價(jià)不是孤立的它建立在需求預(yù)測(cè)的基礎(chǔ)上并且需求通常與價(jià)格負(fù)相關(guān)。我們需要刻畫(huà)這種關(guān)系即需求的價(jià)格彈性。需求函數(shù)建模對(duì)于每種蔬菜或同質(zhì)品類可以假設(shè)一個(gè)簡(jiǎn)化的需求函數(shù)。最常見(jiàn)的是線性需求函數(shù)和指數(shù)需求函數(shù)。線性需求D(p) a - b * p。其中D是預(yù)測(cè)需求量p是定價(jià)a是潛在市場(chǎng)規(guī)模b是價(jià)格敏感系數(shù)??梢酝ㄟ^(guò)歷史數(shù)據(jù)價(jià)格銷量進(jìn)行線性回歸估計(jì)a和b。指數(shù)需求常彈性D(p) A * p^(-e)。取對(duì)數(shù)后變?yōu)閘og(D) log(A) - e * log(p)這是一個(gè)線性關(guān)系e就是價(jià)格彈性系數(shù)通常為正數(shù)。這種形式更符合經(jīng)濟(jì)學(xué)常識(shí)彈性恒定。單日定價(jià)優(yōu)化模型在知道成本c、需求函數(shù)D(p)后單日利潤(rùn)為π(p) (p - c) * D(p)。通過(guò)求導(dǎo)令導(dǎo)數(shù)為零可以解出理論最優(yōu)價(jià)格p* (a b*c) / (2b)線性需求或p* (e/(e-1)) * c指數(shù)需求需滿足e1。但蔬菜定價(jià)的復(fù)雜性在于損耗蔬菜會(huì)腐爛剩余庫(kù)存會(huì)貶值。假設(shè)每日損耗率為θ如葉菜0.2根莖0.05那么今天的剩余庫(kù)存I_tomorrow (I_today Q_today - D_today) * (1-θ)。今天的定價(jià)p_today通過(guò)影響D_today直接影響I_tomorrow進(jìn)而影響明天的決策。這變成了一個(gè)多期動(dòng)態(tài)規(guī)劃問(wèn)題。庫(kù)存約束價(jià)格不能低到讓需求超過(guò)庫(kù)存否則會(huì)產(chǎn)生無(wú)法滿足的需求影響顧客滿意度。多目標(biāo)可能不僅要利潤(rùn)還要考慮銷量市場(chǎng)份額、損耗率、庫(kù)存周轉(zhuǎn)率等。因此更實(shí)際的定價(jià)模型是一個(gè)帶約束的動(dòng)態(tài)優(yōu)化問(wèn)題。我們可以將其離散化構(gòu)建一個(gè)有限期的決策模型。3.3 補(bǔ)貨模型庫(kù)存論與優(yōu)化算法的結(jié)合補(bǔ)貨決策的輸入是預(yù)測(cè)需求D和定價(jià)策略決定的預(yù)期銷量輸出是補(bǔ)貨量Q。目標(biāo)是平衡多個(gè)成本訂貨成本每次下單有固定成本如物流、管理。持有成本庫(kù)存占用資金和倉(cāng)庫(kù)空間的成本按天計(jì)。缺貨成本需求大于庫(kù)存導(dǎo)致的銷售損失和商譽(yù)損失。損耗成本因腐爛而丟棄的蔬菜成本。對(duì)于不易腐的標(biāo)準(zhǔn)品經(jīng)典的(s, S)或(Q, R)庫(kù)存策略很有效。但對(duì)于易腐品需要引入損耗率和保質(zhì)期。一種實(shí)用的建模方法是構(gòu)建一個(gè)有限期的隨機(jī)動(dòng)態(tài)規(guī)劃SDP或近似動(dòng)態(tài)規(guī)劃ADP模型狀態(tài)變量S_t (I_t, d_t)其中I_t是當(dāng)前庫(kù)存水平d_t是當(dāng)天的預(yù)測(cè)需求隨機(jī)變量有其分布。決策變量補(bǔ)貨量Q_t或定價(jià)p_t如果定價(jià)也是決策。狀態(tài)轉(zhuǎn)移I_{t1} max(0, I_t Q_t - min(D_t, I_t Q_t)) * (1-θ)。這里min(D_t, I_tQ_t)是實(shí)際銷量。即時(shí)成本/收益R_t p_t * min(D_t, I_tQ_t) - c * Q_t - h * I_t - v * (I_t * θ)。其中c是進(jìn)貨成本h是單位持有成本v是單位損耗成本可能等于c。目標(biāo)最大化總期望利潤(rùn)E[∑ R_t]。直接求解這個(gè)SDP在商品種類多、周期長(zhǎng)時(shí)是“維數(shù)災(zāi)難”。因此我們通常采用近似方法模型預(yù)測(cè)控制MPC在每個(gè)決策點(diǎn)每天我們只優(yōu)化未來(lái)一個(gè)較短的時(shí)間窗口如未來(lái)3-7天的決策并只執(zhí)行第一天的決策。第二天根據(jù)新的實(shí)際銷售和庫(kù)存狀態(tài)重新滾動(dòng)優(yōu)化。這大大降低了計(jì)算復(fù)雜度且能適應(yīng)不確定性?;诜抡娴膬?yōu)化將補(bǔ)貨策略參數(shù)化例如策略為“當(dāng)庫(kù)存低于R時(shí)補(bǔ)貨到S水平”然后通過(guò)大量隨機(jī)需求仿真評(píng)估不同(R, S)參數(shù)下的平均利潤(rùn)選擇最優(yōu)參數(shù)。4. 方案集成與求解從理論模型到可執(zhí)行代碼將預(yù)測(cè)、定價(jià)、補(bǔ)貨三個(gè)模塊集成起來(lái)并求解出可執(zhí)行的決策是最后也是最關(guān)鍵的一步。這里容易陷入兩個(gè)極端一是追求模型的復(fù)雜和完美導(dǎo)致無(wú)法求解二是模型過(guò)于簡(jiǎn)化脫離實(shí)際。4.1 設(shè)計(jì)求解框架MPC是實(shí)戰(zhàn)中的“瑞士軍刀”我強(qiáng)烈推薦使用模型預(yù)測(cè)控制MPC作為整體求解框架。它的思想非常契合這類動(dòng)態(tài)隨機(jī)優(yōu)化問(wèn)題且易于理解和實(shí)現(xiàn)。MPC工作流程如下初始化在第一天開(kāi)始時(shí)擁有當(dāng)前庫(kù)存I_0和未來(lái)一段時(shí)間如7天的外部信息預(yù)測(cè)天氣、節(jié)假日。滾動(dòng)窗口優(yōu)化 a.預(yù)測(cè)基于當(dāng)前信息和歷史數(shù)據(jù)使用第3.1節(jié)的混合模型預(yù)測(cè)未來(lái)N天預(yù)測(cè)窗口內(nèi)每天的需求分布可以是一個(gè)點(diǎn)估計(jì)也可以是多個(gè)情景。 b.優(yōu)化建立一個(gè)未來(lái)N天的確定性或隨機(jī)優(yōu)化模型。決策變量是未來(lái)N天每天的補(bǔ)貨量Q_t和定價(jià)p_t。目標(biāo)函數(shù)是這N天的總期望利潤(rùn)。約束包括庫(kù)存動(dòng)態(tài)、倉(cāng)庫(kù)容量、補(bǔ)貨上限等。此時(shí)我們將未來(lái)需求的不確定性用上一步得到的預(yù)測(cè)值或幾個(gè)代表性情景來(lái)近似替代。c.執(zhí)行求解這個(gè)優(yōu)化問(wèn)題得到未來(lái)N天的最優(yōu)決策序列{Q_1*, p_1*, Q_2*, p_2*, ..., Q_N*, p_N*}。但只執(zhí)行第一天的決策即按照Q_1*補(bǔ)貨按照p_1*定價(jià)。滾動(dòng)更新進(jìn)入第二天。觀察到第一天的實(shí)際銷量D_1_real和實(shí)際損耗更新庫(kù)存狀態(tài)I_1。將時(shí)間窗口向前滾動(dòng)一天重復(fù)步驟2。MPC的優(yōu)點(diǎn)在于“反饋校正”。它用最新的實(shí)際數(shù)據(jù)來(lái)修正模型預(yù)測(cè)和優(yōu)化決策對(duì)預(yù)測(cè)誤差有很強(qiáng)的魯棒性。預(yù)測(cè)窗口N的選擇是個(gè)權(quán)衡N太短可能目光短淺N太長(zhǎng)預(yù)測(cè)不準(zhǔn)優(yōu)化問(wèn)題也難解。對(duì)于蔬菜考慮到其易腐性和銷售周期性N3到7天通常是個(gè)合理范圍。4.2 優(yōu)化模型求解線性/整數(shù)規(guī)劃與啟發(fā)式算法MPC框架中的核心是第2步的優(yōu)化模型。這個(gè)模型通常可以表述為一個(gè)混合整數(shù)線性規(guī)劃MILP問(wèn)題。模型要素示例集合天數(shù)t in {1,...,N}商品i in {1,...,M}。參數(shù)d_{i,t}商品i在第t天的預(yù)測(cè)需求點(diǎn)估計(jì)。c_i商品i的進(jìn)貨成本。p_{i,t}決策變量商品i在第t天的售價(jià)或作為參數(shù)如果定價(jià)策略已定。h_i單位持有成本。θ_i日損耗率。Cap倉(cāng)庫(kù)總?cè)萘?。Shelf_i商品i的貨架空間。決策變量Q_{i,t}連續(xù)變量商品i在第t天的補(bǔ)貨量。I_{i,t}連續(xù)變量商品i在第t天結(jié)束時(shí)的庫(kù)存。Sales_{i,t}連續(xù)變量商品i在第t天的實(shí)際銷量≤ min(需求 可用庫(kù)存)。目標(biāo)函數(shù)最大化總利潤(rùn)∑_t ∑_i (p_{i,t} * Sales_{i,t} - c_i * Q_{i,t} - h_i * I_{i,t})。約束條件庫(kù)存平衡I_{i,t} (I_{i,t-1} Q_{i,t} - Sales_{i,t}) * (1 - θ_i)。銷量限制Sales_{i,t} ≤ d_{i,t}且Sales_{i,t} ≤ I_{i,t-1} Q_{i,t}。容量約束∑_i I_{i,t} ≤ Cap和I_{i,t} ≤ Shelf_i。非負(fù)與邏輯約束所有變量≥0。這個(gè)MILP模型可以用專業(yè)的優(yōu)化求解器如PuLP調(diào)用CBC、ortools、Gurobi、CPLEX來(lái)求解。對(duì)于大規(guī)模問(wèn)題商品數(shù)M很多求解可能較慢這時(shí)可以分解按品類或關(guān)聯(lián)性不強(qiáng)的商品分組獨(dú)立優(yōu)化。啟發(fā)式規(guī)則先用簡(jiǎn)單的經(jīng)驗(yàn)規(guī)則如報(bào)童模型計(jì)算出每個(gè)單品的大致補(bǔ)貨量再用優(yōu)化模型進(jìn)行微調(diào)和容量協(xié)調(diào)。遺傳算法/模擬退火如果模型非線性程度高可以嘗試元啟發(fā)式算法尋找滿意解。4.3 參考代碼結(jié)構(gòu)一個(gè)模塊化的實(shí)現(xiàn)藍(lán)圖下面給出一個(gè)高度概括的、模塊化的Python代碼結(jié)構(gòu)展示了如何將上述思路串聯(lián)起來(lái)。這只是一個(gè)框架每個(gè)函數(shù)都需要根據(jù)具體數(shù)據(jù)和模型進(jìn)行填充。import pandas as pd import numpy as np from prophet import Prophet import xgboost as xgb from pulp import LpProblem, LpVariable, lpSum, LpMaximize, LpStatus, PULP_CBC_CMD # 或者使用 ortools.linear_solver import pywraplp class VegetablePricingReplenishmentSystem: def __init__(self, historical_data, cost_data, config): 初始化系統(tǒng)。 historical_data: 歷史銷售、庫(kù)存、價(jià)格、特征數(shù)據(jù)。 cost_data: 商品成本、持有成本、損耗率等。 config: 配置參數(shù)如預(yù)測(cè)窗口、優(yōu)化窗口、倉(cāng)庫(kù)容量等。 self.hist_data historical_data self.cost_data cost_data self.config config self.demand_models {} # 存儲(chǔ)每個(gè)商品的需求預(yù)測(cè)模型 self.current_inventory self._get_initial_inventory() def _build_demand_forecast_model(self, product_id): 為單個(gè)商品構(gòu)建混合需求預(yù)測(cè)模型如3.1節(jié)所述。 # 1. 提取該商品歷史數(shù)據(jù) product_data self.hist_data[self.hist_data[product_id] product_id].copy() # 2. 使用時(shí)序模型如Prophet擬合基準(zhǔn) # 3. 計(jì)算殘差 # 4. 使用XGBoost擬合殘差 # 5. 保存模型到 self.demand_models[product_id] pass def forecast_demand(self, product_id, future_features_df): 給定未來(lái)特征預(yù)測(cè)商品在未來(lái)N天的需求。 if product_id not in self.demand_models: self._build_demand_forecast_model(product_id) model self.demand_models[product_id] # 使用時(shí)序部分預(yù)測(cè) temporal_pred model[temporal].predict(future_features_df[[ds]]) # 用機(jī)器學(xué)習(xí)部分預(yù)測(cè)殘差 residual_pred model[ml].predict(future_features_df[model[feature_cols]]) final_pred temporal_pred residual_pred return final_pred def mpc_optimization(self, current_date, horizon7): 執(zhí)行一次MPC滾動(dòng)優(yōu)化返回當(dāng)天決策。 decisions {} all_products self.cost_data[product_id].unique() # 步驟1預(yù)測(cè)未來(lái)horizon天的需求 future_dates pd.date_range(startcurrent_date, periodshorizon, freqD) future_features self._generate_future_features(future_dates) # 構(gòu)建未來(lái)特征已知節(jié)假日、計(jì)劃天氣等 demand_forecast {} for pid in all_products: demand_forecast[pid] self.forecast_demand(pid, future_features) # 步驟2構(gòu)建并求解未來(lái)horizon天的優(yōu)化模型 prob LpProblem(Daily_Replenishment_Pricing, LpMaximize) # 定義決策變量字典 Q LpVariable.dicts(Q, (all_products, range(horizon)), lowBound0) # 補(bǔ)貨量 I LpVariable.dicts(I, (all_products, range(horizon1)), lowBound0) # 庫(kù)存多一期用于初始 S LpVariable.dicts(S, (all_products, range(horizon)), lowBound0) # 銷量 # 設(shè)置初始庫(kù)存 for pid in all_products: I[pid][0] self.current_inventory.get(pid, 0) # 目標(biāo)函數(shù)總利潤(rùn) profit_expr [] for t in range(horizon): for pid in all_products: cost self.cost_data.loc[pid, unit_cost] price ... # 這里價(jià)格可以是決策變量也可以是需求函數(shù)反推的結(jié)果簡(jiǎn)化起見(jiàn)可以先固定或根據(jù)規(guī)則設(shè)定 holding_cost self.cost_data.loc[pid, holding_cost] profit_expr.append(price * S[pid][t] - cost * Q[pid][t] - holding_cost * I[pid][t]) prob lpSum(profit_expr) # 約束條件 for t in range(horizon): # 庫(kù)存平衡約束 for pid in all_products: spoilage_rate self.cost_data.loc[pid, spoilage_rate] prob I[pid][t1] (I[pid][t] Q[pid][t] - S[pid][t]) * (1 - spoilage_rate) # 銷量約束不能超過(guò)預(yù)測(cè)需求和可用庫(kù)存 for pid in all_products: prob S[pid][t] demand_forecast[pid][t] prob S[pid][t] I[pid][t] Q[pid][t] # 倉(cāng)庫(kù)容量約束簡(jiǎn)化示例只考慮總庫(kù)存體積 total_volume lpSum(I[pid][t1] * self.cost_data.loc[pid, volume_per_unit] for pid in all_products) prob total_volume self.config[warehouse_capacity] # 求解 solver PULP_CBC_CMD(msgFalse) prob.solve(solver) # 步驟3提取并返回第一天的決策 if LpStatus[prob.status] Optimal: for pid in all_products: decisions[pid] { replenish: Q[pid][0].varValue, price: ... # 從解中獲取或根據(jù)規(guī)則計(jì)算 } else: # 如果無(wú)解啟用備用啟發(fā)式規(guī)則 decisions self._fallback_heuristic(all_products, demand_forecast) return decisions def _fallback_heuristic(self, product_ids, demand_forecast): 備用啟發(fā)式規(guī)則例如采用報(bào)童模型計(jì)算補(bǔ)貨量?jī)r(jià)格采用成本加成法。 decisions {} for pid in product_ids: # 簡(jiǎn)單報(bào)童模型補(bǔ)貨量 預(yù)測(cè)需求 安全庫(kù)存 forecast_d demand_forecast[pid][0] safety_stock ... # 根據(jù)歷史需求波動(dòng)計(jì)算 decisions[pid] { replenish: max(0, forecast_d safety_stock - self.current_inventory.get(pid, 0)), price: self.cost_data.loc[pid, unit_cost] * 1.3 # 成本加成30% } return decisions def run_daily_operation(self, start_date, end_date): 模擬系統(tǒng)從start_date到end_date的每日運(yùn)行。 current_date start_date decision_log [] while current_date end_date: # 1. 執(zhí)行MPC優(yōu)化得到今日決策 daily_decisions self.mpc_optimization(current_date) # 2. 模擬執(zhí)行決策在實(shí)際系統(tǒng)中這是真實(shí)操作 actual_sales self._simulate_sales(current_date, daily_decisions) # 3. 更新庫(kù)存狀態(tài)考慮實(shí)際銷量和損耗 self._update_inventory(current_date, daily_decisions, actual_sales) # 4. 記錄日志 decision_log.append({date: current_date, decisions: daily_decisions, actual_sales: actual_sales}) # 5. 進(jìn)入下一天 current_date pd.Timedelta(days1) return pd.DataFrame(decision_log) # 主程序入口 if __name__ __main__: # 加載數(shù)據(jù) hist_df pd.read_csv(historical_sales.csv) cost_df pd.read_csv(product_costs.csv).set_index(product_id) config {warehouse_capacity: 10000, forecast_horizon: 7} # 初始化系統(tǒng) system VegetablePricingReplenishmentSystem(hist_df, cost_df, config) # 運(yùn)行模擬 start_date pd.Timestamp(2023-06-01) end_date pd.Timestamp(2023-06-30) operation_log system.run_daily_operation(start_date, end_date) print(operation_log.tail())5. 模型檢驗(yàn)、靈敏度分析與論文呈現(xiàn)要點(diǎn)建好模型和系統(tǒng)后如何驗(yàn)證其有效性并在論文中清晰呈現(xiàn)是拿高分的關(guān)鍵。5.1 模型檢驗(yàn)回溯測(cè)試與關(guān)鍵績(jī)效指標(biāo)KPI絕不能只用訓(xùn)練集上的表現(xiàn)來(lái)評(píng)價(jià)模型。必須進(jìn)行嚴(yán)格的回溯測(cè)試。方法將歷史數(shù)據(jù)按時(shí)間劃分為訓(xùn)練集和測(cè)試集例如用前10個(gè)月訓(xùn)練后2個(gè)月測(cè)試。在測(cè)試集上模擬系統(tǒng)逐日運(yùn)行每天開(kāi)始時(shí)系統(tǒng)只知道過(guò)去的信息用它來(lái)做預(yù)測(cè)和優(yōu)化決策然后將決策與當(dāng)天的“真實(shí)”數(shù)據(jù)在測(cè)試集中進(jìn)行比較。需要計(jì)算的KPI包括利潤(rùn)相關(guān)總利潤(rùn)、平均毛利率、投資回報(bào)率。庫(kù)存相關(guān)平均庫(kù)存水平、庫(kù)存周轉(zhuǎn)天數(shù)、缺貨率需求大于庫(kù)存的天數(shù)比例。損耗相關(guān)總損耗金額、損耗率損耗量/總進(jìn)貨量。預(yù)測(cè)準(zhǔn)確性使用均方根誤差RMSE、平均絕對(duì)百分比誤差MAPE評(píng)估需求預(yù)測(cè)的精度。特別注意評(píng)估在庫(kù)存不足可能缺貨的那些日子的預(yù)測(cè)誤差這更能反映模型估算“真實(shí)需求”的能力。將你的模型與基準(zhǔn)策略對(duì)比例如簡(jiǎn)單策略每天補(bǔ)貨到固定水平如過(guò)去7天平均銷量按成本固定比例加成定價(jià)。經(jīng)驗(yàn)策略模擬有經(jīng)驗(yàn)的店長(zhǎng)的決策規(guī)則可從數(shù)據(jù)中歸納如周末多進(jìn)20%。 只有顯著、穩(wěn)定地超越了基準(zhǔn)策略你的模型才算有價(jià)值。5.2 靈敏度分析找出模型的“脆弱點(diǎn)”和“穩(wěn)健性”模型依賴于許多假設(shè)和參數(shù)如損耗率θ、價(jià)格彈性e、持有成本h。靈敏度分析就是觀察當(dāng)這些參數(shù)在合理范圍內(nèi)變動(dòng)時(shí)模型性能如總利潤(rùn)的變化情況。如何做單因素分析固定其他參數(shù)讓一個(gè)參數(shù)如損耗率在[θ-Δθ, θΔθ]范圍內(nèi)變化運(yùn)行多次模擬觀察利潤(rùn)的變化曲線。如果利潤(rùn)對(duì)該參數(shù)變化非常敏感說(shuō)明模型在該參數(shù)上很脆弱需要更精確地估計(jì)該參數(shù)或者在策略中加入魯棒性設(shè)計(jì)。多因素分析可以使用拉丁超立方抽樣等方法同時(shí)擾動(dòng)多個(gè)參數(shù)觀察利潤(rùn)的分布。這能評(píng)估模型在參數(shù)不確定性下的整體穩(wěn)健性。場(chǎng)景分析模擬一些極端但可能發(fā)生的場(chǎng)景例如連續(xù)三天暴雨導(dǎo)致客流減少50%某主要供應(yīng)商突然斷供一周。你的系統(tǒng)決策會(huì)產(chǎn)生什么變化利潤(rùn)會(huì)下降多少這能體現(xiàn)系統(tǒng)的抗風(fēng)險(xiǎn)能力。在論文中用圖表清晰地展示靈敏度分析的結(jié)果例如畫(huà)出利潤(rùn)隨關(guān)鍵參數(shù)變化的折線圖并給出文字解釋“如圖所示當(dāng)損耗率估計(jì)誤差在±20%內(nèi)時(shí)系統(tǒng)利潤(rùn)波動(dòng)在5%以內(nèi)表明模型對(duì)損耗率參數(shù)具有一定的魯棒性。”5.3 論文寫(xiě)作與可視化講好一個(gè)邏輯閉環(huán)的故事國(guó)賽論文評(píng)審時(shí)間緊清晰的邏輯和直觀的可視化至關(guān)重要。行文邏輯建議問(wèn)題重述與分析不要照抄題目要用自己的話精煉概括并明確指出問(wèn)題的核心矛盾如利潤(rùn)最大化與損耗最小化的權(quán)衡和子問(wèn)題劃分。模型準(zhǔn)備詳細(xì)闡述數(shù)據(jù)預(yù)處理過(guò)程特別是“真實(shí)需求”估算、特征工程方法。這是體現(xiàn)你工作扎實(shí)程度的地方。模型建立分節(jié)闡述預(yù)測(cè)模型、定價(jià)模型、補(bǔ)貨模型。對(duì)每個(gè)模型都要說(shuō)明為什么選擇這個(gè)模型與替代模型相比的優(yōu)勢(shì)以及模型如何與問(wèn)題結(jié)合公式、變量含義要解釋清楚。模型求解重點(diǎn)介紹MPC框架和優(yōu)化模型的求解方法。如果是用求解器說(shuō)明是什么求解器如果用了啟發(fā)式算法描述算法步驟。模型檢驗(yàn)與靈敏度分析展示回溯測(cè)試的結(jié)果表格和對(duì)比圖用數(shù)據(jù)證明模型的有效性。展示靈敏度分析的圖表和結(jié)論。模型評(píng)價(jià)與推廣客觀評(píng)價(jià)模型的優(yōu)點(diǎn)和局限性例如假設(shè)需求函數(shù)形式固定未考慮競(jìng)爭(zhēng)對(duì)手突然降價(jià)等。提出可能的改進(jìn)方向并簡(jiǎn)要說(shuō)明模型思想可以推廣到其他易腐品零售如水果、鮮花、烘焙食品??梢暬c(diǎn)系統(tǒng)框架圖用清晰的框圖展示“數(shù)據(jù)輸入 - 預(yù)測(cè)模塊 - 優(yōu)化模塊 - 決策輸出 - 滾動(dòng)更新”的整個(gè)流程。預(yù)測(cè)效果圖對(duì)于幾個(gè)代表性商品畫(huà)出歷史期真實(shí)銷量、模型擬合銷量以及測(cè)試期預(yù)測(cè)銷量與真實(shí)銷量的對(duì)比折線圖。決策模擬圖選擇一段時(shí)間如測(cè)試期兩周畫(huà)出庫(kù)存水平、補(bǔ)貨量、價(jià)格、銷量的聯(lián)動(dòng)變化圖直觀展示模型決策的動(dòng)態(tài)過(guò)程。靈敏度分析圖用折線圖或熱力圖展示關(guān)鍵參數(shù)變化對(duì)核心KPI的影響。結(jié)果對(duì)比圖用柱狀圖對(duì)比你的模型與基準(zhǔn)策略在多個(gè)KPI上的表現(xiàn)。記住論文的本質(zhì)是向評(píng)委講述你發(fā)現(xiàn)問(wèn)題、分析問(wèn)題、解決問(wèn)題、驗(yàn)證方案的完整思考過(guò)程。代碼是工具模型是手段清晰的邏輯和令人信服的證據(jù)鏈才是獲得高分的關(guān)鍵。這道C題就像一個(gè)微縮的商業(yè)智能系統(tǒng)項(xiàng)目完整地走一遍無(wú)論比賽結(jié)果如何對(duì)解決實(shí)際問(wèn)題的能力都是極大的提升。