Как произвести декартово произведение в bash?

Я хочу создать такой файл (декартово произведение [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

Я могу сделать это, используя вложенный цикл, например:

for i in $(seq 3) 
do
  for j in $(seq 5)
  do
      echo $i $j
  done
done

есть ли решение без циклов?


person Taher Khorshidi    schedule 29.04.2014    source источник


Ответы (3)


Объедините два расширения фигурных скобок!

$ printf "%s\n" {1..3}" "{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

Это работает с использованием одной скобки расширения:

$ echo {1..5}
1 2 3 4 5

а затем в сочетании с другим:

$ echo {1..5}+{a,b,c}
1+a 1+b 1+c 2+a 2+b 2+c 3+a 3+b 3+c 4+a 4+b 4+c 5+a 5+b 5+c
person fedorqui 'SO stop harming'    schedule 29.04.2014
comment
расширяется ли {1..3} оболочкой? - person Taher Khorshidi; 29.04.2014
comment
Да, {1..3} такой же, как seq 3 или seq 1 3, просто он поставляется с оболочкой. - person fedorqui 'SO stop harming'; 29.04.2014
comment
есть ли другие пасты? - person Taher Khorshidi; 29.04.2014
comment
Не то, чтобы я знаю, и я не вижу необходимости. Возможно, вы можете использовать echo {1..3}" "{1..5} | xargs -n 2. - person fedorqui 'SO stop harming'; 29.04.2014
comment
почему, например, echo {1..2}{3..4} производит полное перекрестное произведение вяза 13 14 23 24 , а не 1 23 24 или 13 23 4? - person Jonah; 11.03.2019
comment
Для тех, кто ищет дискретные элементы, например. printf "%s\n" {a,e,z}" "{x,9,u} - person AKX; 19.11.2020

Более короткая (но хакерская) версия ответа Рубенса:

join -j 999999 -o 1.1,2.1 file1 file2

Поскольку поле 999999, скорее всего, не существует, оно считается одинаковым для обоих наборов, и поэтому join приходится производить декартово произведение. Он использует память O(N+M) и выдает на моей машине скорость 100..200 Мб/сек.

Мне не нравится метод «расширения скобок оболочки», такой как echo {1..100}x{1..100} для больших наборов данных, потому что он использует память O (N * M) и может при небрежном использовании поставить вашу машину на колени. Его трудно остановить, потому что ctrl+c не прерывает раскрытие скобок, которое выполняется самой оболочкой.

person legolegs    schedule 06.12.2017
comment
Это наиболее универсальное решение, поскольку оно работает с файлами, а не только с последовательностью чисел. - person kvantour; 05.05.2020

Лучшая альтернатива декартовому произведению в bash, безусловно, - как указывает @fedorqui - использовать расширение параметра. Однако в случае, если ваш ввод нелегко воспроизвести (т. е. если {1..3} и {1..5} недостаточно), вы можете просто использовать join.

Например, если вы хотите выполнить декартово произведение двух обычных файлов, скажем, "a.txt" и "b.txt", вы можете сделать следующее. Сначала два файла:

$ 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

Обратите внимание, что команда sed используется для добавления идентификатора к каждой строке. Идентификатор должен быть одинаковым для всех строк и для всех файлов, поэтому join даст вам декартово произведение вместо того, чтобы отбрасывать некоторые результирующие строки. Итак, join выглядит следующим образом:

$ 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

После того, как оба файла объединены, cut используется в качестве альтернативы для удаления столбца «1», ранее добавленного в начало.

person Rubens    schedule 29.04.2014
comment
то, что вы пишете как соединение, на самом деле является соединением (en.wikipedia.org/ wiki/) мне не нужно присоединяться. то, что я хочу, это декартово произведение (en.wikipedia.org/wiki/Cartesian_product). - person Taher Khorshidi; 29.04.2014
comment
@طاهر Ну, когда вы соединяете каждую строку из одной таблицы со строками из другой таблицы, то есть когда вы делаете перекрестное соединение, ваш вывод представляет собой декартово произведение. - person Rubens; 29.04.2014
comment
Преимущество этого решения заключается в том, что bash по своей сути не позволяет использовать переменные в раскрытии фигурных скобок. Вы можете использовать переменные в раскрытии скобок с помощью eval, но тогда вы используете eval. - person Erik; 22.07.2015