帮助算法计算列(四叉树)矩阵的总和?

时间:2011-02-08 03:33:56

标签: algorithm haskell matrix quadtree

鉴于此定义和测试矩阵:

data (Eq a, Show a) => QT a = C a | Q (QT a) (QT a) (QT a) (QT a)
    deriving (Eq, Show)

data (Eq a, Num a, Show a) => Mat a = Mat {nexp :: Int, mat :: QT a}
    deriving (Eq, Show)

-- test matrix, exponent is 2, that is matrix is 4 x 4
test = Mat 2 (Q (C 5) (C 6) (Q (C 1) (C 0) (C 2) (C 1)) (C 3))

|     |     |
|  5  |  6  |
|     |     |
-------------
|1 | 0|     |
|--|--|  3  |
|2 | 1|     |

我正在尝试编写一个输出列总和列表的函数,例如:[13, 11, 18, 18]。基本思想是对每个子四叉树求和:

  • 如果四叉树为(C c),则输出重复2 ^ (n - 1)次值c * 2 ^ (n - 1)示例:第一个四叉树是(C 5)所以我们重复5 * 2^(2 - 1) = 102 ^ (n - 1) = 2次,获得[5,5]。
  • 否则,给定(Q a b c d),我们zipWith a和c(和b和d)的colsum。

当然这是无法正常工作(甚至没有编译),因为经过一些递归我们有:

zipWith (+) [[10, 10], [12, 12]] [zipWith (+) [[1], [0]] [[2], [1]], [6, 6]]

因为我从Haskell开始,我觉得我错过了什么,需要一些关于我可以使用的功能的建议。 不工作 colsum定义是:

colsum :: (Eq a, Show a, Num a) => Mat a -> [a]
colsum m = csum (mat m)
    where
        n = nexp m
        csum (C c)       = take (2 ^ n) $ repeat (c * 2 ^ n)
        csum (Q a b c d) = zipWith (+) [colsum $ submat a, colsum $ submat b]
                                       [colsum $ submat c, colsum $ submat d]
        submat q = Mat (n - 1) q

任何想法都会很棒并且非常感激......

3 个答案:

答案 0 :(得分:2)

可能“某人”应该解释谁担心QuadTree的深度,Matrix类型中的nexp字段确实用于确定(C _)的实际大小。

关于第一个答案中提出的解决方案,确定它有效。然而,构造和解构Mat是没有用的,这很容易避免。此外,调用fromIntegral来“绕过”来自使用复制的类型检查问题可以解决,而不必强迫首先进入Integral然后再回来,比如

设m = 2 ^ n; k = 2 ^ n,重复k(m * x)

无论如何,这里的挑战是避免因++而产生的二次行为,这就是我所期望的。

干杯,

答案 1 :(得分:1)

让我们考虑一下你的colsum

colsum :: (Eq a, Show a, Num a) => Mat a -> [a]
colsum m = csum (mat m)
    where
        n = nexp m
        csum (C c)       = take (2 ^ n) $ repeat (c * 2 ^ n)
        csum (Q a b c d) = zipWith (+) [colsum $ submat a, colsum $ submat b]
                                       [colsum $ submat c, colsum $ submat d]
        submat q = Mat (n - 1) q

除了您定义csum (Q a b c d) = ...的行外,它几乎是正确的。

让我们考虑类型。 colsum返回一个数字列表。 ZipWith (+)以元素方式对两个列表求和:

ghci> :t zipWith (+)
zipWith (+) :: Num a => [a] -> [a] -> [a]

这意味着您需要将两个数字列表传递给zipWith (+)。而是创建两个数字列表列表,如下所示:

[colsum $ submat a, colsum $ submat b]

此表达式的类型为[[a]],而不是[a]

您需要做的是连接两个数字列表以获得一个数字列表(这可能是您打算做的):

((colsum $ submat a) ++ (colsum $ submat b))

同样,您连接cd的部分和的列表,然后您的函数应该开始工作。

答案 2 :(得分:0)

让我们更加普遍,然后回到手头的目标。

考虑我们如何将四叉树投影到2 n ×2 n 矩阵中。我们可能不需要创建此投影来计算其列总和,但这是一个有用的概念。

  • 如果我们的四叉树是单个单元格,那么我们只需用该单元格的值填充整个矩阵。
  • 否则,如果n≥1,我们可以将矩阵划分为象限,并让子季度每个填充一个象限(也就是说,每个子季节填充2 n-1 ×2 < sup> n-1 矩阵)。
  • 请注意,还有一个案例。如果n = 0(也就是说,我们有1×1矩阵)并且四叉树不是单个单元怎么办?我们需要为这种情况指定一些行为 - 也许我们只是让其中一个子季节填充整个矩阵,或者我们用一些默认值填充矩阵。

现在考虑这种投影的列总和。

  • 如果我们的四叉树是单个细胞,那么2 n 列的总和将全部为2 n 乘以存储在该单元格中的值。

    (提示:查看hoogle上的replicategenericReplicate

  • 否则,如果n≥1,则每列重叠两个不同的象限。 我们的一半栏目将完全由西方象限决定, 另一半是东部象限,是特定列的总和 可以定义为该列的贡献总和 从它的北半部(即北部象限中该列的列总和), 和它的南半部(同样)。

    (提示:我们需要将西部列的总和附加到东部列的总和 得到所有列的总和,并结合北部和南部的半柱总和 得到每列的实际总和。)

  • 同样,我们有第三种情况,这里的列总和取决于方式 你将四个子季度投影到1×1矩阵上。幸运的是,1×1矩阵意味着 只有一列总和!

现在,我们只关心一个特定的投影 - 投影到尺寸为2 d d×2 d 的矩阵上 其中d是我们的四叉树的深度。所以你也需要计算深度。既然一个 单个细胞“自然地”适合大小为1×1的矩阵,这意味着它具有一个 一个四分支必须具有足够大的深度,以允许其每个子标准适合 进入矩阵的象限。