集合的最优子集划分

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

问题描述:如何将一个具有n个元素的集合N划分为k个子集N1…Nk,其中1≤k≤n,使得任意两个子集Ni∩Nj={∅},1<=i,j(i≠j)<=k,且N1∪...∪Nk=N,计算出每个子集的最优结果R1…Rk,使得F(R1…Rk)为最优的结果。

 

这个问题可以分成3步解决:

  1. 求出集合所有子集
  2. 求出所有子集N1…Nk组合
  3. 遍历子集的所有组合,求出最优的F(R1…Rk)值

 

问题一:子集表达

用二进制表达式来表达集合,0代表该位置的元素不存在,1代表该位置的元素存在,其中二进制从右边开始数第n位代表集合从左边开始数第n位,依次类推。

对于集合 {1, 2},表示如下

子集:{ } 没有元素 00

{1}  元素1位于集合中左边开始数第一位,那么用二进制表示就是从右边开始数第一位用1表示, 二进制表示为 01

{2} 元素2位于集合中左边开始数第二位,那么用二进制表示就是从右边开始数第二位用1表示, 二进制表示为 10

{1, 2}  元素1位于集合中左边开始数第一位,那么用二进制表示就是从右边开始数第一位用1表示 ,元素2位于集合中左边开始数第二位,那么用二进制表示就是从右边开始数第二位用1表示, 二进制表示为11  

从上面的规律可以看出子集的表示就是从 0…0 ~ 1…1 的表示,

转为数字就是0~2^n-1

根据子集的数值特点我们就可以定义一个长度为2^n的数组来表示子集的最优结果。数组的下标就是子集的二进制表示。

 

问题二:集合拆分

我们可以使用下面的方式来计算子集组合:

如集合{1,2,3}

当 k=1 时,那么只能划分为{1,2,3}

当 k=2 时,也就是需要划分成两个子集,可以划分为 {1, 2} {3} 或者 {1} {2, 3} 或者 {1, 3} {2}

当 k=3 时,需要划分成3个子集,{1} {2} {3}

注:其中1<=k<=n,当k=1或者k=n时,子集的划分都只有一种情况

其实每加一个元素,就是在原来的划分情况为新元素找到合适的位置放进去。

例如对于集合{1,2,3},现在我们加入第4个元素,值为4,那么应该怎么弄呢?

当 k = 1 时,我们只能找到{1,2,3}划分为1个子集的情况,然后将元素4插进去,得 {1,2,3,4}

当 k = 2时,我们有两种情况可以组成:

①找到{1,2,3}划分为k-1=1个元素的子集组合{1,2,3},然后元素4子集作为一个子集,即{1,2,3} {4}

②找到{1,2,3}划分为k=2个元素的子集组合, {1, 2} {3} 或者 {1} {2, 3} 或者 {1, 3} {2},然后逐个子集插入元素4,形成新的组合

对于{1,2} {3}   将元素4插入{1,2} => {1,2,4} {3}, 将元素插入{3} => {1,2} {3,4}

对于{1} {2,3}   将元素插入{1} => {1,4} {2,3} , 将元素插入{2,3} => {1} {2,3,4}

对于{1,3} {2} 将元素插入{1,3} => {1,3,4} {2},将元素插入{2} => {1,3} {2, 4}

当 k = 3时,步骤跟k=2时一样

当 k = 4时,步骤跟k=2时的①一样,由于{1,2,3}最多只有3个元素,所以步骤②不存在

设F(n, m)表示将一个各数为n的集合拆分成有m个子集表示的情况,其中1≤m≤n,

可以推导出F(n, m) = F(n-1, m-1) + m * F(n-1,m)

其中:F(n, 1) = 1, F(n, n) = 1

那么 2c3303886bf2f88a01b3f52b63a1fe64234.jpg

 

问题三:求最优结果

自行定义F的计算方法即可。

 

下面是一个实现例子。

1、Strategy

/**
 * 策略接口,该接口主要从集合中获取策略算法结果,T为元素类型,R为结果类型
 *
 * @param 
 * @param 
 */
public interface Strategy {
	
	/**获取平均值**/
	Strategy AVG = (datas) -> (int)datas.stream().mapToInt(e -> (e != null ? e.intValue() : 0)).average().getAsDouble();
	
