电商中信息流的实时把控至关重要,在仓库中商品摆放时货架和货位的选择影响了拣货的效率,日常和活动和大促等各种场景下摆放可能会有区别,若是统一位置摆放可能会造成拣货效率低,人工成本高,甚至用户体验差。 如果我们能把系统订单中已推单未配货的数据及时拿到加以提炼,找出商品的最佳组合进行排序,那种组合出现的次数最多那么,这些组合中的商品应该要优先摆放在最容易拿到的货架上面,举例说明: 仓库名称 订单号 69码:数量 临江仓 ECO001 69001:10,69002:3,69003:2 临江仓 ECO002 69001:1 临江仓 ECO003 69002:6,69003:3 临江仓 ECO004 69001:1,69003:2 临江仓 ECO009 69011:1,69012:2 郑州仓 ECO005 69006:8,69003:9 郑州仓 ECO006 69006:8,69003:9 郑州仓 ECO008 69006:8,69003:9 武汉仓 ECO007 69006:8,69003:9,69001:10,69002:3,69008:88 这些数据中,首先拿到这份数据,对数据进行重组合,发货是要到仓的,借助MapReduce的思想,需要把一个仓库中的所有订单号对应的明细拼在一起,组成一个Map数据结构,key是仓库名称,value是一个大数组,大数组中存放一个Map,key为订单号,value为订单号对应的商品明细。然后把所有的订单明细组合起来,进行排列组合。例如: 1,2,3,4,5 5个商品可能会出现的组合: 12,13,14,15 23,24,25 34,35 45 123,124,125 134,135 145 1234,1235 1345 1 2 3 4 5 随机列举了一下一串数字会有n多种组合,数组长度越大,可能出现的子组合越多... 介绍了完了后,我们看下代码,写的仓促,大家发现存在优化点记得联系我 测试数据: 临江仓 ECO001 69001:10,69002:3,69003:2 临江仓 ECO002 69001:1 临江仓 ECO003 69002:6,69003:3 临江仓 ECO004 69001:1,69003:2 临江仓 ECO009 69011:1,69012:2 郑州仓 ECO005 69006:8,69003:9 郑州仓 ECO006 69006:8,69003:9 郑州仓 ECO008 69006:8,69003:9 武汉仓 ECO007 69006:8,69003:9,69001:10,69002:3,69008:88
package PermutationAndCombination;
import java.io.*;
import java.util.*;
/**
* @author 圣斗士宙斯
* @date 2018-11-14 19:08:09
* @Description: 对仓库所有订单的订单明细做排列组合,计算得出最佳组合
*/
public class Main {
/**
* 数据文件路径
*/
public static final String DATAPATH = "C:\\Users\\zhuqitian\\Desktop\\仓库订单明细.txt";
/**
* 字段间分隔符
*/
public static final String FIELDSDELIMITED = "\\t";
/**
* sku信息间的分隔符
*/
public static final String MULTISKUINFODELIMITED = ",";
/**
* 69码跟数量的分隔符
*/
public static final String SINGLESKUINFODELIMITED = ":";
public static void main(String[] args) {
Main main = new Main();
//step 1. 按仓reduce,拿到一个仓库中的所有订单号,以及各个订单号对应的商品69码和quantity(主要用于校验和其他项)
Map<String, List<Map<String, Map<String, Integer>>>> warehouseOrderDetailInfo =
main.getWarehouseOrderDetailInfo(DATAPATH);
// step 1.test success
// Iterator<Map.Entry<String, List<Map<String, Map<String, Integer>>>>> entries =
// warehouseOrderDetailInfo.entrySet().iterator();
// while (entries.hasNext()) {
// Map.Entry<String, List<Map<String, Map<String, Integer>>>> entry = entries.next();
// System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
// }
//step 2. 逐个仓库遍历,按69码排序拿到仓库中所有订单的sku的排列组合一个仓库各个订单的69码组合
Map<String, List<List<Long>>> warehouseOrderCodes =
main.aggWarehouseOrders(warehouseOrderDetailInfo);
warehouseOrderDetailInfo.clear();
// step 2. test success
// Iterator<Map.Entry<String, List<List<Long>>>> entries2 =
// warehouseOrderCodes.entrySet().iterator();
// while (entries2.hasNext()) {
// Map.Entry<String, List<List<Long>>> entry = entries2.next();
// System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
// }
//step 3. 实现排列组合,每一个订单中可能有多个barCode,barcode越多,子组合数也就越多2^n
Map<String, List<List<List<Long>>>> warehouseCombinationOrders =
main.calcWarehouseCombinationOrders(warehouseOrderCodes);
warehouseOrderCodes.clear();
// step 3. test success
// Iterator<Map.Entry<String, List<List<List<Long>>>>> entries3 =
// warehouseCombinationOrders.entrySet().iterator();
// while (entries3.hasNext()) {
// Map.Entry<String, List<List<List<Long>>>> entry = entries3.next();
// System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
// }
// step 4. 按仓拆解子组合过滤空数组,按仓跨订单计算一个子组合出现的次数
Map<String, List<List<Long>>> warehouseAllChildCombination =
main.getWarehouseAllChildCombination(warehouseCombinationOrders);
warehouseCombinationOrders.clear();
// step 4. test success
// Iterator<Map.Entry<String, List<List<Long>>>> entries4 =
// warehouseAllChildCombination.entrySet().iterator();
// while (entries4.hasNext()) {
// Map.Entry<String, List<List<Long>>> entry = entries4.next();
// System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
// }
// step 5. 计算每个仓库中每组商品出现的次数
// 每个仓库所有订单的所有子组合出现的次数
Map<String, Map<List<Long>, Integer>> warehouseAllChildCombinationNumber =
new HashMap<String, Map<List<Long>, Integer>>();
List<Long> singleChildCombination = new ArrayList<>(500);
// 遍历所有仓库
for (String warehouseName : warehouseAllChildCombination.keySet()) {
int childCombinationQuantity = warehouseAllChildCombination.get(warehouseName).size();
// 遍历所有子组合
for (int i = 0; i < childCombinationQuantity; i++) {
// System.out.println(warehouseAllChildCombination.get(warehouseName).get(i));
int count = Collections.frequency(
warehouseAllChildCombination.get(warehouseName),
warehouseAllChildCombination.get(warehouseName).get(i));
System.out.println(warehouseName + ":"+ warehouseAllChildCombination.get(warehouseName).get(i) + " : " + count);
}
}
}
/**
* 计算给定仓库中所有订单的商品组合中的所有可能出现的子组合
*
* @param warehouseOrderCodes 仓库中的所有订单
* @return 仓库 组合BarCodes 一个仓库对应多个订单,一个订单对应一个组合,一个组合对应多个子组合
*/
public Map<String, List<List<List<Long>>>> calcWarehouseCombinationOrders(
Map<String, List<List<Long>>> warehouseOrderCodes) {
Map<String, List<List<List<Long>>>> warehouseCombinationOrders = new HashMap<String, List<List<List<Long>>>>();
List<List<List<Long>>> allCombinationOrders = new ArrayList<List<List<Long>>>();
List<List<Long>> singleCombinationOrders = null;
// 遍历各个仓库下面所有订单
for (String warehouseName : warehouseOrderCodes.keySet()) {
// 拿到指定仓库下面的所有订单号
List<List<Long>> allOrderCodes = warehouseOrderCodes.get(warehouseName);
int len = allOrderCodes.size();
// 遍历所有订单号拿到单个订单号下面的barCodes
for (int i = 0; i < len; i++) {
List<Long> singleOrderCodes = allOrderCodes.get(i);
// 一组商品 可能会出现的所有子组合
singleCombinationOrders = combinationOrders(singleOrderCodes);
allCombinationOrders.add(singleCombinationOrders);
}
warehouseCombinationOrders.put(warehouseName, allCombinationOrders);
allCombinationOrders = new ArrayList<List<List<Long>>>();
}
return warehouseCombinationOrders;
}
/**
* group warehouName,get orderCode : orderDetail
*
* @param dataPath* @return*/
public Map<String, List<Map<String, Map<String, Integer>>>> getWarehouseOrderDetailInfo(String dataPath) {
// 本地文件
File file = new File(dataPath);
// 读取本地文件,封装数据 临江仓 --> [{69001:1,69002:3},{69003=3, 69002=6}] 数组的长度就是单量
Map<String, List<Map<String, Map<String, Integer>>>> warehouseOrderDetailInfo =
new HashMap<String, List<Map<String, Map<String, Integer>>>>();
// orderCode allSku 多个订单中的所有商品信息 ECO001:[69001:1,69002:3],ECO002:[69001:1,69002:3]
List<Map<String, Map<String, Integer>>> multiOrderDetailInfo = null;
List<Map<String, Map<String, Integer>>> multiOrderDetailInfo2 = null;
InputStreamReader isr = null;
BufferedReader br = null;
if (file.isFile() && file.exists()) {
try {
isr = new InputStreamReader(new FileInputStream(file), "utf-8");
br = new BufferedReader(isr);
String lineStr = null;
// all warehouseName
Set<String> warehouse = new HashSet<String>();
String warehouseName = null;
String priorWarehouseOrderInfo = null;
String orderCode = null;
String allSkus = null;
int warehouseNum = 0;
// Eco001 : barcode --> quantity
Map<String, Map<String, Integer>> singleOrderInfo = new HashMap<String, Map<String, Integer>>();
Map<String, Integer> allSku = new HashMap<String, Integer>();
while ((lineStr = br.readLine()) != null) {
warehouseName = lineStr.split(FIELDSDELIMITED)[0];
orderCode = lineStr.split(FIELDSDELIMITED)[1];
allSkus = lineStr.split(FIELDSDELIMITED)[2];
// 历史数据中已经存在当前仓库的订单数据
if (warehouse.contains(warehouseName)) {
allSku = new HashMap<String, Integer>();
singleOrderInfo = new HashMap<String, Map<String, Integer>>();
// 前一个warehouName跟当前warehouseName不同,说明给到的数据是乱序的
// 不能把前一个warehouseName订单信息放在当前warehouseName中
// 但也要保证前一个对象的信息不能清空
// if (!priorWarehouseOrderInfo.equals(warehouseName)) {
// multiOrderDetailInfo2 = multiOrderDetailInfo;
// } else {
splitAllSkuAndPutMap(allSku, allSkus);
singleOrderInfo.put(orderCode, allSku);
splitAllSkuAndPutMap(allSku, allSkus);
singleOrderInfo.put(orderCode, allSku);
// }
// 如果是第一次遍历到一个仓库
} else {
allSku = new HashMap<String, Integer>();
singleOrderInfo = new HashMap<String, Map<String, Integer>>();
multiOrderDetailInfo = new ArrayList<Map<String, Map<String, Integer>>>();
splitAllSkuAndPutMap(allSku, allSkus);
singleOrderInfo.put(orderCode, allSku);
warehouse.add(warehouseName);
}
multiOrderDetailInfo.add(singleOrderInfo);
warehouseOrderDetailInfo.put(warehouseName, multiOrderDetailInfo);
priorWarehouseOrderInfo = warehouseName;
}
br.close();
isr.close();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (isr != null) {
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} else {
System.out.println("file is not a file or file is not existing!");
}
return warehouseOrderDetailInfo;
}
/**
* 拿到读取到的一行数据,切分后put到Map中
*
* @param allSku* @param allSkus*/
public void splitAllSkuAndPutMap(Map<String, Integer> allSku, String allSkus) {
int len = allSkus.split(MULTISKUINFODELIMITED).length;
for (int i = 0; i < len; i++) {
String barCodeQuantity = allSkus.split(MULTISKUINFODELIMITED);
String barCode = barCodeQuantity.split(SINGLESKUINFODELIMITED)[0];
int quantity = Integer.parseInt(barCodeQuantity.split(SINGLESKUINFODELIMITED)[1]);
allSku.put(barCode, quantity);
}
}
/**
* 解析各仓库的所有订单
*
* @param warehouseOrderDetailInfo* @return map : 仓库名 --> List<List<Long>> 一个订单中的多个barcode在List<Long>中
*/
public Map<String, List<List<Long>>> aggWarehouseOrders(
Map<String, List<Map<String, Map<String, Integer>>>> warehouseOrderDetailInfo) {
Map<String, List<List<Long>>> warehouseOrderCodes = new HashMap<String, List<List<Long>>>();
List<List<Long>> barCodes = new ArrayList<List<Long>>();
List<Long> singleOrderBarCodes = new ArrayList<Long>();
// all warehouseName
Set<String> warehouse = new HashSet<String>();
// step2.1 拿到所有仓库名称
for (String warehouName : warehouseOrderDetailInfo.keySet()) {
barCodes = new ArrayList<List<Long>>();
// 仓库中的订单数
int warehouseOrderSize = warehouseOrderDetailInfo.get(warehouName).size();
// 逐个仓库遍历
for (int i = 0; i < warehouseOrderSize; i++) {
// barCodes = new ArrayList<List<Long>>();
Map<String, Map<String, Integer>> orderInfo = warehouseOrderDetailInfo.get(warehouName).get(i);
// 拿到指定仓库的所有订单号
for (Iterator<String> it = orderInfo.keySet().iterator(); it.hasNext(); ) {
String orderCode = it.next();
Map<String, Integer> barCodeQuantity = orderInfo.get(orderCode);
singleOrderBarCodes = new ArrayList<Long>();
// 拿到一个仓库中每个订单中所有barCode
for (String barCode : barCodeQuantity.keySet()) {
singleOrderBarCodes.add(Long.valueOf(barCode));
}
singleOrderBarCodes.sort(null);
barCodes.add(singleOrderBarCodes);
}
}
warehouseOrderCodes.put(warehouName, barCodes);
}
return warehouseOrderCodes;
}
/**
* 将给定的一组订单中的商品排列组合(全组合非全排列,abc 和 bca 相当于同一个组合)
*
* @param list* @return*/
public List<List<Long>> combinationOrders(List<Long> list) {
List<List<Long>> result = new ArrayList<List<Long>>();
// 可能出现的次数为2^n
long n = (long) Math.pow(2, list.size());
List<Long> combine;
for (long l = 0L; l < n; l++) {
combine = new ArrayList<Long>();
// 遍历给定数组
for (int i = 0; i < list.size(); i++) {
// 与运算后若是结果为1(同时1才是1),说明是一个新组合
if ((l >>> i & 1) == 1) {
combine.add(list.get(i));
}
}
result.add(combine);
}
return result;
}
/**
* 获取每个仓库的所有子组合
*
* @param warehouseCombinationOrders* @return*/
public Map<String, List<List<Long>>> getWarehouseAllChildCombination(
Map<String, List<List<List<Long>>>> warehouseCombinationOrders) {
// 开关:仓库遍历到最后一个的时候,停止创建新的对象,前面所有数据已经全部put到Map中
int num = 1;
// 仓库数量
int warehouseNum = warehouseCombinationOrders.size();
// 每个仓库中的所有订单的所有子组合
Map<String, List<List<Long>>> warehouseAllChildCombination = new HashMap<String, List<List<Long>>>();
// 一个订单中的所有子组合
List<List<Long>> childCombination = new ArrayList<List<Long>>(500);
// 遍历所有仓库
for (String warehouseName : warehouseCombinationOrders.keySet()) {
int ordersQuantity = warehouseCombinationOrders.get(warehouseName).size();
// 取出指定仓库所有订单的barCode组合
for (int i = 0; i < ordersQuantity; i++) {
int combinationQuantity = warehouseCombinationOrders.get(warehouseName).get(i).size();
// 拼接指定仓库指定订单的所有子组合
for (int j = 0; j < combinationQuantity; j++) {
if (warehouseCombinationOrders.get(warehouseName).get(i).get(j).size() > 0) {
childCombination.add(warehouseCombinationOrders.get(warehouseName).get(i).get(j));
}
}
}
warehouseAllChildCombination.put(warehouseName, childCombination);
childCombination = new ArrayList<List<Long>>(500);
// System.out.println(warehouseAllChildCombination);
num++;
if (num < warehouseNum - 1) {
warehouseAllChildCombination = new HashMap<String, List<List<Long>>>();
}
}
return warehouseAllChildCombination;
}
}
结果如下: 武汉仓:[69001] : 1 武汉仓:[69002] : 1 武汉仓:[69001, 69002] : 1 武汉仓:[69003] : 1 武汉仓:[69001, 69003] : 1 武汉仓:[69002, 69003] : 1 武汉仓:[69001, 69002, 69003] : 1 武汉仓:[69006] : 1 武汉仓:[69001, 69006] : 1 武汉仓:[69002, 69006] : 1 武汉仓:[69001, 69002, 69006] : 1 武汉仓:[69003, 69006] : 1 武汉仓:[69001, 69003, 69006] : 1 武汉仓:[69002, 69003, 69006] : 1 武汉仓:[69001, 69002, 69003, 69006] : 1 武汉仓:[69008] : 1 武汉仓:[69001, 69008] : 1 武汉仓:[69002, 69008] : 1 武汉仓:[69001, 69002, 69008] : 1 武汉仓:[69003, 69008] : 1 武汉仓:[69001, 69003, 69008] : 1 武汉仓:[69002, 69003, 69008] : 1 武汉仓:[69001, 69002, 69003, 69008] : 1 武汉仓:[69006, 69008] : 1 武汉仓:[69001, 69006, 69008] : 1 武汉仓:[69002, 69006, 69008] : 1 武汉仓:[69001, 69002, 69006, 69008] : 1 武汉仓:[69003, 69006, 69008] : 1 武汉仓:[69001, 69003, 69006, 69008] : 1 武汉仓:[69002, 69003, 69006, 69008] : 1 武汉仓:[69001, 69002, 69003, 69006, 69008] : 1 临江仓:[69001] : 3 临江仓:[69002] : 2 临江仓:[69001, 69002] : 1 临江仓:[69003] : 3 临江仓:[69001, 69003] : 2 临江仓:[69002, 69003] : 2 临江仓:[69001, 69002, 69003] : 1 临江仓:[69001] : 3 临江仓:[69002] : 2 临江仓:[69003] : 3 临江仓:[69002, 69003] : 2 临江仓:[69001] : 3 临江仓:[69003] : 3 临江仓:[69001, 69003] : 2 临江仓:[69011] : 1 临江仓:[69012] : 1 临江仓:[69011, 69012] : 1 郑州仓:[69003] : 3 郑州仓:[69006] : 3 郑州仓:[69003, 69006] : 3 郑州仓:[69003] : 3 郑州仓:[69006] : 3 郑州仓:[69003, 69006] : 3 郑州仓:[69003] : 3 郑州仓:[69006] : 3 郑州仓:[69003, 69006] : 3
同样一份数据可以用在不同的场合计算,类似的数据只要口径稍微调整就可以给运营给企划,给产品等各个不同部门人使用。
|