让我们浏览一下 Matlab 代码并将每一行翻译成 Java。我们将需要一些抽象,我们将在旅途中介绍它们。
第一行:
x = [1,2,3,4,5,6,7];
向量被分配给变量x。我们可以简单地说向量是一个整数数组,但也许我们稍后需要一些更好的抽象。让我们定义一个新类Vector。不要将其与java.util.Vector 混淆:可能存在多个具有相同非限定名称的类。
class Vector {
private int[] value;
Vector(int... value) {
this.value = value;
}
int apply(int i) {
return value[i - 1];
}
int length() {
return value.length;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
String prefix = "";
for (int entry : value) {
result.append(prefix).append(entry);
prefix = " ";
}
return result.toString();
}
}
我们使用整数数组作为Vector 的内部表示。请注意,只要内部表示不泄漏到类的接口中,您就可以随时更换内部表示。因此,我们将value-member 的访问权限限制为private,这意味着只有Vector 类型的对象才能访问它。
新的Vector 对象通过调用构造函数Vector(int... value) 来实例化,该构造函数采用可变参数整数参数。在 Java 内部,可变参数与数组相同,但它们为我们提供了语法糖,可以通过以下方式实例化我们的 x:
Vector x = new Vector(1, 2, 3, 4, 5, 6, 7);
这看起来与您的 Matlab 代码非常相似。
另一件事是,在 Java 中,数组是零索引的,而 Matlab 从 1 开始索引。我们的 Vector 类定义了一个 apply-方法,它应该访问 i-th 索引.因此,它返回value[i-1]。
现在我们要计算
x = perms(x);
perms 返回一个矩阵,包含向量x 的所有排列。所以我们需要另一个抽象:Matrix。
class Matrix {
private Vector[] rows;
Matrix(Vector... value) {
this.rows = value;
}
int apply(int x, int y) {
return rows[x - 1].apply(y);
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
String prefix = "";
for (Vector row : rows) {
result.append(prefix).append(row.toString());
prefix = System.lineSeparator();
}
return result.toString();
}
}
Matrix 的定义与Vector 非常相似,但其内部表示是Vector 的数组,即矩阵的行。同样,我们定义了一个方法apply 来检索一个元素:这一次,它需要两个参数,行索引和列索引。
旁注:重写方法 toString 总是好的,它定义在 Java 类型层次结构的顶部元素中:Object。您可以尝试实例化 Vector 或 Matrix 并将其作为参数传递给 System.out.println 以查看字符串表示的样子。
现在我们仍然需要在 Java 中实现perms。方法perms 接受Vector 并返回Matrix。我有一个非常黑客和丑陋的实现,我有点不愿意展示,但为了完整的答案,这里是:
static Matrix perms(Vector vector) {
int[] indices = new int[vector.length()];
for (int i = 0; i < vector.length(); i++)
indices[i] = i;
List<int[]> allPermuationIndices = new ArrayList<int[]>();
permutation(new int[0], indices, allPermuationIndices);
Vector[] perms = new Vector[allPermuationIndices.size()];
for (int i = 0; i < perms.length; i++) {
int[] permutationIndices = allPermuationIndices.get(i);
int[] vectorValue = new int[permutationIndices.length];
for (int j = 0; j < permutationIndices.length; j++)
vectorValue[j] = vector.apply(permutationIndices[j] + 1);
perms[i] = new Vector(vectorValue);
}
return new Matrix(perms);
}
private static void permutation(int[] prefix, int[] remaining, List<int[]> returnValue) {
if (remaining.length == 0)
returnValue.add(prefix);
else {
for (int i = 0; i < remaining.length; i++) {
int elem = remaining[i];
int[] newPrefix = Arrays.copyOf(prefix, prefix.length + 1);
newPrefix[prefix.length] = elem;
int[] newRemaining = new int[remaining.length - 1];
System.arraycopy(remaining, 0, newRemaining, 0, i);
System.arraycopy(remaining, i + 1, newRemaining, i + 1 - 1, remaining.length - (i + 1));
permutation(newPrefix, newRemaining, returnValue);
}
}
}
不要费心去理解它在做什么。尝试自己编写一个干净的实现(或谷歌寻求解决方案)。
现在,如果我们想重新分配我们的x,我们会遇到麻烦:类型不匹配:我们将x 声明为Vector 类型,但perm 返回Matrix。有多种方法可以解决这个问题:
我们可以将Vector 声明为Matrix,即将签名更改为Vector extends Matrix。这个解决方案可能有意义,但注意不要破坏行为子类型:如果一个类B 是一个 类A,那么B 必须具有与A 相同的行为并且可以定义额外的行为。在同一张纸条上查找Liskov Substitution Principle。
我们可以将x 声明为Vector 和Matrix 的超类型。目前,这是Object,但我们也可以定义一个新的通用超类型。然而,这个解决方案可能会失去我们的静态类型安全性。例如,如果我们想使用x 作为perm 的参数,我们需要将其动态转换为Vector
我们可以定义第二个变量x2 类型为Matrix 来保存结果。在这种情况下,我建议使用此解决方案。
接下来,我们分配i = 0; 和c=1;,在Java 中它们转换为
int i = 0;
int c = 1;
现在,for循环:
for m = 1:1:5040
...
end
翻译成
for (int m = 1; m <= 5040; i++) {
...
}
除了把它们放在一起之外,唯一剩下的就是 if 语句:
if(x2(c,n) == (x2(c,(n+1))-1))
...
end
翻译成
if (x2.apply(c, n) == (x2.apply(c, n+1) - 1)) {
...
}
其中apply 是我们在Matrix 上定义的方法。请注意,在 Java 中,如果将 == 应用于非原始类型(即,除了 int、byte、char、double、boolean 和 float 之外的所有内容,则会给出奇怪的结果。通常,您使用在Object 上定义的方法equals 来测试等价性。