	/**求最大值**/
	Strategy MAX = (datas) -> datas.stream().max((e1, e2) -> e1.compareTo(e2)).get();
	
	/**
	 * 策略器的唯一ID表示,用于判断是否为相同的策略器
	 * @return
	 */
	default String getId() {
		return Integer.toBinaryString(this.hashCode());
	}
	
	/**
	 * 这里给定一个集合datas,计算该集合的策略值并返回
	 * @param datas
	 * @return
	 */
	R get(Collection datas);
	
}

2、StrategyNode

/**
 * 策略节点
 *
 * @param  原始的数据来源类型
 * @param  为该节点所有策略器Strategy返回的结果类型
 */
public class StrategyNode {
	
	final T value;//集合中的原始元素
	
	final Set> strategies = new HashSet<>();//元素拥有的策略
	
	public StrategyNode(T value, Strategy[] strategies) {
		Objects.requireNonNull(value);
		this.value = value;
		if (strategies != null && strategies.length > 0) {
			for (Strategy st : strategies) {
				this.strategies.add(st);
			}
		}
	}
	
	/**
	 * 判断集合中元素是否满足某种策略,通过策略Id匹配
	 * @param strategy
	 * @return
	 */
	public boolean existStrategy(Strategy strategy) {
		if (strategy != null && !this.strategies.isEmpty()) {
			for (Strategy s : this.strategies) {
				if (s.getId().equals(strategy.getId())) {
					return true;
				}
			}
		}
		return false;
	}

}

3、StrategySelector, T, R>

/**
 * 策略选择,从所有策略中选择最优的策略
 * @param 
 * @param 
 * @param 
 */
interface StrategySelector, T, R> {

	/****/
	StrategySelector, Integer, Integer> MAX = (strategies, values) -> {
		Set> rtSet = new HashSet<>();
		Number rt = null;
		for (Strategy st : strategies) {
			Integer r = st.get(values);
			if (r != null && (rt == null || r.intValue() >= rt.intValue())) {
				if (!r.equals(rt)) {
					rtSet.clear();
				}
				rt = r;
				rtSet.add(st);
			}
		}
		return rtSet;
	};

	/**
	 * 返回最优策略,如果有多个,返回多个
	 * 
	 * 给定集合values,并且共有的策略strategies,从strategies取出使得结果最优的strategies集合并返回
	 * 
	 * @param strategies 策略
	 * @param values 集合
	 * @return
	 */
	Set get(Set strategies, Collection values);

}

4、StrategyCalculate

/**
 * 对子集组合进行运算
 *
 * @param  子集策略结果的集合元素
 * @param  子集计算后得到的数据类型
 */
interface StrategyCalculate {

	StrategyCalculate SUM = (datas) -> datas.stream().mapToInt(e -> e != null ? e.intValue() : 0).sum();

	StrategyCalculate AVG = (datas) -> (int) datas.stream().mapToDouble(e -> e != null ? e.doubleValue() : 0.0D).average().getAsDouble();

	StrategyCalculate MAX = (datas) -> datas.stream().max((e1, e2) -> e1.compareTo(e2)).get();

	/**
	 * 获取最优答案
	 * 
	 * R 为集合N 通过 StrategySelector 选择最优的策略 Strategy 所计算出的值
	 * 这里是对每个子集的最优策略结果进行合并运算
	 * 
	 * @return
	 */
	K get(Collection datas);

}

5、StrategyComparator

/**
 * 组合结果比较器
 * @param 
 */
interface StrategyComparator {
	
	StrategyComparator GREAT = (r1, r2) -> r1.compareTo(r2) >= 0;
	
	/**
	 * K 为 StrategyCalculate 计算出的结果,这里对结果进行比较取优
	 * 判断k1 是否优于 k2
	 * @param k1
	 * @param k2
	 * @return
	 */
	boolean better(K k1, K k2);
	
}

6、SubSet

/**
 * 表示子集
 *
 * @param 
 * @param 
 */
public class SubSet {
	
	/**子集元素**/
	private List elements;
	
	/**子集使用的策略算法**/
	private Set> strategies;
	
	/**子集的策略计算结果**/
	private R result;
	
