集束算法的理解上相对来说还是比较简单的,该算法不是求解最优解而是尽可能的靠近最优解的算法。当集束层级达到12层以上每个子节点不超过5个时,节点数最高可达到了30W个节点左右,而需要从此得出最优解可能用穷尽法来算尽节点数也未尝是“最优”了。
这边推荐比较好去理解此算法的两篇博客。
下面具体用代码来说明吧!看集束算法上有分别使用图和树的模型来演示算法规则,此处只实现了树形模型,图形的大同小异就不实现了。
第一步,定义集束实体BEAM
import java.io.Serializable;
/**
* 集束算法的实体Bean,每个元素的定义
* 集束bean中包含了 名称 概率值 哪个层级 该层级对应的集合号
*
*/
public class Beam implements Serializable {
/*
* 集束名称
*/
private String beamName;
/*
* 集束值
*/
private Double beamValue;
/*
* 集束层级
*/
private Integer beamLevel;
/*
* 集束同一层级对应的那个集合号 1 2 3 4
*/
private Integer beamNo;
/*
* 当前集束bean的父集束bean
*/
private Beam parentBeam;
public Beam() {
super();
}
public Beam(String beamName, Double beamValue, Integer beamLevel, Integer beamNo) {
super();
this.beamName = beamName;
this.beamValue = beamValue;
this.beamLevel = beamLevel;
this.beamNo = beamNo;
}
public String getBeamName() {
return beamName;
}
public void setBeamName(String beamName) {
this.beamName = beamName;
}
public Double getBeamValue() {
return beamValue;
}
public void setBeamValue(Double beamValue) {
this.beamValue = beamValue;
}
public Integer getBeamLevel() {
return beamLevel;
}
public void setBeamLevel(Integer beamLevel) {
this.beamLevel = beamLevel;
}
public Integer getBeamNo() {
return beamNo;
}
public void setBeamNo(Integer beamNo) {
this.beamNo = beamNo;
}
public Beam getParentBeam() {
return parentBeam;
}
public void setParentBeam(Beam parentBeam) {
this.parentBeam = parentBeam;
}
}
第二步,定义父子集束关系
import java.util.List;
public class TreeBeam {
/*
* 父层-集束号 规则 1-1第一层的第一个集束集合号
*/
private String parentBeamNo;
/*
* 当前层-集束集合号 规则 1-1第一层的第一个集束集合号
*/
private String nowBeamNo;
/*
* 集束beam的集合
*/
private List<Beam> beamList;
public TreeBeam(String parentBeamNo, String nowBeamNo, List<Beam> beamList) {
super();
this.parentBeamNo = parentBeamNo;
this.nowBeamNo = nowBeamNo;
this.beamList = beamList;
}
public String getParentBeamNo() {
return parentBeamNo;
}
public void setParentBeamNo(String parentBeamNo) {
this.parentBeamNo = parentBeamNo;
}
public String getNowBeamNo() {
return nowBeamNo;
}
public void setNowBeamNo(String nowBeamNo) {
this.nowBeamNo = nowBeamNo;
}
public List<Beam> getBeamList() {
return beamList;
}
public void setBeamList(List<Beam> beamList) {
this.beamList = beamList;
}
}
第三步,初始化集束集合,该集束为树形结构
package com.george.easylearn.algorithm.beam;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* 集束工厂的作用:
* 1.定义好每个元素的信息,包括元素的概率值
* 2.定义每个元素的层级关系
* 3.定义每个元素可衍生出元素个数(随机或者固定)
* 4.定义每个层级中元素集中包含beam的个数
* 5.将上述定义好的元素存储到Map集合中 key 层级 value 为List<TreeBeam>
*
*/
public class TreeBeamMap implements BeamMap{
// 定义该集束的层级
private final Integer levelCount = 6;
// 集束的值范围 1-100之间
private final Integer beamValueRange = 100;
// 每次衍生出元素是否随机或者固定个数
private boolean randFlag = true;
// 每次衍生出元素的随机个数为1-5个
private Integer randCount = 5;
// 每次衍生出元素个数 默认固定2个也可根据随机来获取
private Integer beamCount = 3;
// 每个集束集中包含beam的个数
private final Integer beamListCount = 6;
// 定义每个元素的名称
private char beamName = 'a';
// 存储个元素层级的beam元素 key-Integer层数 key-String层数+集合号 规则为1-1 第一层的一个集合 0-表示其实层无父层级
private Map<Integer, List<TreeBeam>> beamListMap = new HashMap<>();
public TreeBeamMap(){
// 初始化第一层的集束数据集
initBeamListMap();
// 从第二层开始推算后面的元素
for (int i = 1; i < levelCount; i++) {
// 获取上一层的集束集合号个数
long lastLevelCount = beamListMap.get(i).stream().filter(distinctByKey(TreeBeam::getNowBeamNo)).count();
// 上一层
int lastBeamLevel = i;
// 当前层
int nowBeamLevel = i + 1;
int nowBeanListNo = 0;
for (int j = 0; j < lastLevelCount; j++) {
// 集合编号
int lastBeanListNo = j + 1;
// 定义该层元素集合个数
if (randFlag){
Random random = new Random();
beamCount = random.nextInt(randCount)+1;
}
// 集合当前层 集束Map集合
List<TreeBeam> treeBeams = beamListMap.get(nowBeamLevel);
if (treeBeams == null){
treeBeams = new ArrayList<>();
}
for (int m = 0; m < beamCount; m++){
List<Beam> beamList = new ArrayList<>();
nowBeanListNo = nowBeanListNo + 1;
for (int k = 0; k < beamListCount; k++){
Random random = new Random();
Double beamValue = random.nextInt(beamValueRange)+1.0;
Beam beam = new Beam(String.valueOf((char)(beamName + k)), beamValue, nowBeamLevel, nowBeanListNo);
beamList.add(beam);
}
String parentBeamListName = lastBeamLevel + "-" + lastBeanListNo;
String beamListName = nowBeamLevel + "-" + nowBeanListNo;
TreeBeam tempTreeBeam = new TreeBeam(parentBeamListName, beamListName, beamList);
treeBeams.add(tempTreeBeam);
beamListMap.put(nowBeamLevel, treeBeams);
}
}
}
}
/**
* 初始化第一层的集束数据集
*/
private void initBeamListMap(){
// 初始化第一层的元素,只有一个
List<TreeBeam> firstTreeBeams = new ArrayList<>();
List<Beam> firstBeams = new ArrayList<>();
for (int i = 0; i < beamListCount; i++){
Random random = new Random();
Double beamValue = random.nextInt(beamValueRange)+1.0;
Beam beam = new Beam(String.valueOf((char)(beamName + i)), beamValue, 1, 1);
firstBeams.add(beam);
}
// 规则为1-1 第一层的一个集合 0-表示其实层无父层级
String parentBeamListName = "0-1";
String beamListName = "1-1";
TreeBeam treeBeam = new TreeBeam(parentBeamListName, beamListName, firstBeams);
firstTreeBeams.add(treeBeam);
beamListMap.put(1, firstTreeBeams);
}
/**
* 根据TreeBeam对象中的nowBeamNo当前层的
* @param keyExtractor
* @param <T>
* @return
*/
public <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor){
Set<Object> distSet = ConcurrentHashMap.newKeySet();
return t -> distSet.add(keyExtractor.apply(t));
}
/**
* 输出树形的集束集合
*/
@Override
public void showBeamMap(){
System.out.println("集束集合展示如下:");
for (int levelTemp : beamListMap.keySet()) {
System.out.println("层数:"+levelTemp);
System.out.println("父集束号||当前集束号||集束名称-集束值 ");
List<TreeBeam> tempMap = beamListMap.get(levelTemp);
for (TreeBeam treeBeamTemp : tempMap) {
List<Beam> beamTemp = treeBeamTemp.getBeamList();
for (int i = 0; i < beamTemp.size(); i++) {
Beam v = beamTemp.get(i);
System.out.println(treeBeamTemp.getParentBeamNo()+"||"+v.getBeamLevel()+"-"+v.getBeamNo()+"||"+v.getBeamName()+"-"+v.getBeamValue()+" ");
}
}
}
}
public Integer getLevelCount() {
return levelCount;
}
public Map<Integer, List<TreeBeam>> getBeamListMap() {
return beamListMap;
}
}
第四步,实现集束算法的规则,抓取策略尤为重要
import java.util.*;
import java.util.stream.Collectors;
/**
* 集束算法的工厂类和实现集束算法的算法步进部分
* 1.定义集束算法的宽度(即从集束集合号对应的集合中取值最大的数量)
* 2.根据父集束的key获取子集束的集合,而后根据子集束中集束值最大的两个集束集合
* 3.将此父集束再赋值给上述最大值的那两个子集束来作为父集束
* 例如:父集束key为 2-2 对应的集束List个数为5个 最大值分别为 42 86 59 72 69 其中 72和86为最大则取出他们对应的集束List作为下一个父类提供下一层使用
* 4.以此类推,最终追溯到最后一层后得出,每条路径的总值,再将各个总值进行比较得出最佳的路径。
*/
public class TreeBeamSearch implements BeamSearch{
// 集束抓取宽度 少于patchCount则算该集合个数
private final int patchCount = 5;
// 存储路径元素的集合, key为父集束序号(随着层级递进而变动),List为路径集合
private Map<String, List<Beam>> pathMap = new HashMap<>();
// 集束集合
private TreeBeamMap treeBeamMap;
public TreeBeamSearch(TreeBeamMap treeBeamMap) {
super();
this.treeBeamMap = treeBeamMap;
}
public void searchTreeBeamMap(){
// 获取该树形集束的层数
int levelCount = treeBeamMap.getLevelCount();
// 获取该树形集束的所有集合
Map<Integer, List<TreeBeam>> beamMap = treeBeamMap.getBeamListMap();
// 初始化第一层的抓取
TreeBeam treeBeam = beamMap.get(1).get(0);
ArrayList<Beam> tempList = new ArrayList();
// 从第一层开始获取出父集束号,根据父集束号来抓取子集束号最大值的固定个数
for (int i = 1; i < levelCount; i++) {
// 获取第i层的集束集合 将当前层作为父集束集合
List<TreeBeam> treeBeams = beamMap.get(i);
// 如果层数过多和分支过多时,遍历则过于缓慢 筛选掉父节点不在路径集合中的集束
treeBeams = treeBeams.stream().filter(v -> pathMap.keySet().contains(v.getNowBeamNo())).collect(Collectors.toList());
patchBeamByParentTreeBeams(beamMap, treeBeams, i);
}
// 统计出每条路径的总值,并求出最佳路径
totalMaxBeamValueOfTreeBeam();
}
/**
* 首先判断该父集束号 是否在路径中 不在路径中说明不是候选路径
* 根据父集束号依次获取子集束集合并添加到集合内 先移除父集束 后添加子集束到路径集合中
* @param
* @param beamMap
* @param parentTreeBeams
* @param beamLevel 父集束的层号
*/
private void patchBeamByParentTreeBeams(Map<Integer, List<TreeBeam>> beamMap, List<TreeBeam> parentTreeBeams, int beamLevel) {
for (TreeBeam partTreeBeam : parentTreeBeams) {
// 获取子集束集合
List<TreeBeam> treeBeams = beamMap.get(beamLevel + 1);
// 先排序集合,获取当前tempList中最大值的那前两个子集束集合
sortTreeBeamList(tempList);
addMaxBeamValueToPathMap(tempList, partTreeBeam.getNowBeamNo());
}
}
/**
* 添加tempList中最大值的那前两个子集束集合到路径集合
* @param treeBeams
* @param parentBeamNo
*/
private void addMaxBeamValueToPathMap(List<TreeBeam> treeBeams, String parentBeamNo) {
for (int i = 0; i < treeBeams.size(); i++) {
TreeBeam treeBeam = treeBeams.get(i);
if (i >= patchCount){
break;
}
if (treeBeam != null){
// 先获取父集束对应的集合
List<Beam> parBeamList = pathMap.get(treeBeam.getParentBeamNo());
beamList.add(getBeamByMaxBeamValue(treeBeam.getBeamList()));
// 添加当前集束集合
pathMap.put(treeBeam.getNowBeamNo(), beamList);
}
}
// 移除父集束
pathMap.remove(parentBeamNo);
}
/**
* 统计出每条路径的总值,并求出最佳路径
*/
public void totalMaxBeamValueOfTreeBeam(){
Map<String, Double> valueMap = new HashMap<>();
System.out.println("集束集合中候选路径展示如下:");
for (String pathKey : pathMap.keySet()) {
List<Beam> treeBeamList = pathMap.get(pathKey);
// 统计每条路径中集束最大值的那条路径的sum值,添加到map集合中
Double sumBeamValue = treeBeamList.stream().collect(Collectors.summingDouble(Beam::getBeamValue));
valueMap.put(pathKey, sumBeamValue);
// 打印各个路径以及对应的值
System.out.println("候选路径的值为:" + sumBeamValue);
printTreeBeamPath(pathKey);
}
// 比较各个路径中的那个最大值。
Map.Entry<String, Double> maxEntry = sortMapByValue(valueMap, 0);
String bestPath = maxEntry.getKey();
// 打印出最佳路径
System.out.println("集束集合中最佳路径展示如下:");
System.out.println("最佳路径的值为:" + maxEntry.getValue());
printTreeBeamPath(bestPath);
}
/**
* 获取最大的那个Beam集束
* @param beamList
* @return
*/
private Beam getBeamByMaxBeamValue(List<Beam> beamList) {
Optional<Beam> beam = beamList.stream().collect(Collectors.maxBy(Comparator.comparingDouble(Beam::getBeamValue)));
return beam.get();
}
/**
* 打印各个路径以及对应的值
* @param beamNo
*/
private void printTreeBeamPath(String beamNo) {
// 获取该路径的集束集合
List<Beam> beamList = pathMap.get(beamNo);
// 对集束集合按路径从小到大进行排序
sortBeamListByPath(beamList);
System.out.print("路径顺序为: ");
beamList.stream().forEach(beam-> {
String beamListName = beam.getBeamLevel() + "-" + beam.getBeamNo();
System.out.print(beamListName + ":"+ beam.getBeamValue() + " ");
});
System.out.println();
}
/**
* 使用Collections.sort的Comparator比较器将“树形集束”中的节点按F值从大到小排序
* @param treeBeamList
*/
private void sortTreeBeamList(List<TreeBeam> treeBeamList){
Collections.sort(treeBeamList, (n1, n2) -> {
if (getBeamByMaxBeamValue(n1.getBeamList()).getBeamValue() < getBeamByMaxBeamValue(n2.getBeamList()).getBeamValue()){
return 1;
}
return -1;
});
}
/**
* 按集束的路径值从小到大进行排序
* @param beamList
*/
private void sortBeamListByPath(List<Beam> beamList) {
Collections.sort(beamList, (b1, b2) -> {
if (b1.getBeamLevel() > b2.getBeamLevel() || b1.getBeamNo() < b2.getBeamNo()){
return 1;
}
return -1;
});
}
/**
* flag = 1 正序 从小到大
* flag = 0 倒序 从大到小
* @param map
* @param flag
* @return
*/
private static <K, V extends Comparable<? super V>> Map.Entry<K, V> sortMapByValue(Map<K, V> map, int flag) {
return map.entrySet().stream()
.sorted((o1, o2) -> flag == 1 ? o1.getValue().compareTo(o2.getValue()) : o2.getValue().compareTo(o1.getValue()))
.collect(Collectors.toList()).get(0);
}
}
以上就是所有的集束算法的核心代码,还是那种句算法就是一个思想的结晶,当你去思考后你才能发现它的美,以及美中不足的地方。小伙伴们还是自己去编写一遍吧,还是很有意思的。如果想要源码连接在此。代码