前面已经把商品库存按处理方式主要分为以下几种normal sale, return, markdown, liquidation。
现在明确一下,算法是以追求收益最大化为目标,也就是当前手里的这些商品,不考虑进价的情况下,如果能够在未来一定时间内折算成现金,这个现金要是 最大的。现在已知库房有某种商品N个,它是不是除了正常销售之外还有多的M个,这M个商品是不是都是健康的,如果其中有不健康的,我们应该采取哪一种或者 哪几种方式来清理,每种方式各多少个。
举个例子,以一年52周为一个完整的周期,我们已知销量服从某个概率分布,为了便于计算,可以选取等概率的几个值作为多种可能的销量预测。如果不考虑计算复杂度,抽样点当然越多越好。在某一种销量预测下,对当前周
1)假设100个商品,当周可以卖10个,不进行任何清理,那么剩下90个留到下一周,这90个会在第二周产生一个新的价值,也会在当周产生一个持有成本的消耗。它们的差值加上当周卖出10个产生的收益的和,作为在当周该种处理方式的总收益。
2)换一种处理方式,如果return一个,剩下89个留到下周,总收益可以表示为,卖了10个加上 退了一个 减去 89个一周的持有成本 加上89个在第二周的价值。
3)对于第二周产生的价值,它依赖于本周的组合价值以及第三周的价值。
4)一直到最后一周。
以此类推,求出每一种组合在所有不同销量概率预测上的价值均值,然后尝试所有不同组合,选出一个均值最大的组合。建立一个二维坐标,横轴以时间周为单位,纵轴为商品个数,可以求出每一个交叉点的最优值。左上角那个点即为所求。
现在看看怎么优化一下,如果按照上面的思路写成递归,复杂度太高,注意到当前周的计算依赖于下一周,所以实现上应该从以后往当前,横坐标的反方向,就是动态规划,存储上可以做一个二级hash表,分别用时间和总数量作为key,最优值作为value。写成代码,大致是
[java] view plaincopy
- for(T:52 --> 1){
- for(total : 0 --> 100){
- for(markdown : 0 --> total){
- for(return : 0 --> total){
- for(liquidate : 0 --> total){
- }
- }
- }
- }
- }
继续优化,因为里面还是有重复运算,比如,计算total等于100,markdown一个return一个保留98个的当周最优值,实际上包含了 total=99,markdown一个保留98个的当周最优值的计算。素以可以先计算所有markdown的情况,将二维坐标想成三维,有三个平面,从 下往上是markdown平面,return平面和liquidation平面,上面依赖下面,最下面的markdown是上周依赖下周。
[java] view plaincopy
- for(T:52 --> 1){
- for(total : 0 --> 100){
- for(markdown : 0 --> total){
- }
- }
- for(total : 0 --> 100){
- for(return : 0 --> total){
- }
- }
- for(total : 0 --> 100){
- for(liquidate : 0 --> total){
- }
- }
以上只是个大概的思路,具体应用到业务模型上,还有很多细节要处理。
比如,初始那一周的值该如何计算,因为第52周是终点,以后的周没有销量预测了,所以这一周的价值,必须模拟正常销售,可以取52周销量预测均值。 这会导致一个问题,某些商品一年之后销量衰减非常快,没有办法用均值模拟,这是可以使用机器分类鉴别出这些商品,再采用特殊的曲线模拟。
比如有些商品是有过期时间的,这样的商品在离过期时间点越近的时候是不好卖的,而且买家如果退货,只能销毁,所以怎样提前把这些商品清理掉?可以模拟一个价格曲线,这个曲线在越临近时间点衰减的越厉害,在系统中自然价值越来越小。
比如,markdown和normal sell实际是不能并存的,他们之间的价值该如何处理?
比如,退货一般是有限制的,合同里会写上这批货你最多只能退百分之多少。可以考虑在动态规划里增加一个维度。
比如,算法中假设未来的售价是不变的,实际商品价格会有波动,这个也是机器学习需要解决的问题。
比如,某些input参数取不到,该如何处理?是不是可以考虑使用产品线、产品族的均值代替?
另外,每周的销量预测实际是个很困难很复杂的问题,而且它的影响很大,针对历史悠久的商品和新上线的商品,采用时间序列和随机森林相结合的机器学习方法。