Java: 创建一个数组的副本而不使其成为引用
Java: Create a duplicate of an array without making it a reference
我写了一系列矩阵运算,其中我采用二维浮点数组,将其视为矩阵,并对其执行矩阵运算以获得逆矩阵。我的问题是,虽然我使用 class 方法的数组不是 class 的一部分,但每次我 运行 使用数组作为参数的方法时,数组本身也会被修改。
首先我将描述如何得到我的矩阵的逆,然后我将展示输出。
矩阵求逆的步骤如下:
- 获取辅因子矩阵(即创建原始矩阵的矩阵次要矩阵,然后取反所有其他条目。如果C = Cofactor Matrix,M = Matrix of Minors,i是当前行,j是当前列,然后 C[ i ][ j ] = M[ i ][ j ]*( -1 )^( i + j )
- 通过转置(用其类似的列、行条目替换行、列条目,反之亦然)辅助因子矩阵,将辅助因子矩阵转换为辅助(也称为伴随)矩阵。如果 C = Cofactor Matrix,A = Adjugate Matrix,i 是当前行,j 是当前列,则 A[ i ][ j ] = C[ j ][ i ]
- 最后,对原始矩阵取一个以上的行列式,并将辅助矩阵乘以该值。如果 I = 逆矩阵,A = 调整矩阵和 D = 行列式,则 I = (1/D)*A
- 为了检验你是否真正掌握了矩阵的逆矩阵,可以用原矩阵乘以它的逆矩阵得到单位矩阵。
如果 I = 逆矩阵,O = 原始矩阵,id = 单位矩阵,则 O*I = id
现在我将介绍实现这些操作的代码。为了简洁起见,我不会描述如何获得Minor矩阵或行列式,但我遇到的问题无论如何都会变得明显。
public class MatrixOperations {
//Note: this method works fine. There are no problems.
public float determinant(float [][] a)
{
float [][] temp_mat;
float res = 0;
//assuming a square matrix
/*If it's a 2X2, then use the formula for a determinant of
2X2 matrices.*/
if(a.length == 2)
{
return a[0][0]*a[1][1]-a[0][1]*a[1][0];
}
/*Otherwise do the determinant formula recursively until your
determinant is made up of 2X2 matrix determinants and scalar products*/
else
{
temp_mat = new float[a.length-1][a.length-1];
int placej = 0;
int placei = 0;
for(int k = 0; k<a.length;k++)
{
for(int j = 0; j<a.length; j++)
{
for(int i = 1; i < a.length; i++)
{
placei = i-1;
if(j != k)
{
if(j < k)
{
temp_mat[placei][j] = a[i][j];
}
else if(j > k)
{
if (i == 1){
placej = j-1;
}
temp_mat[placei][placej] = a[i][j];
}
}
}
}
res+=a[0][k]*determinant(temp_mat)*(int)Math.pow(-1, k);
}
return res;
}
}
//Note: this method also works fine
//Scalar product method
public float[][] mul(float[][] m, float r)
{
float[][] res = new float[m.length][m.length];
for(int i = 0; i < m.length; i++)
{
for(int j = 0; j < m.length; j++)
{
res[i][j]= m[i][j]*r;
}
}
return res;
}
//Note: This method also works fine
public float[][] mul(float[][] m,float[][] n)
{
float[][] res = new float[m.length][m.length];
for(int i = 0; i < m.length; i++)
{
for(int j = 0; j < m.length; j++)
{
for(int k = 0; k < m.length; k++)
{
res[i][j] += m[i][k]*m[k][i];
}
}
}
return res;
}
//The method for creating a matrix of minors
//Here I start having problems
public float[][] minor(float [][] m)
{
float [][] minor_mat = new float [m.length][m.length];
//If the matrix is greater than a 2X2, use this to generate a matrix of minors
if(m.length > 2)
{
float [][] current_minor = new float [m.length-1][m.length-1];
int placei = 0;
int placej = 0;
for(int i = 0; i < m.length; i++)
{
for(int j = 0; j < m.length; j++)
{
for(int k = 0; k < m.length; k++)
{
for(int l = 0; l < m.length; l++)
{
if(i != k && j != l)
{
if(k<i)
placei = k;
else if(k>i)
placei = k-1;
if(l<j)
placej = l;
else if(l>j)
placej = l-1;
current_minor[placei][placej] = m[k][l];
}
}
}
minor_mat[i][j] = this.determinant(current_minor);
}
}
}
//otherwise use the definition for 2X2 matrix of minors
else
{
//even though minor_mat is using m.clone() rather than m, when I return the result, m has still been modified for some reason.
minor_mat = m.clone()
float temp;
temp = minor_mat[0][0];
minor_mat[0][0] = minor_mat[1][1];
minor_mat[1][1] = temp;
temp = minor_mat[0][1];
minor_mat[0][1] = minor_mat[1][0];
minor_mat[1][0] = temp;
}
return minor_mat;
}
//the same problem occurs here as it did in the minor method
//m appears to get modified even though I only use m.clone()
public float[][] cofactor(float [][] m)
{
float[][] res = m.clone();
res = this.minor(res)
for(int i = 0; i < m.length; i++)
{
for(int j = 0; j < m.length; j++)
{
res[i][j] = res[i][j]*(int)Math.pow(-1, i + j);
}
}
return res;
}
//The following transpose, adjugate, and inverse methods have the same problem
public float[][] transpose(float[][] m)
{
float[][] res = new float[m.length][m.length];
float temp = 0;
for(int i = 0; i < m.length; i++)
{
for(int j = 0; j < m.length; j++)
{
temp = m[i][j];
res[i][j] = m[j][i];
res[j][i] = temp;
}
}
return res;
}
public float[][] adjugate(float[][] m)
{
float[][] res = this.transpose(this.cofactor(m));
return res;
}
public float[][] inverse(float[][] m)
{
float[][] res = this.mul(this.adjugate(m), (1/this.determinant(m)));
return res;
}
//print out the matrix in square form
public void matrixprint(float [][] m)
{
for(int i = 0; i < m.length; i++)
{
System.out.println("");
for(int j = 0; j < m[i].length; j++){
System.out.print(m[i][j] + " ");
}
}
System.out.println("\n");
}
}
现在是主要 class 和创建 MatrixOperations class 实例并在 2X2 矩阵上使用其方法的主要方法。
public class Main {
public static void main(String[] args) {
MatrixOperations mo = new MatrixOperations();
//Create a 2X2 matrix called "matrix" and set its elements
//Then perform each step on "matrix" and finally test if you have acquired the correct inverse
float [][] matrix = new float[2][2];
matrix[0][0] = 2;
matrix [0][1] = 5;
matrix [1][0] = 4;
matrix [1][1] = 3;
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Minor = ");
mo.matrixprint(mo.minor(matrix));
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Cofactor = ");
mo.matrixprint(mo.cofactor(matrix));
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Adjugate = ");
mo.matrixprint(mo.adjugate(matrix));
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Determinant = ");
System.out.println(mo.determinant(matrix));
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Inverse = ");
mo.matrixprint(mo.inverse(matrix));
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Identity = ");
mo.matrixprint(mo.mul(mo.inverse(matrix), matrix));
}
}
现在你会看到,当我显示输出时,每次我在"matrix"上使用方法并重新打印"matrix",即使我的方法也被修改了"matrix"只使用 "matrix" 的副本而不是 "matrix" 本身。
输出:
Matrix =
2.0 5.0
4.0 3.0
Minor =
3.0 4.0
5.0 2.0
Matrix =
3.0 4.0
5.0 2.0
Cofactor =
3.0 -4.0
-5.0 2.0
Matrix =
3.0 -4.0
-5.0 2.0
Adjugate =
3.0 5.0
4.0 2.0
Matrix =
3.0 4.0
5.0 2.0
Determinant =
-14.0
Matrix =
3.0 4.0
5.0 2.0
Inverse =
-0.21428573 0.35714287
0.2857143 -0.14285715
Matrix =
3.0 -4.0
-5.0 2.0
Identity =
0.1479592 0.1479592
0.12244898 0.12244898
任何 help/explanation 为什么会发生这种情况,我们将不胜感激。
这一行做一个浅层克隆;
float[][] res = m.clone();
这将复制 res
,它是对数组的引用的数组。但不是 res
指向的任何数组。很可能你想要的是
float[][] res = new float[m.length][];
for (int i = 0; i < m.length; i++)
res[i] = m[i].clone();
这是因为您在 MatrixOperations
class 的方法中传递了 matrix
对象的引用。它不是 matrix
对象的副本。
来自Java doc:
Reference data type parameters, such as objects, are also passed into
methods by value. This means that when the method returns, the
passed-in reference still references the same object as before.
二维数组就是数组的数组。
clone()
对数组进行浅表克隆。
所以你有一个新的克隆外部数组,但它引用相同的条目(内部数组)。
克隆外部数组后,遍历外部数组并克隆所有内部数组以获得深度克隆。
我写了一系列矩阵运算,其中我采用二维浮点数组,将其视为矩阵,并对其执行矩阵运算以获得逆矩阵。我的问题是,虽然我使用 class 方法的数组不是 class 的一部分,但每次我 运行 使用数组作为参数的方法时,数组本身也会被修改。
首先我将描述如何得到我的矩阵的逆,然后我将展示输出。
矩阵求逆的步骤如下:
- 获取辅因子矩阵(即创建原始矩阵的矩阵次要矩阵,然后取反所有其他条目。如果C = Cofactor Matrix,M = Matrix of Minors,i是当前行,j是当前列,然后 C[ i ][ j ] = M[ i ][ j ]*( -1 )^( i + j )
- 通过转置(用其类似的列、行条目替换行、列条目,反之亦然)辅助因子矩阵,将辅助因子矩阵转换为辅助(也称为伴随)矩阵。如果 C = Cofactor Matrix,A = Adjugate Matrix,i 是当前行,j 是当前列,则 A[ i ][ j ] = C[ j ][ i ]
- 最后,对原始矩阵取一个以上的行列式,并将辅助矩阵乘以该值。如果 I = 逆矩阵,A = 调整矩阵和 D = 行列式,则 I = (1/D)*A
- 为了检验你是否真正掌握了矩阵的逆矩阵,可以用原矩阵乘以它的逆矩阵得到单位矩阵。 如果 I = 逆矩阵,O = 原始矩阵,id = 单位矩阵,则 O*I = id
现在我将介绍实现这些操作的代码。为了简洁起见,我不会描述如何获得Minor矩阵或行列式,但我遇到的问题无论如何都会变得明显。
public class MatrixOperations {
//Note: this method works fine. There are no problems.
public float determinant(float [][] a)
{
float [][] temp_mat;
float res = 0;
//assuming a square matrix
/*If it's a 2X2, then use the formula for a determinant of
2X2 matrices.*/
if(a.length == 2)
{
return a[0][0]*a[1][1]-a[0][1]*a[1][0];
}
/*Otherwise do the determinant formula recursively until your
determinant is made up of 2X2 matrix determinants and scalar products*/
else
{
temp_mat = new float[a.length-1][a.length-1];
int placej = 0;
int placei = 0;
for(int k = 0; k<a.length;k++)
{
for(int j = 0; j<a.length; j++)
{
for(int i = 1; i < a.length; i++)
{
placei = i-1;
if(j != k)
{
if(j < k)
{
temp_mat[placei][j] = a[i][j];
}
else if(j > k)
{
if (i == 1){
placej = j-1;
}
temp_mat[placei][placej] = a[i][j];
}
}
}
}
res+=a[0][k]*determinant(temp_mat)*(int)Math.pow(-1, k);
}
return res;
}
}
//Note: this method also works fine
//Scalar product method
public float[][] mul(float[][] m, float r)
{
float[][] res = new float[m.length][m.length];
for(int i = 0; i < m.length; i++)
{
for(int j = 0; j < m.length; j++)
{
res[i][j]= m[i][j]*r;
}
}
return res;
}
//Note: This method also works fine
public float[][] mul(float[][] m,float[][] n)
{
float[][] res = new float[m.length][m.length];
for(int i = 0; i < m.length; i++)
{
for(int j = 0; j < m.length; j++)
{
for(int k = 0; k < m.length; k++)
{
res[i][j] += m[i][k]*m[k][i];
}
}
}
return res;
}
//The method for creating a matrix of minors
//Here I start having problems
public float[][] minor(float [][] m)
{
float [][] minor_mat = new float [m.length][m.length];
//If the matrix is greater than a 2X2, use this to generate a matrix of minors
if(m.length > 2)
{
float [][] current_minor = new float [m.length-1][m.length-1];
int placei = 0;
int placej = 0;
for(int i = 0; i < m.length; i++)
{
for(int j = 0; j < m.length; j++)
{
for(int k = 0; k < m.length; k++)
{
for(int l = 0; l < m.length; l++)
{
if(i != k && j != l)
{
if(k<i)
placei = k;
else if(k>i)
placei = k-1;
if(l<j)
placej = l;
else if(l>j)
placej = l-1;
current_minor[placei][placej] = m[k][l];
}
}
}
minor_mat[i][j] = this.determinant(current_minor);
}
}
}
//otherwise use the definition for 2X2 matrix of minors
else
{
//even though minor_mat is using m.clone() rather than m, when I return the result, m has still been modified for some reason.
minor_mat = m.clone()
float temp;
temp = minor_mat[0][0];
minor_mat[0][0] = minor_mat[1][1];
minor_mat[1][1] = temp;
temp = minor_mat[0][1];
minor_mat[0][1] = minor_mat[1][0];
minor_mat[1][0] = temp;
}
return minor_mat;
}
//the same problem occurs here as it did in the minor method
//m appears to get modified even though I only use m.clone()
public float[][] cofactor(float [][] m)
{
float[][] res = m.clone();
res = this.minor(res)
for(int i = 0; i < m.length; i++)
{
for(int j = 0; j < m.length; j++)
{
res[i][j] = res[i][j]*(int)Math.pow(-1, i + j);
}
}
return res;
}
//The following transpose, adjugate, and inverse methods have the same problem
public float[][] transpose(float[][] m)
{
float[][] res = new float[m.length][m.length];
float temp = 0;
for(int i = 0; i < m.length; i++)
{
for(int j = 0; j < m.length; j++)
{
temp = m[i][j];
res[i][j] = m[j][i];
res[j][i] = temp;
}
}
return res;
}
public float[][] adjugate(float[][] m)
{
float[][] res = this.transpose(this.cofactor(m));
return res;
}
public float[][] inverse(float[][] m)
{
float[][] res = this.mul(this.adjugate(m), (1/this.determinant(m)));
return res;
}
//print out the matrix in square form
public void matrixprint(float [][] m)
{
for(int i = 0; i < m.length; i++)
{
System.out.println("");
for(int j = 0; j < m[i].length; j++){
System.out.print(m[i][j] + " ");
}
}
System.out.println("\n");
}
}
现在是主要 class 和创建 MatrixOperations class 实例并在 2X2 矩阵上使用其方法的主要方法。
public class Main {
public static void main(String[] args) {
MatrixOperations mo = new MatrixOperations();
//Create a 2X2 matrix called "matrix" and set its elements
//Then perform each step on "matrix" and finally test if you have acquired the correct inverse
float [][] matrix = new float[2][2];
matrix[0][0] = 2;
matrix [0][1] = 5;
matrix [1][0] = 4;
matrix [1][1] = 3;
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Minor = ");
mo.matrixprint(mo.minor(matrix));
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Cofactor = ");
mo.matrixprint(mo.cofactor(matrix));
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Adjugate = ");
mo.matrixprint(mo.adjugate(matrix));
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Determinant = ");
System.out.println(mo.determinant(matrix));
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Inverse = ");
mo.matrixprint(mo.inverse(matrix));
System.out.println("Matrix = ");
mo.matrixprint(matrix);
System.out.println("Identity = ");
mo.matrixprint(mo.mul(mo.inverse(matrix), matrix));
}
}
现在你会看到,当我显示输出时,每次我在"matrix"上使用方法并重新打印"matrix",即使我的方法也被修改了"matrix"只使用 "matrix" 的副本而不是 "matrix" 本身。
输出:
Matrix =
2.0 5.0
4.0 3.0
Minor =
3.0 4.0
5.0 2.0
Matrix =
3.0 4.0
5.0 2.0
Cofactor =
3.0 -4.0
-5.0 2.0
Matrix =
3.0 -4.0
-5.0 2.0
Adjugate =
3.0 5.0
4.0 2.0
Matrix =
3.0 4.0
5.0 2.0
Determinant =
-14.0
Matrix =
3.0 4.0
5.0 2.0
Inverse =
-0.21428573 0.35714287
0.2857143 -0.14285715
Matrix =
3.0 -4.0
-5.0 2.0
Identity =
0.1479592 0.1479592
0.12244898 0.12244898
任何 help/explanation 为什么会发生这种情况,我们将不胜感激。
这一行做一个浅层克隆;
float[][] res = m.clone();
这将复制 res
,它是对数组的引用的数组。但不是 res
指向的任何数组。很可能你想要的是
float[][] res = new float[m.length][];
for (int i = 0; i < m.length; i++)
res[i] = m[i].clone();
这是因为您在 MatrixOperations
class 的方法中传递了 matrix
对象的引用。它不是 matrix
对象的副本。
来自Java doc:
Reference data type parameters, such as objects, are also passed into methods by value. This means that when the method returns, the passed-in reference still references the same object as before.
二维数组就是数组的数组。
clone()
对数组进行浅表克隆。
所以你有一个新的克隆外部数组,但它引用相同的条目(内部数组)。
克隆外部数组后,遍历外部数组并克隆所有内部数组以获得深度克隆。