【问题标题】:Is there a way to compute permutations in bash?有没有办法在 bash 中计算排列?
【发布时间】:2022-01-14 09:16:53
【问题描述】:

我有多个文件(其中近 1000 个)用空格分隔,我需要仅使用第二列在每个文件之间使用连接函数计算排列。重要的是不能重复比较,这就是排列的原因。

例如,一个有 3 个文件 A.txt B.txtC.txt

的小例子

总体思路是得到A B比较,A CB C既不是 B A 也不是 C A 也不是 C B

101 代码是

join -1 2 -2 2 A.txt B.txt | cut -d ' ' -f1 > AB.txt
join -1 2 -2 2 A.txt C.txt | cut -d ' ' -f1 > AC.txt
join -1 2 -2 2 B.txt C.txt | cut -d ' ' -f1 > BC.txt

有没有办法为数千个文件完成此操作?我尝试使用 for 循环,但我的大脑已经烤焦了,现在我正在尝试使用 while 循环。但我最好先了解一下方向。

【问题讨论】:

  • 考虑使用您尝试过的forwhile 循环代码更新问题;这可能会让我们更好地了解您想要完成的工作
  • 所以,你想将第一个文件与所有其他文件进行比较,然后将第二个文件与所有其他文件进行比较,除了第一个文件,然后......这很容易实现,但你知道有多少对它会产生吗?如果您有 1000 个文件,这将是 999 + 998 + ... + 1,即 999000 / 2 = 499500。确定这是您想要的吗?
  • 如果你有A,B,C,D,结果应该是什么?您是在寻找集合中的独特对,还是其他?

标签: bash for-loop while-loop comparison


【解决方案1】:

由于迭代次数非常大,性能成为一个问题。这是 Matty 答案的优化版本,使用数组将迭代次数除以 2(半百万而不是一百万)并避免测试:

declare -a files=( *.txt )
declare -i len=${#files[@]}
declare -i lenm1=$(( len - 1 ))
for (( i = 0; i < lenm1; i++ )); do
  a="${files[i]}"
  ab="${a%.txt}"
  for (( j = i + 1; j < len; j++ )); do
    b="${files[j]}"
    join -1 2 -2 2 "$a" "$b" | cut -d ' ' -f1 > "$ab$b"
  done
done

但请考虑一下,bash 并不是为这种具有 50 万次迭代的密集任务而设计的。可能有更好(更有效)的方式来完成您想要的。

【讨论】:

  • 使用len=${#files[@]} before 第一个循环,您将获得性能提升,并使用i &lt; len - 1j &lt; len 作为循环条件。在 bash 中查找数组的长度非常昂贵。
  • @glennjackman 谢谢,我编辑了我的答案以纳入你的改进,加上两个小改进(不是 100% 肯定 len - 1 会在最外层循环的每次迭代中重新评估,但预先计算它不会'不疼...)
【解决方案2】:

看起来你所追求的可以通过两个嵌套的 for 循环和一个字典比较来保持字母顺序?

# prints pairs of filenames
for f in dir/*; do
    for g in dir/*; do
        if [[ "$f" < "$g" ]]; then  # ensure alphabetical order
            echo $f $g
        fi
    done
done

【讨论】:

    【解决方案3】:

    这就是您不想为此使用 bash 的原因:

    先创建1000个文件

    seq 1000 | xargs touch
    

    现在,与 bash 不同的对

    time {
        files=(*)
        len=${#files[@]}
        for ((i=0; i<len-1; i++)); do
            a=${files[i]}
            for ((j=i+1; j<len; j++)); do
                b=${files[j]}
                echo "$a $b"
            done
        done >/dev/null
    }
    
    real    0m5.091s
    user    0m4.818s
    sys     0m0.262s
    

    例如,与 perl 中的相同:

    time {
        perl -e '
            opendir my $dh, ".";
            my @files = sort grep {$_ != "." && $_ != ".."} readdir $dh;
            closedir $dh;
            for (my $i = 0; $i < @files - 1; $i++) {
                my $a = $files[$i];
                for (my $j = $i + 1; $j < @files; $j++) {
                    my $b = $files[$j];
                    print "$a $b\n";
                }
            }
        ' > /dev/null 
    }
    
    real    0m0.131s
    user    0m0.120s
    sys     0m0.006s
    

    【讨论】:

    • 所以建议在 Perl 或 Python 中做这类事情,它们变得更加优化
    • bash 很慢。 Shell 通常用于粘合外部程序的 IO。 bash 具有通用编程功能,但它不是用于复杂计算或数据结构的好语言。
    猜你喜欢
    • 2018-02-24
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    • 2012-10-16
    • 1970-01-01
    • 2020-04-16
    • 2020-05-11
    • 2016-12-01
    相关资源
    最近更新 更多