私はRで正方行列を使用しています。これをmat
と呼び、対角要素の合計を最大化するように列を並べ替える(つまり、順序を変更する)ことができます。これを線形計画法、つまり最適化パッケージlpSolveに依存して実行したいと考えています。コードソリューションは当然のことながら評価されますが、それが失敗した場合は、線形計画問題としてそれを定式化する助けがあれば評価されます。
私の質問はこれに似ています: 対角線を最大化するために正方形の2方向分割表(行列)の列を並べ替えます 。ただし、その質問、および私がSOで見つけた他の質問では、その行の対角要素を最大化して行方向に進むことで十分であると考えられています。問題は、
mat2 <- mat[,max.col(mat, 'first')]
私にとってはうまくいきません:行に複数の等しい最大値がある場合、または(たとえば)行Xで10ではなく対角線上で11を選択するが、結果として行X + 1で強制される場合があります。 30は10と同じ列の一部だったので、30ではなく対角線上に5。
これを行うためのハンガリーアルゴリズムと呼ばれるアルゴリズムがあることは知っていますが、lpSolveを除いて、このチャレンジにはパッケージを使用できません。
行列A
の列置換は、行列乗算AP
に対応します。ここで、P
は置換行列(置換された単位行列)です。したがって、次の数学モデルを定式化できます。
最初の制約は_Y=AP
_です。 P
の制約により、P
が適切な順列行列(各行と列に1つずつ)であることを確認してください。目的は、列置換行列Y
のトレースを最大化します(行列のトレースは、対角要素の合計です)。
この定式化をかなり最適化できることに注意してください(すべての_y[i,j]
_と_i<>j
_は使用せず、残りのyを置き換えることができます)。
これを試すRコード:
_library(CVXR)
# random matrix A
set.seed(123)
n <- 10
A <- matrix(runif(n^2,min=-1,max=1),nrow=n,ncol=n)
# decision variables
P <- Variable(n,n,boolean=T)
Y <- Variable(n,n)
# optimization model
# direct translation of the mathematical model given above
problem <- Problem(Maximize(matrix_trace(Y)),
list(Y==A %*% P,
sum_entries(P,axis=1) == 1,
sum_entries(P,axis=2) == 1))
# solve and print results
result <- solve(problem)
cat("status:",result$status)
cat("objective:",result$value)
_
この例では、マトリックスから始めます
_ [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] -0.42484496 0.91366669 0.77907863 0.92604847 -0.7144000 -0.9083377 0.3302304 0.50895032 -0.5127611 -0.73860862
[2,] 0.57661027 -0.09333169 0.38560681 0.80459809 -0.1709073 -0.1155999 -0.8103187 0.25844226 0.3361112 0.30620385
[3,] -0.18204616 0.35514127 0.28101363 0.38141056 -0.1725513 0.5978497 -0.2320607 0.42036480 -0.1647064 -0.31296706
[4,] 0.76603481 0.14526680 0.98853955 0.59093484 -0.2623091 -0.7562015 -0.4512327 -0.99875045 0.5763917 0.31351626
[5,] 0.88093457 -0.79415063 0.31141160 -0.95077263 -0.6951105 0.1218960 0.6292801 -0.04936685 -0.7942707 -0.35925352
[6,] -0.90888700 0.79964994 0.41706094 -0.04440806 -0.7223879 -0.5869372 -0.1029673 -0.55976223 -0.1302145 -0.62461776
[7,] 0.05621098 -0.50782453 0.08813205 0.51691908 -0.5339318 -0.7449367 0.6201287 -0.24036692 0.9699140 0.56458860
[8,] 0.78483809 -0.91588093 0.18828404 -0.56718413 -0.0680751 0.5066157 0.6247790 0.22554201 0.7861022 -0.81281003
[9,] 0.10287003 -0.34415856 -0.42168053 -0.36363798 -0.4680547 0.7900907 0.5886846 -0.29640418 0.7729381 -0.06644192
[10,] -0.08677053 0.90900730 -0.70577271 -0.53674843 0.7156554 -0.2510744 -0.1203366 -0.77772915 -0.6498947 0.02301092
_
これにはtrace(A)=0.7133438
があります。
Y変数には列が入れ替えられています。
_ [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0.92604847 -0.73860862 0.50895032 0.77907863 -0.42484496 0.91366669 -0.5127611 0.3302304 -0.9083377 -0.7144000
[2,] 0.80459809 0.30620385 0.25844226 0.38560681 0.57661027 -0.09333169 0.3361112 -0.8103187 -0.1155999 -0.1709073
[3,] 0.38141056 -0.31296706 0.42036480 0.28101363 -0.18204616 0.35514127 -0.1647064 -0.2320607 0.5978497 -0.1725513
[4,] 0.59093484 0.31351626 -0.99875045 0.98853955 0.76603481 0.14526680 0.5763917 -0.4512327 -0.7562015 -0.2623091
[5,] -0.95077263 -0.35925352 -0.04936685 0.31141160 0.88093457 -0.79415063 -0.7942707 0.6292801 0.1218960 -0.6951105
[6,] -0.04440806 -0.62461776 -0.55976223 0.41706094 -0.90888700 0.79964994 -0.1302145 -0.1029673 -0.5869372 -0.7223879
[7,] 0.51691908 0.56458860 -0.24036692 0.08813205 0.05621098 -0.50782453 0.9699140 0.6201287 -0.7449367 -0.5339318
[8,] -0.56718413 -0.81281003 0.22554201 0.18828404 0.78483809 -0.91588093 0.7861022 0.6247790 0.5066157 -0.0680751
[9,] -0.36363798 -0.06644192 -0.29640418 -0.42168053 0.10287003 -0.34415856 0.7729381 0.5886846 0.7900907 -0.4680547
[10,] -0.53674843 0.02301092 -0.77772915 -0.70577271 -0.08677053 0.90900730 -0.6498947 -0.1203366 -0.2510744 0.7156554
_
trace(Y)=7.42218
があります。これが私たちができる最善の方法です(証明済み)。
これは、すべての順列を調べるブルートフォースメソッドです。大きな行列では受け入れられなくなる可能性があります。
library(RcppAlgos)
n = 5L
set.seed(123L)
mat = matrix(sample(1:10, n^2, TRUE), ncol = n)
mat
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 3 5 5 3 9
#> [2,] 3 4 3 8 3
#> [3,] 10 6 9 10 4
#> [4,] 2 9 9 7 1
#> [5,] 6 10 9 10 7
col_perms = permuteGeneral(n, n)
rows = seq_len(n)
diag_sum = apply(col_perms, 1, function(col) sum(mat[cbind(rows, col)]))
optim_cols = which.max(diag_sum)
mat[cbind(rows, col_perms[optim_cols, ])]
#> [1] 9 8 10 9 10
mat[, col_perms[optim_cols, ]]
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 9 3 3 5 5
#> [2,] 3 8 3 3 4
#> [3,] 4 10 10 9 6
#> [4,] 1 7 2 9 9
#> [5,] 7 10 6 9 10