	public SubSet(List elements, Set> strategies, R result) {
		this.elements = elements;
		this.strategies = strategies;
		this.result = result;
	}

	public List getElements() {
		return elements;
	}

	public void setElements(List elements) {
		this.elements = elements;
	}

	public Set> getStrategies() {
		return strategies;
	}

	public void setStrategies(Set> strategies) {
		this.strategies = strategies;
	}

	public R getResult() {
		return result;
	}

	public void setResult(R result) {
		this.result = result;
	}
	
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("{");
		if (this.elements != null && !this.elements.isEmpty()) {
			for (int i = 0, len = this.elements.size(); i < len; ++i) {
				if (i != 0) {
					builder.append(", ");
				}
				builder.append(String.valueOf(this.elements.get(i)));
			}
		}
		builder.append("} = ").append(String.valueOf(this.result));
		return builder.toString();
	}

}

7、Plan

/**
 * 表示子集的一种组合方案
 *
 * @param 
 * @param 
 * @param 
 */
public class Plan {
	
	/**子集组合**/
	private List> subSets;
	
	/**子集组合的计算结果**/
	private K result;
	
	public Plan(List> subSets, K result) {
		this.subSets = subSets;
		this.result = result;
	}

	public List> getSubSets() {
		return subSets;
	}

	public void setSubSets(List> subSets) {
		this.subSets = subSets;
	}

	public K getResult() {
		return result;
	}

	public void setResult(K result) {
		this.result = result;
	}
	
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("[");
		if (this.subSets != null && !this.subSets.isEmpty()) {
			for (int i = 0, len = this.subSets.size(); i < len; ++i) {
				if (i != 0) {
					builder.append(", ");
				}
				builder.append(String.valueOf(this.subSets.get(i)));
			}
		}
		builder.append("] = ").append(String.valueOf(this.result));
		return builder.toString();
	}
	
}

8、Answer

/**
 * 最优答案的组合
 *
 * @param 
 * @param 
 * @param 
 */
public class Answer {
	
	/**最优结果**/
	private K result;
	
	/**最优的方案集合**/
	private List> plans;
	
	public Answer(List> plans, K result) {
		this.plans = plans;
		this.result = result;
	}

	public K getResult() {
		return result;
	}

	public void setResult(K result) {
		this.result = result;
	}

	public List> getPlans() {
		return plans;
	}

	public void setPlans(List> plans) {
		this.plans = plans;
	}
	
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("最优值:").append(String.valueOf(this.result)).append("\n");
		builder.append("最优方案个数:").append(this.plans == null ? 0 : this.plans.size()).append("\n");
		if (this.plans != null && !this.plans.isEmpty()) {
			this.plans.forEach(e -> builder.append(String.valueOf(e)));
		}
		return builder.toString();
	}
	
}

9、

/**
 * 集合划分求最优的算法构造器
 *
 * @param 
 * @param 
 * @param 
 * @param 
 */
public class SetSplitBuilder, T, R, K> {
	
	static Logger logger = Logger.getLogger(SetSplitBuilder.class.getName());
	
	private static final Map> cache = new HashMap<>();//子集组合缓存
	
	private static final int cache_size = 10;
	
	static {
		initCache();
	}
	
	private static void initCache() {
		for (int i = 1; i <= cache_size; ++i) {
			List rows = new ArrayList<>(s(i));
			initCache(0, i, new int[0], rows);
			cache.put(i, rows);
		}
	}
	
	private static void initCache(int idx, int len, int[] subs, List combs) {
		if (idx < len) {
			for (int j = 0; j < subs.length; ++j) {
				int[] newSubs = Arrays.copyOf(subs, subs.length);
				newSubs[j] = newSubs[j] | (1 << idx);
				initCache(idx + 1, len, newSubs, combs);
			}
			int[] newSubs = Arrays.copyOf(subs, subs.length + 1);
			newSubs[subs.length] = (1 << idx);
			initCache(idx + 1, len, newSubs, combs);
		} else {
			combs.add(Arrays.copyOf(subs, subs.length));
		}
	}
	
	/**
	 * n个元素的集合有多少种划分方法
	 * @param n
	 * @return
	 */
	private static int s(int n) {
		int sum = 0;
		for (int i = 1; i <= n; ++i) {
			sum += f(n, i);
		}
		return sum;
	}
	
