【问题标题】:Optimally finding the index of the maximum element in BASH array优化查找 BASH 数组中最大元素的索引
【发布时间】:2020-09-23 09:42:24
【问题描述】:

我正在使用 bash 来即时处理软件响应,我正在寻找一种方法来找到 数组中最大元素的索引。

输入到 bash 脚本的数据是这样的:

25 9
72 0
 3 3
 0 4
 0 7

所以我创建了两个数组。有

arr1 = [ 25 72 3 0 0 ]
arr2 = [ 9   0 3 4 7 ]

我需要在 arr1 中找到最大数的索引,以便将它也用于 arr2。 但我想看看是否有一种快速 - 最佳的方法来做到这一点。

对我拥有的数据使用字典结构 [key][value] 会更好吗?这会让这个过程更容易吗?

我还找到了[1](来自用户 jhnc),但我不认为这是我想要的。

我的蛮力方法如下:


function MAX {

   arr1=( 25 72 3 0 0 )
   arr2=( 9   0 3 4 7 )

   local indx=0
   local max=${arr1[0]}
   local flag

   for ((i=1; i<${#arr1[@]};i++)); do

       #To avoid invalid arithmetic operators when items are floats/doubles
       flag=$( python <<< "print(${arr1$[${i}]} > ${max})")    

       if [ $flag == "True" ]; then

           indx=${i}
           max=${arr1[${i}]}

       fi

    done

    echo "MAX:INDEX = ${max}:${indx}"
    echo "${arr1[${indx}]}"
    echo "${arr2[${indx}]}"

}

这种方法显然可行,但是,它是最优的吗?有没有更快的方法来执行任务?

arr1 = [ 99.97 0.01 0.01 0.01 0 ]
arr2 = [ 0 6 4 3 2 ]

在这个例子中,如果一个数组包含浮点数,那么我会得到一个

语法错误:算术运算符无效(错误标记为“.97)”

所以,我正在使用

flag=$( python <<< "print(${arr1$[${i}]} > ${max})")

为了解决这个问题。

【问题讨论】:

  • 我会用 Python 编写整个脚本。数据结构和浮点数并不是 Bash 的强项。
  • @JohnKugelman True... 我使用 bash 是因为它给了我一些灵活性 w.r.t. python ( + os 包) 来处理正在运行的软件,直到我不得不处理这个问题......
  • 这能回答你的问题吗? Bash Script Binary Search
  • @ceving 在我的情况下,不幸的是,我必须处理浮点数,这使得这个解决方案没有那么有用。 @chepner 的 awk 下面的建议就像一个魅力!

标签: bash


【解决方案1】:

找到最大值本质上是一个 O(n) 操作。但是没有必要在每次迭代时生成 Python 进程来执行比较。改为编写一个 awk 脚本。

awk 'BEGIN {
   split(ARGV[1], a1);
   split(ARGV[2], a2);
   max=a1[1];
   indx=1;
   for (i in a1) {
     if (a1[i] > max) {
       indx = i;
       max = a1[i];
     }
   }
   print "MAX:INDEX = " max ":" (indx - 1)
   print a1[indx]
   print a2[indx]
}' "${arr1[*]}" "${arr2[*]}"

两个 shell 数组作为空格分隔的字符串传递给 awk,后者将它们拆分回 awk 数组。

【讨论】:

  • 在本例中,awk 脚本输出MAX:INDEX = 99.97:1 \n 99.97 \n 0。但是为什么索引是1?不应该是 0 吗?
  • split 生成的数组从 1 开始索引。第一个 print 语句可能应该更新为输出 indx - 1 而不是 indx
【解决方案2】:

如果您确实需要比较浮点数,则很难有效地做到这一点。 Bash 不能做浮点数,这意味着为每个数字比较调用一个外部程序。但是,不一定需要比较 bash 中的每个数字。

这是一个快速、纯 bash、仅整数的解决方案,使用比较:

#!/bin/bash

arr1=( 25 72 3 0 0)
arr2=( 9   0 3 4 7)

# Get the maximum, and also save its index(es)
for i in "${!arr1[@]}"; do
        if ((arr1[i]>arr1_max)); then
                arr1_max=${arr1[i]}
                max_indexes=($i)
        elif [[ "${arr1[i]}" == "$arr1_max" ]]; then
                max_indexes+=($i)
        fi
done

# Print the results
printf '%s\n' \
        "Array1 max is $arr1_max" \
        "The index(s) of the maximum are:" \
        "${max_indexes[@]}" \
        "The corresponding values from array 2 are:"

for i in "${max_indexes[@]}"; do
        echo "${arr2[i]}"
done

这是另一种可以处理浮点数的最佳方法。完全避免了 bash 中的比较。相反,使用更快的sort(1),并且只需要一次。而不是为每个数字启动一个新的 python 实例。

#!/bin/bash

arr1=( 25 72 3 0 0)
arr2=( 9  0 3 4 7)

arr1_max=$(printf '%s\n' "${arr1[@]}" | sort -n | tail -1)
for i in "${!arr1[@]}"; do
        [[ "${arr1[i]}" == "$arr1_max" ]] &&
        max_indexes+=($i)
done

# Print the results
printf '%s\n' \
        "Array 1 max is $arr1_max" \
        "The index(s) of the maximum are:" \
        "${max_indexes[@]}" \
        "The corresponding values from array 2 are:"

for i in "${max_indexes[@]}"; do
        echo "${arr2[i]}"
done

示例输出:

Array 1 max is 72
The index(s) of the maximum are:
1
The corresponding values from array 2 are:
0

除非您需要这些数组,否则您也可以将输入脚本直接输入到如下内容:

#!/bin/bash

input-script |
sort -nr |
awk '
(NR==1) {print "Max: "$1"\nCorresponding numbers:"; max = $1}
{if (max == $1) print $2; else  exit}'

示例(带有一些额外的数字):

$ echo \
'25 9
72 0
72 11
72 4
 3 3
 3 14
 0 4
 0 1
 0 7' |
sort -nr |
awk '(NR==1) {max = $1; print "Max: "$1"\nCorresponding numbers:"}
{if (max == $1) print $2; else  exit}'
Max: 72
Corresponding numbers:
4
11
0

你也可以在awk中做到100%,包括排序:

$ echo \
'25 9
72 0
72 11
72 4
 3 3
 3 14
 0 4
 0 1
 0 7' |
awk '
{
    col1[a++] = $1
    line[a-1] = $0
}

END {
    asort(col1)
    col1_max = col1[a-1]
    print "Max is "col1_max"\nCorresponding numbers are:"

    for (i in line) {
        if (line[i] ~ col1_max"\\s") {
            split(line[i], max_line)
            print max_line[2]
        }
    }
}'
Max is 72
Corresponding numbers are:
0
11
4

或者,只是为了获得第 1 列的最大值,以及第 2 列中的 any 单个数字,这与它对应。尽可能简单:

$ echo \
'25 9
72 0
 3 3
 0 4
 0 7' |
sort -nr |
head -1
72 0

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-20
    • 1970-01-01
    • 2018-10-17
    • 2021-11-07
    • 2021-07-02
    • 1970-01-01
    • 2019-01-12
    相关资源
    最近更新 更多