我们知道置换分为三类:恒等置换、轮换、轮换组合。所以定义一个接口和三个实现类。
Displaceexpression是接口,EmptyDisplaceexpression是恒等置换,用枚举做成了单例模式,CompositeDisplaceexpression是轮换组合,使用组合模式,CyclicDisplaceexpression是轮换。
接口核心方法是,表示应用于一个数组后,进行置换操作,返回一个新数组。因为置换是可以置换任何对象的,所以这里使用了Object[]。
Object[] apply(Object[] array);
因为计算机是赋值操作,所以对轮换位置的遍历要从后到前,并且需要特别处理首尾两个元素。而轮换的核心代码如下:
@Override
public Object[] apply(Object[] array) {
final Object[] objects = new Object[array.length];
System.arraycopy(array, 0, objects, 0, array.length);
Object temp = array[cyclic[cyclic.length - 1]];
for (int i = cyclic.length - 1; i > 0; i--) {
objects[cyclic[i]] = array[cyclic[i - 1]];
}
objects[cyclic[0]] = temp;
return objects;
}
轮换组合的核心代码,就比较容易了。
@Override
public Object[] apply(Object[] array) {
// 循环去搞呗
Object[] result = array;
for (Displaceexpression displaceexpression : expressions) {
result = displaceexpression.apply(result);
}
return result;
}
恒等置换的代码我就不写了,直接返回原数组。还有一个复杂的逻辑是轮换组合的化简方法。思路上,我是先找到每个位置置换后的位置,放入一个map中,然后根据这个map,重新生成置换表达式,Java代码如下:
public Displaceexpression simplify() {
// 首先是先作用于一个数组
// 代数运算的化简
final TreeMap positionMap = new TreeMap<>();
// 第一步是要获取所有的变量
final Set positions = this.positions();
for (Integer position : positions) {
// 过滤掉自己映射到自己的特殊情况
final Integer next = this.next(position);
if (!Objects.equals(position, next)) {
positionMap.put(position, next);
}
}
List result = new ArrayList<>();
// 组成最简表达式
while (!positionMap.isEmpty()) {
List cyclic = new ArrayList<>();
final Map.Entry firstEntry = positionMap.firstEntry();
for (Integer k = firstEntry.getKey(), v = firstEntry.getValue();
!cyclic.contains(k);
k = v, v = positionMap.get(k)) {
cyclic.add(k);
}
cyclic.forEach(positionMap::remove);
if (!cyclic.isEmpty()) {
result.add(new CyclicDisplaceexpression(cyclic));
cyclic.clear();
}
}
if (result.isEmpty()) {
return EmptyDisplaceexpression.INSTANCE;
}
if (result.size() == 1) {
return result.get(0);
}
return new CompositeDisplaceexpression(result);
}
源码git地址: https://e.coding.net/buildt/math/displace.git