	/**
	 * n个元素的集合划分成m个子集的个数
	 * @param n
	 * @param m
	 * @return
	 */
	private static int f(int n, int m) {
		if (m == 1 || n == m) {
			return 1;
		}
		
		int[][] mt = new int[n + 1][m + 1];
		for (int i = 0; i < mt.length; ++i) {
			mt[i][1] = 1;
		}
		for (int i = 1; i <= m; ++i) {
			mt[m][m] = 1;
		}
		
		for (int i = 2; i <= n; ++i) {
			for (int j = 2; j <= m; ++j) {
				mt[i][j] = mt[i - 1][j - 1] + mt[i - 1][j] * j;
			}
		}
		
		return mt[n][m];
	}
	
	private N[] values; 
	
	private Object[] subsetResults;//每个子集的最优结果
	
	private Object[] subsetResultStrategies;//每个子集对应的最优策略,可以有多个,用set表示
	
	private StrategySelector, T, R> strategySelector;
	
	private StrategyCalculate strategyCalculate;
	
	private StrategyComparator strategyComparator;
	
	private K result;
	
	private List optimalSubsetCombs = new ArrayList<>();//子集最优组合
	
	List> plans;
	
	public SetSplitBuilder(N[] values, StrategySelector, T, R> strategySelector, StrategyCalculate strategyCalculate, StrategyComparator strategyComparator) {
		Objects.requireNonNull(values);
		Objects.requireNonNull(strategySelector);
		Objects.requireNonNull(strategyCalculate);
		Objects.requireNonNull(strategyComparator);
		this.values = values;
		this.strategySelector = strategySelector;
		this.strategyCalculate = strategyCalculate;
		this.strategyComparator = strategyComparator;
	}

	public void setValues(N[] values) {
		Objects.requireNonNull(values);
		this.values = values;
	}

	public void setStrategySelector(StrategySelector, T, R> strategySelector) {
		Objects.requireNonNull(strategySelector);
		this.strategySelector = strategySelector;
	}

	public void setStrategyCalculate(StrategyCalculate strategyCalculate) {
		Objects.requireNonNull(strategyCalculate);
		this.strategyCalculate = strategyCalculate;
	}

	public void setStrategyComparator(StrategyComparator strategyComparator) {
		Objects.requireNonNull(strategyComparator);
		this.strategyComparator = strategyComparator;
	}
	
	/**
	 * 计算结果
	 */
	public void build() {
		logger.info("子集初始化开始----->");
		initSubsetResults();
		logger.info("子集初始化结束----->");
		
		logger.info("计算最优路径开始----->");
		calculate();
		logger.info("计算最优路径结束----->");
		
		logger.info("组装结果开始----->");
		packageResult();
		logger.info("组装结果结束----->");
	}
	
	public Answer getAnswer() {
		return new Answer<>(this.plans, this.result);
	}
	
	/**
	 * 计算每个子集的最优策略
	 */
	private void initSubsetResults() {
		int len = 2 << this.values.length;
		this.subsetResults = new Object[len];
		this.subsetResultStrategies = new Object[len];
		
		for (int i = 0; i < len; ++i) {
			//int sub = i;//子集组合
			List subNodes = new ArrayList<>(this.values.length);
			for (int j = 0; j < this.values.length; ++j) {
				if (((i >> j) & 1) == 1) {
					subNodes.add(this.values[j]);
				}
			}
			
			//获取最少的策略算法
			Set> nodesStrategies = null;
			for (N n : subNodes) {
				if (nodesStrategies == null || n.strategies.size() < nodesStrategies.size()) {
					nodesStrategies = n.strategies;
				}
			}
			
			//满足子集的策略
			Set> satisfiedStrategy = new HashSet<>();
			if (nodesStrategies != null && !nodesStrategies.isEmpty()) {
				boolean flag = true;
				for (Strategy s : nodesStrategies) {
					for (N n : subNodes) {
						if (!n.existStrategy(s)) {
							flag = false;
							break;
						}
					}
					if (flag) {
						satisfiedStrategy.add(s);
					}
				}
			}
			
			//获取子集最优的策略集合
			Set> r = null;
			if (!satisfiedStrategy.isEmpty()) {
				r = this.strategySelector.get(satisfiedStrategy, subNodes.stream().map(e -> e.value).collect(Collectors.toList()));
			}
			this.subsetResults[i] = r != null && !r.isEmpty() ? r.iterator().next().get(subNodes.stream().map(e -> e.value).collect(Collectors.toList())) : null;
			this.subsetResultStrategies[i] = r;
		}
	}
	
