决策树

简单的决策树可以画一个流程图解决,只需要3种节点:决策节点、事件节点、结果节点

一、假设某药店需要采购一批新冠特效药,该特效药保质期不长,需要在3个月内售完。现有供应商A:产能5000盒,签约费用0,单价120;供应商B:产能10000盒,签约费用50000,单价100。假设未来3个月的市场热度高(卖10000盒)、低(卖5000盒)的概率都是50%,如果你是药店店长,请问你会如何做决策?

决策策略

  • 极大最小化策略:安全系数最高,此策略会选择供应商A,结果为:150000
  • 极大最大化策略:高风险高收益,此策略会选择供应商B,结果为:450000
  • 最大化期望值策略:风险适中,根据好坏结果的比重加权计算得出,结果为:150000(加权后)

二、题目升级,假设热度高和低的概率依然一样(各50%),但是热度高(6000盒到14000盒)、热度低(2000盒到8000盒),都是均匀分布

import numpy as np

low = np.random.uniform(2000, 8000, 10000)
high = np.random.uniform(6000, 14000, 10000)


def calc(mock_data, supplier):
    mock_data = mock_data.copy()
    if supplier == 'A':
        mock_data[mock_data > 5000] = 5000
        mock_data = mock_data * 150 - 5000 * 120
    else:
        mock_data[mock_data > 10000] = 10000
        mock_data = mock_data * 150 - 10000 * 100 - 50000
    return mock_data


a_low = calc(low, 'A')
a_high = calc(high, 'A')
b_low = calc(low, 'B')
b_high = calc(high, 'B')

# 根据大数定律,计算出模拟数据的期望值,然后再使用最大化期望值策略,得出:供应商A依然是最好的选项
a = 0.5 * a_low.mean() + 0.5 * a_high.mean()
b = 0.5 * b_low.mean() + 0.5 * b_high.mean()

print(a)
print(b)

三、题目再次升级,供应商B的签约费用提升到100000,但是前期只需要定4000盒,剩下的6000盒可以根据市场热度再定

import numpy as np

low = np.random.uniform(2000, 8000, 10000)
high = np.random.uniform(6000, 14000, 10000)


def calc(mock_data, supplier, heat):
    mock_data = mock_data.copy()
    if supplier == 'A':
        mock_data[mock_data > 5000] = 5000
        mock_data = mock_data * 150 - 5000 * 120
    else:
        if heat == 'low':
            mock_data[mock_data > 4000] = 4000
            mock_data = mock_data * 150 - 4000 * 100 - 100000
        else:
            mock_data[mock_data > 10000] = 10000
            mock_data = mock_data * 150 - 10000 * 100 - 100000


    return mock_data


a_low = calc(low, 'A', 'low')
a_high = calc(high, 'A', 'high')
b_low = calc(low, 'B', 'low')
b_high = calc(high, 'B', 'high')

a = 0.5 * a_low.mean() + 0.5 * a_high.mean()
b = 0.5 * b_low.mean() + 0.5 * b_high.mean()

print(a)
print(b)

四、题目再次升级,供应商B的签约费用提升到100000,但是前期只需要定4000盒,然后可以根据市场热度再定0到6000盒,请问你会定多少盒?

这个涉及最优解问题,参照前面最优解的示例进行解答

import numpy as np
from scipy import optimize

# low跟high的模拟数据要在外层设置并固定下来
# 否则目标函数最优解每次都能重刷low跟high,结果必定会为4000和10000
low = np.random.uniform(2000, 8000, 10000)
high = np.random.uniform(6000, 14000, 10000)


# 目标函数(计算结果),x[0]表示订购数量
def calcLow(x):
    global low
    temp = low.copy()
    temp[temp > x[0]] = x[0]
    temp = 150 * temp - 100 * x[0] - 100000
    return temp.mean()


# 目标函数(minimize是求最小值的,我们需要的是最大值,所以将计算结果取反)
def objectiveLow(x):
    return -calcLow(x)


# 目标函数(计算结果),x[0]表示订购数量
def calcHigh(x):
    global high
    temp = high.copy()
    temp[temp > x[0]] = x[0]
    temp = 150 * temp - 100 * x[0] - 100000
    return temp.mean()


# 目标函数(minimize是求最小值的,我们需要的是最大值,所以将计算结果取反)
def objectiveHigh(x):
    return -calcHigh(x)


# 约束条件(ineq表示大于等于(eq表示等于),fun表示约束函数)
cons = (
    {'type': 'ineq', 'fun': lambda x: x[0] - 4000},
    {'type': 'ineq', 'fun': lambda x: 10000 - x[0]}
)

# 初始猜测值
x_low = np.array([4000])
x_high = np.array([10000])

# 最优解计算
# message:Desired error not necessarily achieved due to precision loss.
# 以上信息为浮点数精度损失,可忽略,对结果影响不大
res_low = optimize.minimize(objectiveLow, x_low, options={"disp": True})
print(res_low)
print(calcLow([int(x) for x in res_low.x]))

res_high = optimize.minimize(objectiveHigh, x_high, options={"disp": True})
print(res_high)
print(calcHigh([int(x) for x in res_high.x]))

该案例使用了最大化期望值策略,但是如果你是一个风险厌恶的人

  • 我们可以在这个基础上进行风险约束(将标准差小于某个值添加到约束条件中)
  • 适当调低最大订购量(将10000适当调低)

results matching ""

    No results matching ""