【问题标题】:find unique matrices from a larger matrix从更大的矩阵中找到唯一的矩阵
【发布时间】:2013-11-12 16:29:51
【问题描述】:

我是函数式编程的新手,所以我正在做一些练习。我想写一个函数,给定一个独特的自然矩阵,比如说 5x5,返回一个较小尺寸的独特矩阵的集合,比如说 3x3,其中矩阵必须是完整的,即从原始中相邻的值创建。

01 02 03 04 05
06 07 08 09 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25

简单。只需 3 个一组一个接一个地滑过,然后向下滑动,得到如下所示的东西:

01 02 03 | 02 03 04 | 03 04 05 | 06 07 08
06 07 08 | 07 08 09 | 08 09 10 | 11 12 13
11 12 13 | 12 13 14 | 13 14 15 | 16 17 18

或者,在 Scala 中,

List(List(1, 2, 3), List(6, 7, 8), List(11, 12, 13))
List(List(2, 3, 4), List(7, 8, 9), List(12, 13, 14))
List(List(3, 4, 5), List(8, 9, 10), List(13, 14, 15))
List(List(6, 7, 8), List(11, 12, 13), List(16, 17, 18))

等等等等……

所以我尝试使用 Scala(我选择的语言,因为它允许我从命令式发展到函数式,而且我最近几年一直在使用 Java。

val array2D = "01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25".grouped(3).map(_.trim.toInt).grouped(5)
val sliced = array2D.map(row => row.sliding(3, 1).toList).sliding(3, 1).toList

现在我有了一个可以使用的数据结构,但我没有看到一种功能性的方式。当然,我可以遍历sliced 的每一部分,创建一个var matrix = new ListBuffer[Seq[Int]]() 并强制创建一个包,然后我就完成了。

我想找到一种使用 Scala 的功能性、理想情况下无点方法,但我被难住了。必须有一种方法可以使用 3 或类似的东西进行压缩……我搜索了 ScalaDocs,但似乎无法弄清楚。

【问题讨论】:

    标签: scala functional-programming pointfree


    【解决方案1】:

    你已经成功了一半。事实上,我很难弄清楚如何去做你已经做过的事情。我对您的代码进行了一些分解,以使其更易于理解。另外,我将array2D 设为List,这样我可以更轻松地使用代码。 :-)

    val input = "01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25"
    val intArray = (input split " " map (_.toInt) toList)
    val array2D = (intArray grouped 5 toList)
    val sliced = array2D.map(row => row.sliding(3, 1).toList).sliding(3, 1).toList
    

    好的,所以你有一堆列表,每个列表有点像这样:

    List(List(List( 1,  2,  3), List( 2,  3,  4), List( 3,  4,  5)), 
         List(List( 6,  7,  8), List( 7,  8,  9), List( 8,  9, 10)), 
         List(List(11, 12, 13), List(12, 13, 14), List(13, 14, 15)))
    

    你希望他们像这样:

    List(List(List(1, 2, 3), List(6, 7,  8), List(11, 12, 13)), 
         List(List(2, 3, 4), List(7, 8,  9), List(12, 13, 14)), 
         List(List(3, 4, 5), List(8, 9, 10), List(13, 14, 15)))
    

    你觉得这对吗?三个子列表中的每一个都是一个矩阵:

    List(List(1, 2, 3), List(6, 7,  8), List(11, 12, 13))
    

    01 02 03
    06 07 08
    11 12 13
    

    所以,基本上,你想转置它们。那么下一步就是:

    val subMatrices = sliced map (_.transpose)
    

    那个东西的类型是List[List[List[Seq[Int]]]]。让我们考虑一下... 2D矩阵是由一个序列的一个序列来表示的,所以List[Seq[Int]]对应一个矩阵。比方说:

    type Matrix = Seq[Seq[Int]]
    val subMatrices: List[List[Matrix]] = sliced map (_.transpose)
    

    但是您想要一个矩阵列表,因此您可以将其展平:

    type Matrix = Seq[Seq[Int]]
    val subMatrices: List[Matrix] = (sliced map (_.transpose) flatten)
    

    但是,唉,map 加上 flatten 就是 flatMap

    type Matrix = Seq[Seq[Int]]
    val subMatrices: List[Matrix] = sliced flatMap (_.transpose)
    

    现在,您需要唯一的子矩阵。这很简单:它是一个集合。

    val uniqueSubMatrices = subMatrices.toSet
    

    或者,如果您希望将结果保留为序列,

    val uniqueSubMatrices = subMatrices.distinct
    

    就是这样。完整代码只是为了说明:

    type Matrix = Seq[Seq[Int]]
    val input = "01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25"
    val intArray = (input split " " map (_.toInt) toList)
    val array2D: Matrix = (intArray grouped 5 toList)
    val sliced: List[List[Matrix]] = (array2D map (row => row sliding 3 toList) sliding 3 toList)
    val subMatrices: List[Matrix] = sliced flatMap (_.transpose)
    val uniqueSubMatrices: Set[Matrix] = subMatrices.toSet
    

    它可以写成一个表达式,但除非你把它分解成函数,否则读起来会很糟糕。而且您要么必须使用正向管道(|>,不在标准库中),要么将这些函数隐式添加到它们作用的类型中,否则无论如何都难以阅读。

    【讨论】:

    • 我不得不更改您的代码 - 它无法编译,因为 sn-p sliced flatMap (_.transpose) 不返回 Seq[Seq[Int]]Matrix 类型。
    • @arcticpenguin 不,它返回一个List[Seq[Seq[Int]]],这是我在代码中使用的类型。它返回一个矩阵列表,而不是一个矩阵。所以我看不出你在说什么改变。
    • 你必须在intArray 上说toList 否则你会得到一个List[List[Array[Int]]],根据2.8.1 与List[Seq[Seq[Int]]] 不兼容
    • @articpenguin 我明白了。很好奇,因为我已经测试编译了整个代码。我想知道当时我的环境有什么不同。
    【解决方案2】:

    编辑:好的,我想我终于明白你想要什么了。我将展示一种有效的方式,而不是一种高性能的方式。 (这通常是可变的类似 Java 的解决方案,但您已经知道如何做到这一点。)

    首先,你真的,真的应该用你自己的 2D 合集来做这件事。使用一堆 1D 集合来模拟 2D 集合会导致不必要的混乱和复杂化。不要这样做。真的。这是个坏主意。

    但是,好吧,我们还是这样做吧。

    val big = (1 to 25).grouped(5).map(_.toList).toList
    

    这是您想要的整个矩阵。接下来,

    val smaller = (for (r <- big.sliding(3)) yield r.toList).toList
    

    是您想要的行组。现在,您应该一直在使用 2D 数据结构,因为您想做一些不能很好地映射到 1D 操作的事情。但是:

    val small = smaller.map(xss =>
      Iterator.iterate(xss.map(_.sliding(3)))(identity).
        takeWhile(_.forall(_.hasNext)).
        map(_.map(_.next)).
        toList
    ).toList
    

    如果你小心地把它拆开,你会发现你正在创建一堆迭代器 (xss.map(_.sliding(3))),然后通过一步一步地保持这些相同的迭代器来锁定迭代它们,至少在其中一个是空的,并将它们映射到它们的下一个值(这就是你与它们一起前进的方式)。

    现在您已经获得了矩阵,您可以随意存储它们。就个人而言,我会将列表展平:

    val flattened = small.flatten
    

    您编写了一个矩阵并排的结构,您也可以付出一些努力(同样,因为从 1D 操作创建 2D 操作并不总是那么简单):

    val sidebyside = flattened.reduceRight((l,r) => (l,r).zipped.map(_ ::: _))
    

    (注意 reduceRight 使它成为 O(n) 操作而不是 O(n^2)——加入长累加列表的末尾是一个坏主意——但还要注意,如果矩阵太多,这可能会溢出堆栈)。

    【讨论】:

    • 当我说完整时,我的意思是横跨和向下相邻 3。我展示的示例只是答案中的前三个矩阵,滑过 5x5 原始矩阵。
    • 我调整了原来的问题来澄清
    • @arcticpenguin - 好的,这有帮助。但是现在我不知道您要对生成的数据结构做什么
    • 一旦我得到它,我很好。我想将每个结果矩阵传递给另一个函数,但那部分我已经解决了。
    • @arcticpenguin - 那么sliceduniquely 不是你想要的吗? (或者slicedflat,如果你知道你的子矩阵是唯一的?)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多