I want to produce such file (cartesian product of [1-3]X[1-5]
):
1 1
1 2
1 3
1 4
1 5
2 1
2 2
2 3
2 4
2 5
3 1
3 2
3 3
3 4
3 5
I can do this using nested loop like:
for i in $(seq 3)
do
for j in $(seq 5)
do
echo $i $j
done
done
is there any solution without loops?
Let’s learn it. A Cartesian product of two sets X and Y, denoted X × Y, is the set of all ordered pairs where x is in X and y is in Y. In terms of SQL, the Cartesian product is a new table formed of two tables. If those tables have 3 and 4 lines respectively, the Cartesian product table will have 3×4 lines.
Given two non-empty sets P and Q. The Cartesian product P × Q is the set of all ordered pairs of elements from P and Q, i.e., If either P or Q is the null set, then P × Q will also be empty set, i.e., P × Q = φ
If those tables have 3 and 4 lines respectively, the Cartesian product table will have 3×4 lines. Therefore, each row from the first table joins each row of the second table. You get the multiplication result of two sets making all possible ordered pairs of the original sets’ elements.
(v) The Cartesian product of sets is not commutative, i.e. A × B ≠ B × A (vi) The Cartesian product of sets is not associative, i.e. A × (B × C) ≠ (A × B) × C (vii) If A is a set, then A × ∅ = ∅ and ∅ × A = ∅.
The best alternative for cartesian product in bash is surely -- as pointed by @fedorqui -- to use parameter expansion. However, in case your input that is not easily producible (i.e., if {1..3}
and {1..5}
does not suffice), you could simply use join
.
For example, if you want to peform the cartesian product of two regular files, say "a.txt" and "b.txt", you could do the following. First, the two files:
$ echo -en {a..c}"\tx\n" | sed 's/^/1\t/' > a.txt
$ cat a.txt
1 a x
1 b x
1 c x
$ echo -en "foo\nbar\n" | sed 's/^/1\t/' > b.txt
$ cat b.txt
1 foo
1 bar
Notice the sed
command is used to prepend each line with an identifier. The identifier must be the same for all lines, and for all files, so the join
will give you the cartesian product -- instead of putting aside some of the resultant lines. So, the join
goes as follows:
$ join -j 1 -t $'\t' a.txt b.txt | cut -d $'\t' -f 2-
a x foo
a x bar
b x foo
b x bar
c x foo
c x bar
After both files are joined, cut
is used as an alternative to remove the column of "1"s formerly prepended.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With