这个算法是来源于毕设、本来是打算采用遗传算法优化安踏一个最佳优惠策略的算法,但是后来因为商业合作的原因并没有用在安踏上。就将此算法用到了另外设计的一个生鲜上层的系统上。应用的场景是,通过商品的属性、用户行为等信息通过定时任务执行遗传算法来计算出一个商家利益最大化的每日推荐商品列表、站在每个用户角度角度的猜你喜欢列表(根据用户的行为特征不同)每个用户的列表都是不同的。
遗传算法顾名思义就是运用了生物上的达尔文进化论的思想,我们需要想明白的是达尔文进化轮中有群体、群体中有个体、个体中会进行交叉遗传、根据适者生存的法则能够存活下来的个体就能够进行下一代的遗传进化,代代相叠,最终剩下的群体就是我们想要的最优群体。上面的这句话可以看成是算法的一个核心,此算法的实现也是围绕着对上面各个概念的解释而展开的,我们需要明白在系统中下列的各个名词所代表的是什么:
1、个体:在每日推荐列表中个体就是若干个(取决于推荐列表中需要的商品个数)不同的商品组成的一组商品,这组商品我们可以称之为个体,每一个商品对应的二级制编码我们可以看做是个体的一个基因。
2、群体:若干个个体组成的就是我们的一个群体,初始的群体是随机生成的,后边每一代的群体都是进过前一代交叉遗传后进过自然选择得到的。
3、交叉遗传:生物中我们知道两个个体之间可以进行交叉变异,在这里的交叉变异就是指两个个体的基因(01字符串)进行交换,交换之后根据一定的概率再进行随机变异。如果交换或是变异之后的某段基因映射不到我们一个个体就需要进行一定的处理否则的话就会造成最后的基因序列翻译的得不到商品。
4、生存法则:自然界中有自然界的法则可以将好的个体留下来,这里的规则需要我们自己定义——在某一代中那一个体是最符合商家的最大利益的——那么商家的最大利益应该如何设计、如何反应出来,这是算法最核心的东西,也最能体现算法的好坏。在下面会有专门的小结来介绍算法的接评估维度。这里面就是根据定义好的维度来计算一组商品的权重,我们最终选择权重排名最大的一群个体,将这群个体单做下一代进化的基础。
算法是基于遗传算法的电商智能推荐算法,算法的运算结果为用户 小程序端的“猜你喜欢”、“今日推荐”两个模块的商品信息列表。整个算法的数据主要 来源于数据库设计—DBMS 实现模块中的三张相关表。智能算法的整体运作流程是:
(1)通过 PC 管理端设置智能算法的定时任务;
(2)从数据库读取数据并写入内存中,供智能算法使用;
(3)运算智能算法并输出结果,将结果保存在持久层;
(4)用户调用接口从持久层获取相应商品信息列表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xtumrZg2-1636383109373)(F:\typroa\aimages\image-20211108215438950.png)]
根据算法的实现以及数据库表的实现,设计了智能算法中单个商品信息的数据结构 —IntelligentDataSource,在算法中也可以将值称之为基因组,一个商品就可以称为一 个基因组,基因组是通过将 goodId 字段翻译成一个固定长度的 01 字符串,这个固定长度 的大小取决所有商品中 goodId 的最大值。
public class IntelligentDataSource { private int uid;//用户 id,此属性在分析猜你喜欢的时候方才启用 private int goodId;//商品 id private int synRelationValues;//商品点赞收藏综合评估值 private int synVisitValue;//商品浏览次数综合评估值 private double profit;//单商品利润 售价-成本价 private double disparityPrice;//单商品市场价钱售价差别 private double giveIntegral;//单商品购买所获得的积分 private int sales;//库存 private int stock;//库存 private boolean isGood;//是否为推荐商品 private boolean isNew;//是否为新品 private boolean isBenefit; //是否优惠了 } HashMap<String,IndividualValue> groups;
其中的 synRelationValues、synVisitValue 是结合点赞收藏、浏览的次数与触发这 些事件的时间经过一个自定义算法计算得到的,目的是希望将时间的因素考虑在内,通过 这两个参数的值来更大程度反映商品最近一段时间的受欢迎程度,这其中是借鉴了 JVM 中 垃圾回收算法中有关衰减均值的思想,算法的数学描述为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cYk9v2wO-1636383109377)(F:\typroa\aimages\image-20211108215733956.png)]
x 的取值为:(当前时间-触发点时间),在计算中时间的取值为 1970 年 1 月 1 日 (UTC/GMT 的午夜)开始到某个时间点的小时数。计算式中 W 的取值是一个重要的因子, 他的取值范围在[0.9,0.98]为宜,在算法中的默认值是 0.92。Z、P 的取值都是可调节的 在算法中取默认值 95、5。Z、W、P 的取值调节的是评价及浏览量的先后时间在计算商品 推荐值比重时的影响。
根据 goodId 生成基因组基因的代码实现为:
//生成物体具体的基因序列 public String geneOrderProduct(int index){ StringBuilder geneOrder = new StringBuilder(); while (index > 0){ geneOrder = geneOrder.append(index % 2); index /= 2; }; while (geneOrder.length()<geneLeng){ geneOrder.append(0); } return geneOrder.reverse().toString(); }
关于基因序列生成这一块的算法是存在很大的优化空间的,当初由于时间紧急就没有做过多的考虑,大家感兴趣的可以尝试进行优化。
算法中种群个体是由多个不重复的商品组成,个体的基因就是多个基因组的逐一拼接。 算法的实现逻辑是模拟生物遗传中自然选择的思想,来做优胜略汰,通过多轮的选择找到 一个局部最优解。首先我们构建一个用来模拟自然环境的函数,这个函数可以计算出个体 (商品列表)价值在这里我们将之为推荐值,在淘汰的过程中只留下群体中推荐值高的个 体,再让这些个体进行后代繁殖,逐代筛选直到得到一个相对最优解。后代繁殖的过程中 还存在交叉变异、算法中是通过采用“01”编码来模拟实现,将每一个体赋予一个由 01 组成的编号,编号模拟交叉变异,个体中的属性用来计算评估推荐值。
每日推荐算法的具体设计考虑三个维度:欢迎度 f (rela,vist) 、放映浏览量与实际销售 的实销度f(sale,visit)、库存量f(stocks)、还有一个商家用来设定的是否推荐、是否新 品等的自由维度,在描述中所用到的变量均来自于 IntelligentDataSource 实体,变量名 为相应属性名的简写:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-80HBsS4E-1636383109379)(F:\typroa\aimages\image-20211108220043839.png)]
将 x 的一组织收敛在[0,10],其中的 X 代表的是 x 所属的整个数组:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8BPOVqJt-1636383109382)(F:\typroa\aimages\image-20211108220124521.png)]
猜你喜欢的算法设计与自我推荐相似,在猜你喜欢的算法中权重最大的一个维度是个 人利益最大化,以及用户个人对一个商品可能的喜爱程度。本文对猜你喜欢部分的算法设 计不再做详细描述。
(1)系统全局变量,在本系统旨对于关键参数都设计为全局变量的形式从而提高代 码的可配置性。算法采用的遗传策略并不是固定遗传多少代而是设置经过多少代生物种群 的最大推荐值仍旧不变就自动结束算法,默认找到了相对最优解。
//基因单体产生的来源,false 随机产生用于测试、true 从数据库中读取商品信息产生 static final boolean sourceFlag = false; //设置群组中农基因体的数量、即商品的数量,用于测试环境生成基础数据 static int geneCount = 120; //设置单个基因序列长度 static final int geneLeng =Integer.toBinaryString(geneCount).length(); //设置种群数量 static final int individualCount = 36; //设置进化赛选比率 static final double evoluRate = 0.5; //群体推荐度相关平均值 public static double relasVisiAverage = 0; //群体库存平均值 public static double stockAverage = 0; //visi 尺度标记值 public static double visiMax = 0; public static double visiMin = Double.MAX_VALUE; //sale 尺度标记值 static double saleMax =0; static double saleMin =Double.MAX_VALUE; //几率积分的最大最小值 public static double giveMax = 0; public static double giveMin = Double.MIN_VALUE; //用户个数 static int userCount = 100; //设置种群突变几率,如果突变产生的基因不再基因库则随机在基因库中选取一个基因 作为突变基因 public double mutation = 0.01; //标记当前正在进化的是第几代 public int currentEvolutionIndex = 0; //设置系统日志 public static boolean ifShowInitData = false; public static boolean ifShowAnalysisProcess = false; public static boolean isIfShowResult = true; public static int recordGranularity = 500; //基于商品与个人不同维度的相关信息:用户 id、商品 id,[点赞/收藏/浏览量] static HashMap<Integer,HashMap<Integer,Integer[]>> userGoodsValues = new HashMap<Integer, HashMap<Integer, Integer[]>>(); //指定小数位长度 static DecimalFormat df2 =new java.text.DecimalFormat("#.00"); static DecimalFormat df3 =new java.text.DecimalFormat("#.000"); //用户生成随机数 static Random random = new Random(); //所有商品即所有基因点 public static HashMap<String, GoodsGene> genes = new HashMap<>();
(2)根据基因库信息产生种群个体
//根据基因库产生个体 public void productIndividual(){ //System.out.println("~~~~~~~~开始产生种群个体~~~~~~~~~"); String geneSequences = ""; for (int i = 0; i < individualCount; i++) { IndividualValue individualValue = new IndividualValue(); StringBuilder indiGeneString = new StringBuilder(); while (individualValue.getGeneSet().size()<indiviGeneCoutRec){ geneSequences = geneOrderProduct(random.nextInt(geneCount)); if (individualValue.getGeneSet().add(geneSequences)){ indiGeneString.append(geneSequences); individualValue.getIndividuals().put(geneSequences,genes.get(geneSequences)); } } individualValue.setGeneSequences(indiGeneString.toString()); individuals.put(individualValue.getGeneSequences(),individualValue); //System.out.println("第"+i+"个个体,种群个体的基因序列为:"+individualValue.getGeneSequences()); } }
(3)设置基因发生突变的位置
//基因突变 public String geneMutation(String geneOrder){ char[] geneTemp = geneOrder.toCharArray(); int geneIndex = random.nextInt(geneTemp.length); if (geneTemp[geneIndex]=='1'){ geneTemp[geneIndex] = '0'; }else{ geneTemp[geneIndex]= '1'; } String tempResult = new String(geneTemp); String geneSequences = ""; if(!genes.containsKey(tempResult)){ do { geneSequences = geneOrderProduct(random.nextInt(geneCount)); }while (!genes.containsKey(geneSequences)); return geneSequences; } //System.out.println("&*&:"+tempResult+" "+geneOrder+" "+geneTemp.length); return tempResult; }
(4)XY 染色体的交叉变异,在此产生的突变策略是随机突变,如果最终产生的突变基 因不再基因库中则视为无效基因,重新在基因库中随机选取一个基因作为此突变基因 的最终基因。
//X、Y染色体进行交叉变异,产生新个体 public IndividualValue[] geneCross(String[] crossGenes,int indiviGeneCout){ String[] manGeneArray = new String[indiviGeneCout]; String[] womanGeneArray = new String[indiviGeneCout]; String geneTemp = ""; //设置两个临时的个体 IndividualValue manIndividual = new IndividualValue(); IndividualValue womanIndividual = new IndividualValue(); //基因序列拼接中间值 StringBuilder manTemp = new StringBuilder(); StringBuilder womanTemp = new StringBuilder(); //最终返回值 IndividualValue[] result = new IndividualValue[2]; //将个体基因进行分割留作后续交叉变异使用 for (int i = 0; i <indiviGeneCout; i++) { manGeneArray[i] = crossGenes[0].substring(i*geneLeng,(i+1)*geneLeng); womanGeneArray[i] = crossGenes[1].substring(i*geneLeng,(i+1)*geneLeng); } //基因交叉及变异-父类 for (int i = 0; i < indiviGeneCout; i++) { do { if(random.nextBoolean()){ geneTemp = manGeneArray[i]; manGeneArray [i] = womanGeneArray[i]; womanGeneArray[i] = geneTemp; } if(random.nextDouble()<=mutation){ //满足条件对基因进行突变 if (random.nextBoolean()){ womanGeneArray[i] = geneMutation(womanGeneArray[i]); }else { manGeneArray[i] = geneMutation(manGeneArray[i]); } } }while (womanIndividual.getGeneSet().contains(womanGeneArray[i])|| manIndividual.getGeneSet().contains(manGeneArray[i])); //根据变异出来的基因产生新的个体 womanIndividual.getGeneSet().add(womanGeneArray[i]); manIndividual.getGeneSet().add(manGeneArray[i]); womanIndividual.getIndividuals().put(womanGeneArray[i],genes.get(womanGeneArray[i])); manIndividual.getIndividuals().put(manGeneArray[i],genes.get(manGeneArray[i])); }; //产生新个体的最终基因序列 for (int i = 0; i < manGeneArray.length; i++) { womanTemp.append(womanGeneArray[i]); manTemp.append(manGeneArray[i]); } womanIndividual.setGeneSequences(womanTemp.toString()); manIndividual.setGeneSequences(manTemp.toString()); result[0] = manIndividual;result[1] = womanIndividual; return result; }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4i2n6bnq-1636383109384)(F:\typroa\aimages\image-20211108221610690.png)]
另个遗传算法模块需要用到的公共部分代码,这里进行一定程度的抽象,来实现代码的一个共用与封装。本以上也是想要引入模板方法的设计模式,但是做的并不成功。
@Slf4j @AllArgsConstructor public abstract class GenticAlgorithmAbstract { static final boolean sourceFlag = false; //设置群组中农基因体的数量、即商品的数量, static int geneCount = 120; //设置单个基因序列长度 static final int geneLeng =Integer.toBinaryString(geneCount).length(); //设置种群数量 static final int individualCount = 36; //设置进化赛选比率 static final double evoluRate = 0.5; //群体推荐度相关平均值 public static double relasVisiAverage = 0; //群体库存平均值 public static double stockAverage = 0; //visi尺度标记值 public static double visiMax = 0; public static double visiMin = Double.MAX_VALUE; //sale尺度标记值 static double saleMax =0; static double saleMin =Double.MAX_VALUE; //几率积分的最大最小值 public static double giveMax = 0; public static double giveMin = Double.MIN_VALUE; //用户个数 static int userCount = 100; //设置种群突变几率,如果突变产生的基因不再基因库则随机在基因库中选取一个基因作为突变基因 public static double mutation = 0.01; //标记当前正在进化的是第几代 public static int currentEvolutionIndex = 0; //设置系统日志 public static boolean ifShowInitData = false; public static boolean ifShowAnalysisProcess = true; public static boolean isIfShowResult = true; public static int recordGranularity = 100; //基于商品与个人不同维度的相关信息:用户id、商品id,[点赞/收藏/浏览量] static HashMap<Integer,HashMap<Integer,Integer[]>> userGoodsValues = new HashMap<Integer, HashMap<Integer, Integer[]>>(); //指定小数位长度 static DecimalFormat df2 =new java.text.DecimalFormat("#.00"); static DecimalFormat df3 =new java.text.DecimalFormat("#.000"); //用户生成随机数 static Random random = new Random(); //所有商品即所有基因点 public static HashMap<String, GoodsGene> genes = new HashMap<>(); //商品列表 public static HashMap<String, YxStoreProduct> allGoodLists = new HashMap<>(); public static YxStoreProductService yxStoreProductService; public static YxStoreProductRelationService yxStoreProductRelationService; public static RedisUtils redisUtils; //生成种群基因库,基因库的数据来源可以是随机产生的也可以是从数据库读取的 public static void productGene(){ if(sourceFlag){ //从数据库读取产生 databaseProduct();return; } //随机产生,用于测试 randomProduct(); if (ifShowInitData){ log.info("智能分析算法基因为:"); showGenes(); log.info("商品用户行为数据为:"); showUserGoodsMes(); } //计算基因库特征值 log.info("计算基因库特征值"); gensAverage(); } //产生种群个体 public void productIndividual(HashMap<String, IndividualValue> individuals,int indiviGeneCout) { String geneSequences = ""; for (int i = 0; i < individualCount; i++) { IndividualValue individualValue = new IndividualValue(); StringBuilder indiGeneString = new StringBuilder(); while (individualValue.getGeneSet().size()<indiviGeneCout){ geneSequences = geneOrderProduct(random.nextInt(geneCount)); if (individualValue.getGeneSet().add(geneSequences)){ indiGeneString.append(geneSequences); individualValue.getIndividuals().put(geneSequences,genes.get(geneSequences)); } } individualValue.setGeneSequences(indiGeneString.toString()); individuals.put(individualValue.getGeneSequences(),individualValue); if(ifShowInitData)showIndividual(i,individualValue); } } public static void randomProduct(){ for (int i = 0; i < geneCount; i++) { GoodsGene goodsGene = new GoodsGene(); goodsGene.setGene(geneOrderProduct(i)); goodsGene.setGoodId(i+1); goodsGene.setSynRelationValues(Double.parseDouble(df2.format(99*random.nextDouble()+1))); goodsGene.setSynVisitValue(Double.parseDouble(df2.format(99*random.nextDouble()+1))); goodsGene.setProfit(Double.parseDouble(df2.format(19*random.nextDouble()+1))); goodsGene.setDisparityPrice(Double.parseDouble(df2.format(9*random.nextDouble()+1))); goodsGene.setGiveIntegral(Double.parseDouble(df2.format(4*random.nextDouble()+1))); goodsGene.setSales(random.nextInt(999)+1); goodsGene.setStock(random.nextInt(199)+1); goodsGene.setGood(random.nextBoolean()); goodsGene.setNew(random.nextBoolean()); goodsGene.setBenefit(random.nextBoolean()); genes.put(goodsGene.getGene(), goodsGene); } log.info("开始生成商品用户行为数据"); randomUserGoods(); } //自由生成用户商品行为数据 public static void randomUserGoods(){ int goodsCount = 0; for (int i =1; i <=userCount; i++) { //产生客户信息 HashMap<Integer,Integer[]> goodsMesTemp = new HashMap<>(); int goodId = 0 ; goodsCount = random.nextInt(10)+1; for(int j = 1; j <=goodsCount; j++){ //产生商品及各维度点赞信息 Integer [] values = new Integer[3]; values[0] = random.nextInt(2); values[1] = random.nextInt(2); values[2] = random.nextInt(50); goodId = random.nextInt(geneCount)+1; if (!goodsMesTemp.containsKey(goodId)){ goodsMesTemp.put(goodId,values); } } userGoodsValues.put(i,goodsMesTemp); } } public static void databaseProduct(){ //查询出所有的商品序列 YxStoreProductQueryCriteria criteria = new YxStoreProductQueryCriteria(); List<YxStoreProduct> yxStoreProductList = yxStoreProductService.queryAll(criteria); log.info("智能算法查出的用户总数量为:"+yxStoreProductList.size()); YxStoreProductRelationQueryCriteria criteria1 = new YxStoreProductRelationQueryCriteria(); List<YxStoreProductRelation> relations = yxStoreProductRelationService.queryAll(criteria1); log.info("用户商品行为信息的数据量为:"+relations.size()); HashMap<Integer,YxStoreProduct> goodsMap = new HashMap<>(); if(yxStoreProductList!=null&&yxStoreProductList.size() >0){ for (YxStoreProduct item:yxStoreProductList){ GoodsGene goodsGene = new GoodsGene(); goodsGene.setGene(geneOrderProduct(item.getId())); goodsGene.setGoodId(item.getId()); goodsGene.setSynRelationValues(0); goodsGene.setSynVisitValue(0); goodsGene.setProfit(item.getPrice().doubleValue()-item.getCost().doubleValue()); goodsGene.setDisparityPrice(item.getOtPrice().doubleValue()-item.getPrice().doubleValue()); goodsGene.setGiveIntegral(item.getGiveIntegral().doubleValue()); goodsGene.setSales(item.getSales()); goodsGene.setStock(item.getStock()); goodsGene.setGood(item.getIsGood()==1?true:false); goodsGene.setNew(item.getIsNew()==1?true:false); goodsGene.setBenefit(item.getIsBenefit()==1?true:false); genes.put(goodsGene.getGene(),goodsGene); goodsMap.put(item.getId(),item); } redisUtils.set("goodsMap",goodsMap); goodsMap = null;//进行内存释放 } if(relations!=null&&relations.size()>0){ for (YxStoreProductRelation item:relations){ } } } //根据十进制编号生成二进制基因序列 public static String geneOrderProduct(int index){ StringBuilder geneOrder = new StringBuilder(); while (index > 0){ geneOrder = geneOrder.append(index % 2); index /= 2; }; while (geneOrder.length()<geneLeng){ geneOrder.append(0); } return geneOrder.reverse().toString(); } //计算基因库中所有商品基因的受欢迎维度的平均值,为后续的个体计算做准备 public static void gensAverage(){ for(String key:genes.keySet()){ GoodsGene temp = genes.get(key); relasVisiAverage = relasVisiAverage+temp.getSynVisitValue()+temp.getSynRelationValues(); stockAverage = stockAverage+ temp.getStock(); if(temp.getSynVisitValue()>visiMax){ visiMax = temp.getSynVisitValue(); } if(temp.getSynVisitValue()<visiMin){ visiMin = temp.getSynVisitValue(); } if(temp.getSales()>saleMax){ saleMax = temp.getSales(); } if(temp.getSales()<saleMin){ saleMin = temp.getSales(); } if(temp.getGiveIntegral()>giveMax){ giveMax = temp.getGiveIntegral(); } if(temp.getGiveIntegral()<giveMin){ giveMin = temp.getGiveIntegral(); } } relasVisiAverage = Double.parseDouble(df2.format(relasVisiAverage/(2*genes.size()))); stockAverage = Double.parseDouble(df2.format(stockAverage/genes.size())); //System.out.println("基因库群体特征值——relasVisiAverage:"+relasVisiAverage+" stockAverage:"+stockAverage); //System.out.println("visiMax:"+visiMax+" visiMin"+visiMin+" saleMax"+saleMax+" saleMin"+saleMin); } //计算群体推荐值 public void individualsRecValue(HashMap<String, IndividualValue> individuals){ //System.out.println("~~~~~~开始种群推荐值计算~~~~~~~~~~"); IndividualValue individualValue = null; for (String key:individuals.keySet()){ individualValue = individuals.get(key); individualValue.setRecommendValue(Double.parseDouble(df3.format(recValue(individualValue)))); //System.out.println("基因序列为:"+individualValue.getGeneSequences()+"的推荐值为:"+individualValue.getRecommendValue()); } } //产生下一代,在产生下一代的过程中还要保证基因序列的不重复性-父类 public HashMap<String, IndividualValue> productNextGroup(ArrayList<IndividualValue> betterIndividuals,int indiviGeneCout){ //System.out.println("%%%%开始进行种群进化:"); int betterIndivLength = betterIndividuals.size()-1; HashMap<String, IndividualValue> currentIndividuals = new HashMap<>(); while (currentIndividuals.size()<individualCount){ //从优秀个体中获得两个个体进行杂交 String[] crossGenes = new String[2]; crossGenes[0] = betterIndividuals.get(random.nextInt(betterIndivLength)).getGeneSequences(); do { crossGenes[1] = betterIndividuals.get(random.nextInt(betterIndivLength)).getGeneSequences(); }while (crossGenes[0].equals(crossGenes[1])); IndividualValue[] individualValues = geneCross(crossGenes,indiviGeneCout); currentIndividuals.put(individualValues[0].getGeneSequences(),individualValues[0]); currentIndividuals.put(individualValues[1].getGeneSequences(),individualValues[1]); } //System.out.println("+++++++新一代的种群个体为++++++++++++++"); //if (ifShowInitData){ // for (String key:currentIndividuals.keySet()){ // System.out.println(currentIndividuals.get(key).toString()); // } //} return currentIndividuals; } //X、Y染色体进行交叉变异,产生新个体 public IndividualValue[] geneCross(String[] crossGenes,int indiviGeneCout){ String[] manGeneArray = new String[indiviGeneCout]; String[] womanGeneArray = new String[indiviGeneCout]; String geneTemp = ""; //设置两个临时的个体 IndividualValue manIndividual = new IndividualValue(); IndividualValue womanIndividual = new IndividualValue(); //基因序列拼接中间值 StringBuilder manTemp = new StringBuilder(); StringBuilder womanTemp = new StringBuilder(); //最终返回值 IndividualValue[] result = new IndividualValue[2]; //将个体基因进行分割留作后续交叉变异使用 for (int i = 0; i <indiviGeneCout; i++) { manGeneArray[i] = crossGenes[0].substring(i*geneLeng,(i+1)*geneLeng); womanGeneArray[i] = crossGenes[1].substring(i*geneLeng,(i+1)*geneLeng); } //基因交叉及变异-父类 for (int i = 0; i < indiviGeneCout; i++) { do { if(random.nextBoolean()){ geneTemp = manGeneArray[i]; manGeneArray [i] = womanGeneArray[i]; womanGeneArray[i] = geneTemp; } if(random.nextDouble()<=mutation){ //满足条件对基因进行突变 if (random.nextBoolean()){ womanGeneArray[i] = geneMutation(womanGeneArray[i]); }else { manGeneArray[i] = geneMutation(manGeneArray[i]); } } }while (womanIndividual.getGeneSet().contains(womanGeneArray[i])|| manIndividual.getGeneSet().contains(manGeneArray[i])); //根据变异出来的基因产生新的个体 womanIndividual.getGeneSet().add(womanGeneArray[i]); manIndividual.getGeneSet().add(manGeneArray[i]); womanIndividual.getIndividuals().put(womanGeneArray[i],genes.get(womanGeneArray[i])); manIndividual.getIndividuals().put(manGeneArray[i],genes.get(manGeneArray[i])); }; //产生新个体的最终基因序列 for (int i = 0; i < manGeneArray.length; i++) { womanTemp.append(womanGeneArray[i]); manTemp.append(manGeneArray[i]); } womanIndividual.setGeneSequences(womanTemp.toString()); manIndividual.setGeneSequences(manTemp.toString()); result[0] = manIndividual;result[1] = womanIndividual; return result; } //基因突变,接受正常基因返回突变后的基因 public String geneMutation(String geneOrder){ char[] geneTemp = geneOrder.toCharArray(); int geneIndex = random.nextInt(geneTemp.length); geneTemp[geneIndex]=geneTemp[geneIndex]=='1'?'0':'1'; String tempResult = new String(geneTemp); String geneSequences = ""; if(!genes.containsKey(tempResult)){ do { geneSequences = geneOrderProduct(random.nextInt(geneCount)); }while (!genes.containsKey(geneSequences)); return geneSequences; }return tempResult; } //用来展示用户商品的行为数据 public static void showUserGoodsMes(){ for (Integer key:userGoodsValues.keySet()){ System.out.println("User-"+key+"的商品行为数据如下:"); for (Integer item:userGoodsValues.get(key).keySet()){ Integer[] temp = userGoodsValues.get(key).get(item); System.out.println("goodId:"+item+";点赞:"+temp[0]+";收藏:"+temp[1]+";浏览量:"+temp[2]); temp = null;//释放内存 } } } //展示系统初始化基因 public static void showGenes(){ for (String key: genes.keySet()){ System.out.println(genes.get(key).toString()); } } //展示种群个体详细信息 public static void showIndividual(int index,IndividualValue individualValue){ System.out.println("第 "+index+" 个体,种群个体的基因序列为:"+individualValue.getGeneSet()); } //计算种群推荐值 public abstract double recValue(IndividualValue individualValue); //种群赛选 public abstract boolean filtrate(ArrayList<IndividualValue> betterIndividuals, ArrayList<EvolutionRecord> evolutionRecords,HashMap<String, IndividualValue> individuals); }
@Slf4j public class GeneticAlorithmGueYouLick extends GenticAlgorithmAbstract { //设置个体中的基因数,即商品推荐列表的商品数 private int indiviGeneCout = 10; //设置计算隔代种群进化推荐特征值 public ArrayList<EvolutionRecord> evolutionRecords = new ArrayList<>(); //生物原始种群 private HashMap<String, IndividualValue> individuals = new HashMap<>(); //用户相关信息是否来源于数据库 private final boolean sourceUserFlag = false; //单用户浏览量特征值 public int userVisitMax; public int userVisitMin; //设置isNew、isBenefit的相应值 public final double isNewUnit = 5/indiviGeneCout; public final double isBenefitUnit = 20/indiviGeneCout; //当前正在分析的用户id public Integer currentUserId = 0; //智能进化相关参数 public final int intergenerationCount =500; public double beforeMax = 0; public int beforeMaxIntergeneration = 0; //用户信息推荐 private HashMap<String, HashSet<String>> youLikeGoods = new HashMap<>(); @Test public void run(){ // //个体推荐值计算 log.info("开始生产猜你喜欢种群个体~"); productIndividual(individuals,indiviGeneCout); evolution(); } public void showResult(){ log.info("猜你喜欢模块分析的结果为:"); for (String key:youLikeGoods.keySet()){ System.out.println("用户:"+key+";推荐商品组合为:"+youLikeGoods.get(key)); } } //用户群体,种群进化 public void evolution(){ //读取出用户相关的信息 //计算用户当前浏览量的最大最小值 for (Integer key:userGoodsValues.keySet()){ currentUserId = key; //产生单个用户的原始群体,这两部操作个人认为都是可操作性可不操操作的,如果不要这两部只需要突变概率放大即可 //productIndividual(); //gensAverage(); //计算单用户的浏览量特征值 userVisitMax = 0;userVisitMin = Integer.MAX_VALUE; for (Integer item:userGoodsValues.get(key).keySet()){ if (userGoodsValues.get(key).get(item)[2]<userVisitMin){ userVisitMin = userGoodsValues.get(key).get(item)[2]; } if (userGoodsValues.get(key).get(item)[2]>userVisitMax){ userVisitMax = userGoodsValues.get(key).get(item)[2]; } } //针对单个用户进行生物种群进化 if(ifShowAnalysisProcess){ System.out.println("现在正在用户编号为:"+key+"用户进行分析"); } boolean envolutionFlag = true; //对全局表计量进行置零复位 currentEvolutionIndex = 0; beforeMaxIntergeneration = 0; beforeMax = 0; do { ArrayList<IndividualValue> betterIndividuals = new ArrayList<>(); ++currentEvolutionIndex; //计算群体推荐值赛选优秀个体 individualsRecValue(individuals); envolutionFlag = filtrate(betterIndividuals,evolutionRecords,individuals); //利用优秀个体进行种群进化,铲射鞥下一代个体 individuals = productNextGroup(betterIndividuals,indiviGeneCout); betterIndividuals = null; }while (envolutionFlag); } } @Override public double recValue(IndividualValue individualValue) { double behaviorValue = 0,isNewValue = 0,giveInteValue = 0,isBenefitValue =0; for (String key:individualValue.getGeneSet()){ GoodsGene curDataSource = genes.get(key); if (curDataSource==null){ System.out.println("*****报错为空,基因序列非法"); } isNewValue = isNewValue +(curDataSource.isNew()==true?isBenefitUnit:0); giveInteValue = giveInteValue + 4*(curDataSource.getGiveIntegral()-giveMin)/(giveMax-giveMin)+1; isBenefitValue = isBenefitValue + (curDataSource.isBenefit()==true?isBenefitUnit:0); behaviorValue = behaviorValue + behaviorValueCal(curDataSource.getGoodId()); } giveInteValue = giveInteValue/indiviGeneCout; behaviorValue = behaviorValue/indiviGeneCout; //System.out.println("behaviorValue:"+behaviorValue+" isNewValue:"+isNewValue+" giveInteValue:"+giveInteValue+" isBenefitValue:"+isBenefitValue); return behaviorValue+isNewValue+giveInteValue+isBenefitValue; } @Override public boolean filtrate(ArrayList<IndividualValue> betterIndividuals, ArrayList<EvolutionRecord> evolutionRecords,HashMap<String, IndividualValue> individuals){ double averageTemp =0.0; int betterCount = (int) (individualCount*evoluRate); List<IndividualValue> temp = new ArrayList<>(); for (String key:individuals.keySet()){ temp.add(individuals.get(key)); } Collections.sort(temp); for (int i = 0; i < betterCount; i++) { //System.out.println(temp.get(i).getRecommendValue()); betterIndividuals.add(temp.get(i)); averageTemp =averageTemp+temp.get(i).getRecommendValue() ; } //System.out.println("在本次进化中,最高推荐值:"+temp.get(0).getRecommendValue()+";最低推荐值:"+temp.get(temp.size()-1).getRecommendValue()+";" + //"优秀个体平均推荐值:"+Double.parseDouble(df3.format(averageTemp/betterCount))); //记录进化过程 if ((currentEvolutionIndex%recordGranularity==0||currentEvolutionIndex==1)&&ifShowAnalysisProcess){ EvolutionRecord evolutionRecord = new EvolutionRecord(currentEvolutionIndex); evolutionRecord.setArts(Double.parseDouble(df3.format(averageTemp/betterCount)), temp.get(0).getRecommendValue(),temp.get(temp.size()-1).getRecommendValue()); System.out.println(evolutionRecord.toString()); evolutionRecord = null; } //智能判断 if (temp.get(0).getRecommendValue()>beforeMax){ beforeMax = temp.get(0).getRecommendValue(); beforeMaxIntergeneration = currentEvolutionIndex; } if (currentEvolutionIndex-beforeMaxIntergeneration>intergenerationCount){ youLikeGoods.put(currentUserId+"",temp.get(0).getGeneSet()); if(ifShowAnalysisProcess){ System.out.println("在第"+currentEvolutionIndex+"中找到了用户"+currentUserId+"的推荐最大值:"+temp.get(0)); } return false; } return true; //evolutionRecords.add(evolutionRecord); } //计算用户行为纬度值 public double behaviorValueCal(Integer goodsId){ //这里需要传递进去的值是商品的id double visitValue = 0; Integer[] temp = userGoodsValues.get(currentUserId).get(goodsId); double visitRuler = 0; if (temp == null) return 0; if((userVisitMin - userVisitMax)==0)return 40; if (visitRuler>=9){ visitValue = 40; }else if (visitRuler>=6){ visitValue = 30; }else if (visitRuler>=4){ visitValue = 20; }else if (visitRuler>=2){ visitValue = 10; }else if (visitRuler>0){ visitValue = 5; }else{ visitValue =0; } return visitValue+(temp[0]==1?10:0)+(temp[1]==1?20:0); } }
@Slf4j public class GeneticAlorithmRecommend extends GenticAlgorithmAbstract { //设置个体中的基因数,即商品推荐列表的商品数 private int indiviGeneCout = 5; //profit尺度标记值 public double synProfitMax = 0; public double synProfitMin = Double.MAX_VALUE; //设置种群进化代数 public int evolutionCount = 1000000; //智能进化相关参数 public final int intergenerationCount =2000; public double beforeMax = 0; public int beforeMaxIntergeneration = 0; //计算隔代种群进化推荐特征值 public ArrayList<EvolutionRecord> evolutionRecords = new ArrayList<>(); //生物种群 private HashMap<String, IndividualValue> individuals = new HashMap<>(); //最强个体 private IndividualValue individualValueBest = new IndividualValue(); @Test public void run(){ //产生个体 log.info("开始生产猜你喜欢种群个体~"); productIndividual(individuals,indiviGeneCout); //个体推荐值计算 evolution(); } public void showResult(){ log.info("系统推荐算法分析的结果为:"); System.out.println(individualValueBest.toString()); } //种群进化 public void evolution(){ //System.out.println("@@@开始进行生物种群进化:"); boolean envolutionFlag = true; currentEvolutionIndex = 0; do{ ArrayList<IndividualValue> betterIndividuals = new ArrayList<>(); ++currentEvolutionIndex; //计算利润中间值 calProfitTemp(); //计算群体推荐值赛选优秀个体 individualsRecValue(individuals); envolutionFlag = filtrate(betterIndividuals,evolutionRecords,individuals); //利用优秀个体进行种群进化,铲射鞥下一代个体 individuals = productNextGroup(betterIndividuals,indiviGeneCout); betterIndividuals = null; }while (envolutionFlag); } public void calProfitTemp(){ double visiTemp = 0; double saleTemp = 0; GoodsGene temp = null; for (String key:genes.keySet()){ temp = genes.get(key); visiTemp = 9*(temp.getSynVisitValue()-visiMin)/(visiMax-visiMin)+1; saleTemp = 9*(temp.getSales()-saleMin)/(saleMax-saleMin)+1; //System.out.println("visiTemp:"+visiTemp+" saleTemp:"+saleTemp+" temp.getProfit():"+temp.getProfit()); temp.setSynProfitTemp((saleTemp/visiTemp)*(saleTemp+visiTemp)*temp.getProfit()); //System.out.println("$SynProfitTemp:"+temp.getSynProfitTemp()); if(temp.getSynProfitTemp()>synProfitMax){ synProfitMax = temp.getSynProfitTemp(); // System.out.println("进行了更新synProfitMax:"+temp.getSynProfitTemp()); } if(temp.getSynProfitTemp()<synProfitMin){ synProfitMin = temp.getSynProfitTemp(); //System.out.println("进行了更新synProfitMin:"+temp.getSynProfitTemp()); } } synProfitMin = Double.parseDouble(df2.format(synProfitMin)); synProfitMax = Double.parseDouble(df2.format(synProfitMax)); //System.out.println("synProfitMax:"+synProfitMax+" synProfitMin:"+synProfitMin); } @Override public double recValue(IndividualValue individualValue){ double relaVisi = 0,stock = 0,prof = 0,free = 0; double profis[] = new double[indiviGeneCout]; for (String key:individualValue.getIndividuals().keySet()){ GoodsGene curDataSource = individualValue.getIndividuals().get(key); relaVisi =relaVisi+curDataSource.getSynRelationValues()+curDataSource.getSynVisitValue(); free = free + (curDataSource.isGood()==true?6:0)+(curDataSource.isNew()==true?2:0); stock = stock+calStock(curDataSource.getStock()); prof = prof + 39*(curDataSource.getSynProfitTemp()-synProfitMin)/(synProfitMax-synProfitMin)+1; curDataSource = null; } relaVisi = 0.65*(relaVisi/indiviGeneCout)-relasVisiAverage; stock = stock/indiviGeneCout; prof = prof/indiviGeneCout; // System.out.println("@@@relaVisi:"+relaVisi+"stock"+stock+"prof"+prof); return relaVisi+stock+prof+free; } @Override public boolean filtrate(ArrayList<IndividualValue> betterIndividuals, ArrayList<EvolutionRecord> evolutionRecords,HashMap<String, IndividualValue> individuals){ double averageTemp =0.0; int betterCount = (int) (individualCount*evoluRate); List<IndividualValue> temp = new ArrayList<>(); for (String key:individuals.keySet()){ temp.add(individuals.get(key)); } Collections.sort(temp); for (int i = 0; i < betterCount; i++) { //System.out.println(temp.get(i).getRecommendValue()); betterIndividuals.add(temp.get(i)); averageTemp =averageTemp+temp.get(i).getRecommendValue() ; } //记录进化过程 if ((currentEvolutionIndex%recordGranularity==0||currentEvolutionIndex==1)&&ifShowAnalysisProcess){ EvolutionRecord evolutionRecord = new EvolutionRecord(currentEvolutionIndex); evolutionRecord.setArts(Double.parseDouble(df3.format(averageTemp/betterCount)), temp.get(0).getRecommendValue(),temp.get(temp.size()-1).getRecommendValue()); System.out.println(evolutionRecord.toString()); evolutionRecord = null; } //evolutionRecords.add(evolutionRecord); //智能判断 if (temp.get(0).getRecommendValue()>beforeMax){ beforeMax = temp.get(0).getRecommendValue(); beforeMaxIntergeneration = currentEvolutionIndex; } if (currentEvolutionIndex-beforeMaxIntergeneration>intergenerationCount){ individualValueBest = temp.get(0); if (ifShowAnalysisProcess){ System.out.println("在第"+currentEvolutionIndex+"中找到推荐最大值:"+temp.get(0)); } return false; } return true; } private double calStock(double stock){ double flag = stock/stockAverage; double stockValue = 0; if(flag>2){ return 30; }else if(flag>1.5){ return 20; }else if(flag>1){ return 10; }else{ return 0; } } }
优化项 | 原因与策略 | 是否已优化 |
---|---|---|
程序执行在执行的过程中会越来越慢甚至到最后的时候还会出现卡死或是内存溢出的情况 | 在迭代的过程中产生了大量的对象,虽然JVM有垃圾自动回收的能力但是仍然会存在回收不及时或是有些对象存在强引用,所以造成内存浪费。需要做的是及时的将对象置null。进行内存释放 | 90% |
配置将较多,后续配置耦合性太高 | 现在是初步提取做来作为了常量,实际上可进一步进行抽取为配置文件——>配置文件搭配配置中心使用或是将配置项持久化到数据库在通过图形界面进行展示动态的配置 | 20% |
代码结构总体较为凌乱,需要进一步进行抽象 | 可以参照模板方法的设计模式进行分装与公共部分代码共用 | 65% |
得带进化的过程比较复杂 | 借用函数式编程的流处理的思想与方法处理进化迭代部分的代码 | 0% |
算法完整之后代码量应该在1000行左右,不同情况下的日志项难以控制 | 结合配置项与日志处理函数进行不同粒度日志的打印,不同环境下不同内容的日志打印 | 30% |
定时任务执行时,目前的代码运行不起来 | 初步排查是因为static、abstract等机加载时机先后顺序引起的问题;此部分的解决需要对算法的代码结构进行重构,并且处理好两种模式——自动生成、数据库读取的切换 | 0% |
目前想到的就只有这些,感觉还是挺有意思的,后边也会抽出时间将这个算法的代码进行一次彻底的重构。