【问题标题】:How to produce cartesian product in bash?如何在bash中产生笛卡尔积?
【发布时间】:2014-04-29 11:11:09
【问题描述】:

我想生成这样的文件([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

有没有没有循环的解决方案?

【问题讨论】:

    标签: bash seq


    【解决方案1】:

    合并两个brace expansions!

    $ 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
    

    【讨论】:

    • {1..3} 是否通过 shell 扩展?
    • 是的,{1..3}seq 3seq 1 3 相同,只是它带有shell。
    • 还有其他使用粘贴的吗?
    • 我不知道,我不认为有必要。你也许可以使用echo {1..3}" "{1..5} | xargs -n 2
    • 为什么 echo {1..2}{3..4} 会产生完整的榆树叉积 13 14 23 24,而不是 1 23 2413 23 4
    【解决方案2】:

    鲁本斯答案的较短(但 hacky)版本:

    join -j 999999 -o 1.1,2.1 file1 file2
    

    由于字段 999999 很可能不存在,因此认为这两个集合相等,因此 join 必须进行笛卡尔积。它使用 O(N+M) 内存并在我的机器上以 100..200 Mb/秒的速度产生输出。

    我不喜欢像echo {1..100}x{1..100} 这样用于大型数据集的“shell 大括号扩展”方法,因为它使用 O(N*M) 内存,并且在使用不慎时会使您的机器瘫痪。很难停止,因为 ctrl+c 不会中断由 shell 本身完成的大括号扩展。

    【讨论】:

    • 这是最通用的解决方案,因为它适用于文件,而不仅仅是数字序列
    【解决方案3】:

    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”列的替代方法。

    【讨论】:

    • 你写的加入真的是加入(en.wikipedia.org/wiki/…)我不需要加入。我想要的是笛卡尔积(en.wikipedia.org/wiki/Cartesian_product)。
    • @طاهر 好吧,当您将一个表中的每一行与另一个表中的行连接起来时,即当您执行cross join 时,您的输出是一个笛卡尔积。
    • 这个解决方案的好处是 bash 本质上不允许在大括号扩展中使用变量。您可以使用 eval 在大括号扩展中使用变量,但是您正在使用 eval。
    猜你喜欢
    • 2021-05-06
    • 2016-10-08
    • 2011-12-26
    • 2016-05-07
    • 1970-01-01
    • 2016-04-20
    • 1970-01-01
    • 2015-11-08
    • 1970-01-01
    相关资源
    最近更新 更多