抽样平均近似方法 Sample Average Approximation (SAA)

多次听说或看到抽样平均方法 Sample Average Approximation ,但不是真正理解这个方法到底怎么用的。决定写一篇博客,将每次看到的心得体会记录下来。

对于一个随机规划问题,

min ⁡ f ( x , ξ ) \min\quad f(x, \xi) minf(x,ξ)

其中 ξ \xi ξ 为一个随机变量,而 x x x 是一个求解变量。抽样平均方法 SAA 的大致含义是用抽样的方法将随机变量用样本表示,从而将随机规划问题转化为确定性问题。 假设随机变量 ξ \xi ξ 的样本分别为: ξ 1 , ξ 2 , … , ξ N \xi_1, \xi_2,\dots, \xi_N ξ1,ξ2,,ξN. 则原问题转化为:

min ⁡ 1 N ∑ i = 1 N f ( x , ξ i ) \min\quad \frac{1}{N}\sum_{i=1}^{N} f(x, \xi_i) minN1i=1Nf(x,ξi)

而在每个样本下, min ⁡ f ( x , ξ ) \min f(x, \xi) minf(x,ξ) 可以通过线性规划求解。

该方法有一定的适用范围,这一点还没弄清楚。

1. 单阶段时

例如一个单阶段报童模型:

max ⁡ π = p min ⁡ { Q , d } − c Q + γ max ⁡ { Q − d , 0 } \max\quad\pi= p\min\{Q, d\}-cQ+\gamma\max\{Q-d, 0\} maxπ=pmin{Q,d}cQ+γmax{Qd,0}

假如随机需求抽样了 N N N 个样本,每个样本值 d i d^i di,则表达式可以转化为:
max ⁡ π = 1 N ∑ i = 1 N p min ⁡ { Q , d i } − c Q + γ max ⁡ { Q − d i , 0 } \max\quad\pi= \frac{1}{N}\sum_{i=1}^Np\min\{Q, d^i\}-cQ+\gamma\max\{Q-d^i, 0\} maxπ=N1i=1Npmin{Q,di}cQ+γmax{Qdi,0}

进一步转化为可以用规划软件求解的混合整数规划模型,首先引入两个辅助变量表示目标函数中的 min ,max 表达式:

max ⁡ π = 1 N ∑ i = 1 N ( p x i + γ y i ) − c Q s . t . ∀ i = 1 , 2 , … , N x i = min ⁡ { Q , d i } y i = max ⁡ { Q − d i , 0 } Q ≥ 0 \begin{aligned} \max\quad&\pi= \frac{1}{N}\sum_{i=1}^N(px^i+\gamma y^i)-cQ\\ s.t.\quad&\forall i=1, 2, \dots, N\\ &x^i=\min\{Q, d^i\}\\ &y^i=\max\{Q-d^i, 0\}\\ & Q\geq 0 \end{aligned} maxs.t.π=N1i=1N(pxi+γyi)cQi=1,2,,Nxi=min{Q,di}yi=max{Qdi,0}Q0

可以再进一步引入辅助变量 δ \delta δ 将约束条件中的 min 或 max 转化为线性表达式,不过很多规划软件已经可以求解了。

使用 python 调用 gurobi 实现单阶段报童模型的例子:

# -*- coding: utf-8 -*-
"""
Created on Tue Feb 23 15:08:06 2021

@author: zhen chen

MIT Licence.

Python version: 3.7


Description: SAA for single period newsvendor
"""
    
import numpy as np
import scipy.stats as st
from gurobipy import *


# 使用拉丁超立方抽样生成样本
def generate_sample(sample_num, trunQuantile, mu):
    samples = [0 for i in range(sample_num)]
    for i in range(sample_num):
        rand_p = np.random.uniform(trunQuantile*i/sample_num, trunQuantile*(i+1)/sample_num)
        samples[i] = st.poisson.ppf(rand_p, mu)
    return samples


price  =  6
vari_cost = 2
sal_value = 1
mean_demands = 5
sample_num = 10
trunQuantile = 0.999

samples = generate_sample(sample_num, trunQuantile, mean_demands)
   

try:
    # Create a new model
    m = Model("saa-mip")
    
    # Create variables
    Q = m.addVar(vtype = GRB.CONTINUOUS) # only one value for all scenarios           
    realized_demand = [m.addVar(vtype = GRB.CONTINUOUS) for s in range(sample_num)] # auxiliary variable
    remnant_demand = [m.addVar(vtype = GRB.CONTINUOUS) for s in range(sample_num)] # auxiliary variable
    profit = LinExpr() 
    
    profit = price * sum(realized_demand) / sample_num - vari_cost * Q + sal_value * sum(remnant_demand) / sample_num
    
    m.update()
    m.setObjective(profit, GRB.MAXIMIZE)
    
    # Add constraints
    for s in range(sample_num):
        m.addGenConstrMin(realized_demand[s], [Q], samples[s])
        m.addConstr(remnant_demand[s] == Q - realized_demand[s])
        
    m.update()
    m.optimize()
    
    print('ordering quantity in the first period is:  %.2f ' % Q.X)
    
except GurobiError as e:
        print('Error code ' + str(e.errno) + ": " + str(e))    
        
except AttributeError:
        print('Encountered an attribute error')

基本上只需 10 个需求样本就能算出正确的最优订货量 7.

2. 多阶段时

多阶段的基本思路跟单阶段一样,只是每个阶段都要抽取 N N N 个样本,所以 T T T 个阶段会有 N T N^T NT 个样本,运算复杂度为指数级。此时,可能会涉及到怎么减少样本量,同时又不那么影响计算结果的处理技巧。

我试了试多阶段的报童模型,最优期望利润一般比随机动态规划的计算结果大一些。每个阶段的样本数比较小时,计算结果波动较大。若阶段数目增加到 4 个阶段以上,每个阶段样本数为 10,SAA 的计算速度实在太慢了(实践觉得,只要总样本数小于 10 万,gurobi 求解 SAA 还是挺快的)。

多阶段时,SAA 求解结果的上界下界不太好确定,只能求到一个下界(最小化问题时),对于上界,要想方设法得到问题的一个可行解,我看目前的论文中也没有明确。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心态与习惯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值