这与问题密切相关:How to map the indexes of a matrix to a 1-dimensional array (C++)?
我需要为带状矩阵中的每个非零元素分配一个可逆索引 在正常的完整矩阵中,很容易做到:
|-------- 5 ---------|
Row ______________________ _ _
0 |0 1 2 3 4 | |
1 |5 6 7 8 9 | 4
2 |10 11 12 13 14| |
3 |15 16 17 18 19| _|_
|______________________|
Column 0 1 2 3 4
要查找数组索引,我们只使用以下双射公式:
matrix[ i ][ j ] = array[ i*m + j ]
在我的例子中,我们有一个对称带状矩阵,对角线的距离有一些约束。例如,以下使用1的上限和下限:
|-------- 5 ---------|
Row ______________________ _ _
0 |0 1 X X X | |
1 |2 3 4 X X | 4
2 |X 5 6 7 X | |
3 |X X 8 9 10| _|_
|______________________|
Column 0 1 2 3 4
在这种情况下,我想为带宽内的每个元素分配索引位置,并忽略外部的所有内容。有两种方法可以做到这一点,其中一种方法是创建所有可接受的索引ix's
的列表,然后使用地图查找在(row,col)
对和a之间快速来回切换奇异指数:
ix's :: [(Int,Int)] -- List of all valid indices
lkup :: Map (Int,Int) Int
lkup = M.fromList $ zip ix's [0..]
rlkup :: Map Int (Int, Int)
rlkup = M.fromList $ zip [0..] ix's
fromTup :: (Int, Int) -> Int
fromTup tup = fromMaybe 0 $ M.lookup tup lkup
toTup :: Int -> (Int, Int)
toTup i = fromMaybe (0,0) $ M.lookup i rlkup
对于大型矩阵,这会导致大量的地图查找,从而导致瓶颈。是否有更有效的公式在有效地址k
和(row,col)
对之间进行转换?
答案 0 :(得分:1)
您可能会发现在矩阵的开头和结尾“浪费”一些索引更为直接,因此请分配:
Row ______________________ _ _
0 (0) |1 2 X X X | |
1 |3 4 5 X X | 4
2 |X 6 7 8 X | |
3 |X X 9 10 11 | _|_
|______________________|
Column 0 1 2 3 4
其中(0)
是被忽略的索引。
这类似于备受推崇的LAPACK库使用的band matrix representation。
在执行可能影响已使用元素的操作时,您只需要注意未使用的元素被正确忽略。 (例如,可以编写快速填充例程而不考虑使用或未使用哪些元素;但矩阵乘法需要更多关注。)
如果采用这种方法,则双射非常简单:
import Data.Char
import Data.Maybe
type Index = Int
-- |(row,col) coordinate: (0,0) is top level
type Coord = (Int, Int)
-- |Matrix dimensions: (rows, cols, edges) where edges gives
-- the count of auxiliary diagonals to *each side* of the main
-- diagonal (i.e., what you call the maximum distance), so the
-- total band width is 1+2*edges
type Dims = (Int, Int, Int)
-- |Get index for (row,col)
idx :: Dims -> Coord -> Index
idx (m, n, e) (i, j) = let w = 1+2*e in w*i+(j-i+e)
-- |Get (row,col) for index
ij :: Dims -> Index -> Coord
ij (m, n, e) idx = let w = 1+2*e
(i, j') = idx `quotRem` w
in (i, j'+i-e)
--
-- test code
--
showCoords :: Dims -> [(Coord, Char)] -> String
showCoords (m, n, _) cs =
unlines $
for [0..m-1] $ \i ->
for [0..n-1] $ \j ->
fromMaybe '.' $ lookup (i,j) cs
where for = flip map
test :: Dims -> IO ()
test dm@(m,n,_) = do
putStrLn $ "Testing " ++ show dm
let idxs = [0..]
-- get valid index/coordinates for this matrix
let cs = takeWhile (\(_, (i,j)) -> i<m || j<n)
$ filter (\(_, (i,j)) -> i>=0 && j>=0)
$ map (\ix -> (ix, ij dm ix)) idxs
-- prove the coordinates are right
putStr $ showCoords dm (map (\(ix, (i,j)) -> ((i,j), chr (ord 'A' + ix))) cs)
-- prove getIndex inverts getCoord
print $ all (\(ix, (i,j)) -> idx dm (i,j) == ix) cs
putStrLn ""
main = do test (4, 5, 1) -- your example
test (3, 8, 2) -- another example