【问题标题】:Help with algorithm for compute columns sum of a (quadtree) matrix?帮助计算(四叉树)矩阵的列总和的算法?
【发布时间】:2011-02-08 03:33:56
【问题描述】:

给定这个定义和一个测试矩阵:

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

任何想法都会很棒,非常感谢......

【问题讨论】:

  • 为什么后两列的和是 21?他们不应该是 18 岁吗?
  • @drvitek:你说得对,谢谢。想法?
  • 看来您在这里问了很多关于 SO 的问题,这些问题都是作业问题。也许问一个同学会是一个更好的解决方案,因为他们会遇到同样的问题?

标签: algorithm haskell matrix quadtree


【解决方案1】:

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

关于第一个答案中提出的解决方案,好的。然而,构造和解构 Mat 是毫无用处的,这可以很容易地避免。此外,调用 fromIntegral 以“绕过”来自使用复制的类型检查问题可以得到解决,而无需强制先进入 Integral 然后再返回,例如

让 m = 2^n; k=2^n 复制 k (m*x)

无论如何,这里的挑战是避免由于 ++ 导致的二次行为,这正是我所期望的。

干杯,

【讨论】:

    【解决方案2】:

    让我们考虑一下您的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 的部分和列表连接起来,然后您的函数应该开始工作了。

    【讨论】:

    • 需要明确的是,如果您向他提供了提示,向他展示了他的编译错误的来源以及如何纠正它(除了为他重写他的代码),向他提出引导性问题,或给出有细节要填写的草图,我会投票 +1。
    • 这似乎并不能帮助任何人从中学习。我不确定你在用基本情况做什么。词多,代码少?
    • 我更新了答案:删除了完整的工作代码并解释了@Gremo 的错误在哪里。附言就个人而言,我总是发现解释的例子是更好的学习方法,然后是模糊的暗示,假设我知道一些我不知道的东西。因此,如果一个人要求答案,对我来说这意味着他想要答案(这里:解决任务的代码,这是完成他的任务所必需的)。
    【解决方案3】:

    让我们更笼统地说,回到手头的目标。

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

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

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

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

      (提示:在 hoogle 上查看 replicategenericReplicate)。

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

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

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

    现在,我们只关心一个特定的投影——投影到大小为 2dd×2d 的矩阵上 其中 d 是我们的四叉树的深度。所以你也需要计算深度。由于一个 单个单元格“自然地”适合大小为 1×1 的矩阵,这意味着它具有 深度为 0。四边形的深度必须足够大,以允许其每个子四边形适合 进入矩阵的象限。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-05
      • 2014-01-17
      • 1970-01-01
      • 2011-03-30
      • 2015-05-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多