Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cartesian product of two lists

Given a map where a digit is associated to several characters

scala> val conversion = Map("0" -> List("A", "B"), "1" -> List("C", "D"))
conversion: scala.collection.immutable.Map[java.lang.String,List[java.lang.String]] =
  Map(0 -> List(A, B), 1 -> List(C, D))

I want to generate all possible character sequences based on a sequence of digits. Examples:

"00" -> List("AA", "AB", "BA", "BB")
"01" -> List("AC", "AD", "BC", "BD")

I can do this with for comprehensions

scala> val number = "011"
number: java.lang.String = 011

Create a sequence of possible characters per index

scala> val values = number map { case c => conversion(c.toString) }
values: scala.collection.immutable.IndexedSeq[List[java.lang.String]] =
  Vector(List(A, B), List(C, D), List(C, D))

Generate all the possible character sequences

scala> for {
     | a <- values(0)
     | b <- values(1)
     | c <- values(2)
     | } yield a+b+c
res13: List[java.lang.String] = List(ACC, ACD, ADC, ADD, BCC, BCD, BDC, BDD)

Here things get ugly and it will only work for sequences of three digits. Is there any way to achieve the same result for any sequence length?

like image 337
Mark Jayxcela Avatar asked Nov 21 '11 19:11

Mark Jayxcela


People also ask

What is the Cartesian product of two sets?

In mathematics, the Cartesian Product of sets A and B is defined as the set of all ordered pairs (x, y) such that x belongs to A and y belongs to B. For example, if A = {1, 2} and B = {3, 4, 5}, then the Cartesian Product of A and B is {(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5)}.

What is a Cartesian product in Python?

The Cartesian product of two lists is the set containing all ordered pairs (x, y) such that x belongs to the first list and y belongs to the second. For example, the Cartesian product of [1, 2] and ["a"] is [(1, "a"), (2, "a")] .

Where is Cartesian product used in real life?

Most computers display images as a raster of points called pixels that can be addressed by their coordinates. These coordinates are ordered pairs and hence elements of a Cartesian product.


1 Answers

The following suggestion is not using a for-comprehension. But I don't think it's a good idea after all, because as you noticed you'd be tied to a certain length of your cartesian product.

scala> def cartesianProduct[T](xss: List[List[T]]): List[List[T]] = xss match {
     |   case Nil => List(Nil)
     |   case h :: t => for(xh <- h; xt <- cartesianProduct(t)) yield xh :: xt
     | }
cartesianProduct: [T](xss: List[List[T]])List[List[T]]

scala> val conversion = Map('0' -> List("A", "B"), '1' -> List("C", "D"))
conversion: scala.collection.immutable.Map[Char,List[java.lang.String]] = Map(0 -> List(A, B), 1 -> List(C, D))

scala> cartesianProduct("01".map(conversion).toList)
res9: List[List[java.lang.String]] = List(List(A, C), List(A, D), List(B, C), List(B, D))

Why not tail-recursive?

Note that above recursive function is not tail-recursive. This isn't a problem, as xss will be short unless you have a lot of singleton lists in xss. This is the case, because the size of the result grows exponentially with the number of non-singleton elements of xss.

like image 50
ziggystar Avatar answered Oct 19 '22 13:10

ziggystar