	/**
	 * 计算最优路径
	 */
	private void calculate() {
		List rows = cache.get(this.values.length);
		if (rows != null) {
			calculateByCache(rows);
		} else {
			calculate(0, this.values.length, new int[0]);
		}
	}
	
	private void calculateByCache(List rows) {
		for (int[] subs : rows) {
			cacheCurrentOptimalResults(subs);
		}
	}
	
	private void calculate(int idx, int len, int[] subs) {
		if (idx < len) {
			for (int j = 0; j < subs.length; ++j) {
				int[] newSubs = Arrays.copyOf(subs, subs.length);
				newSubs[j] = newSubs[j] | (1 << idx);
				calculate(idx + 1, len, newSubs);
			}
			int[] newSubs = Arrays.copyOf(subs, subs.length + 1);
			newSubs[subs.length] = (1 << idx);
			calculate(idx + 1, len, newSubs);
		} else {
			cacheCurrentOptimalResults(subs);
		}
	}
	
	/**
	 * subs 为子集的组合结果
	 * 缓存当前最优结果
	 */
	@SuppressWarnings("unchecked")
	private void cacheCurrentOptimalResults(int[] subs) {
		//记录每个集合的策略结果
		List datas = new ArrayList<>();
		for (Integer st : subs) {
			if (st != null) {
				R r = (R)this.subsetResults[st.intValue()];
				if (r != null) {
					datas.add(r);
				}
			}
		}
		
		K rt = this.strategyCalculate.get(datas);//获取子集计算结果
		if (this.result == null || this.strategyComparator.better(rt, this.result)) {
			if (!rt.equals(this.result)) {
				this.optimalSubsetCombs.clear();
			}
			this.optimalSubsetCombs.add(subs);
			this.result = rt;
		}
	}
	
	/**
	 * 组装结果
	 */
	@SuppressWarnings("unchecked")
	private void packageResult() {
		this.plans = new ArrayList<>(this.optimalSubsetCombs.size());
		for (int[] rows : this.optimalSubsetCombs) {
			List> subsets = new ArrayList<>(rows.length);
			for (int sub : rows) {
				subsets.add(new SubSet(getSubSetValues(sub), (Set>)this.subsetResultStrategies[sub], (R)this.subsetResults[sub]));
			}
			this.plans.add(new Plan(subsets, this.result));
		}
	}
	
	/**
	 * 获取子集中的元素集合
	 * @param sub
	 * @return
	 */
	private List getSubSetValues(int sub) {
		List values = new ArrayList<>();
		for (int i = 0; i < this.values.length; ++i) {
			if ((sub & (1 << i)) != 0) {
				values.add(this.values[i].value);
			}
		}
		return values;
	}
	
	
}

10、测试

public class Test {
	
	public static void main(String[] args) {
		
		for (int l = 1; l <= 10; ++l) {
			StrategyNode[] nodes = new StrategyNode[l];
			for (int i = 0; i < nodes.length; ++i) {
				nodes[i] = new StrategyNode(i + 1, Arrays.asList(Strategy.AVG, Strategy.MAX, Strategy.AVG, Strategy.MAX, Strategy.AVG, Strategy.MAX, Strategy.AVG, Strategy.MAX).toArray(new Strategy[0]));
			}
			SetSplitBuilder,Integer,Integer,Integer> strategyBuilder = 
					new SetSplitBuilder<>(nodes, StrategySelector.MAX, StrategyCalculate.AVG, StrategyComparator.GREAT);
			strategyBuilder.build();
			Answer answer = strategyBuilder.getAnswer();
			System.out.println(answer.toString());
		}
		
	}

}

 

转载于:https://my.oschina.net/linchuhao23/blog/2877703

你可能感兴趣的:(集合的最优子集划分)