找到最接近的前 5 个,而不仅仅是最接近的?

Find the top 5 closest rather than just the closest?

如何在一列数字中找到最接近 $VariableNumber 的 5 个数字?

例如,如果 $VariableNumber = 30 那么:
输入文件示例:

50
100
70
40
20
10
65
41
92

示例输出:

20
40
41
10
50

之前有人在其他地方发布过一个答案,在特定行的特定列中找到与给定值最接近的数字匹配,如下所示:

awk -v col_num="3" -v value="$Number" '
    func abs(x) { return (x<0) ? -x : x }

    {
        distance = abs($col_num - value)
    }
    NR==1 || distance<shortest_distance {
        shortest_distance = distance
        nearest_value = $col_num
    }
    END {
        print nearest_value
    }
'

但是一直没能适应

我只是按到 n 的距离对条目进行排序,select 第一个像:

awk -v n=30 '
  function abs(x) {return x < 0 ? -x : x}
  {print abs([=10=] - n) "\t" [=10=]}' < file |
  sort -n |
  head -n 5 |
  cut -f 2-

一如既往,很好; 简单明了。 但是,如果您真的 真的 想完全在 awk 内完成, 并且你有 GNU awk (a.k.a. gawk),你可以这样做:

awk -v t="$VariableNumber" '
       {
            d =  - t
            if (d < 0) d = -d
            e = d "#" 
            if (NR <= 5) {
                   a[NR] = e
            } else {
                   a[5+1] = e
                   asort(a, a, "@val_num_asc")
                   delete a[5+1]
            }
       }
  END  {
            print "---"
            if (NR <= 5) asort(a, a, "@val_num_asc")
            for (i in a) { gsub(".*#", "", a[i]); print a[i]; }
       }
'

对于每个输入值, 这将 d 计算为绝对 d 差异 在该值和 t 目标值之间, t(在命令行上设置为 $VariableNumber 的值, 根据您的示例,这可能是 30)。 然后构造数组entry,e, 组成的差异, 与 # 和原始数字连接。 然后将此数组条目添加到 array a

前五个输入值简单地放入数组元素 1 到 5。

之后,将每个数字追加到数组中 通过放入元素 6。然后对数组进行排序。 由于数组条目以 d 差异值开头, 接近 t 目标的数字 (d差异值较低) 被排序到数组的开头, 和远离 target 的数字 被排序到数组的末尾。 (指定 "@val_num_asc" 将值排序为 数字 而不是字符串。 如果没有这个,1020 的差异将排序在 34 之下。) 然后删除第6个元素(距离目标最远的那个)。

最后(到达数据的末尾),我们

  • 检查记录数是否≤5。 如果是,则对数组进行排序, 因为它仍然是输入数据的顺序。 (可以说,这一步是可选的。)
  • 对于每个元素都是数组, 去掉差异和 # 通过搜索正则表达式 .*# 并替换 (gsub) 什么。 然后打印原始值。

显然,如果您想查看第一列以外的列, 您可以更改脚本中出现的所有 </code>。 (您在问题中显示的脚本 演示了如何允许在 运行 时指定列号。) 而且,如果你想要一些不是最接近的五个数字, 只需更改 <code>5 的所有外观即可。 (我本可以在第 9 行和第 11 行中提到 a[6]; 我写 a[5+1] 是为了方便简单的参数化。)

另一个,适用于所有 awk(使用 gawk、mawk、Debian 的原始 awk 和 Busybox awk 测试):

$ awk -v v=30 -v n=5 '               # v is the central value, 
function abs(d) {                    # n is the number of wanted values
    return (d<0?-d:d)                # d is distance, c array index, va value array
}                                    # da distance array, max is greatest of smallest
((d=abs(v-))<max) && c==n {        # if we find distance < max of top n smallest
    da[maxc]=d                       # replace max in distance array
    va[maxc]=                      # and in value array
    max=0                            # set max to min to find new max distance
    for(ct in da)
        if(da[ct]>max) {             # find the new max in the top n smallest
            max=da[ct]
            maxc=ct
        }
    if(max==0)                       # if max is 0, all are 0s so might as well exit
        exit
    next
}
c<n {                                # we need n first distances
    da[++c]=d                        # fill distance array with them
    va[c]=                         # related values to value array
    if(d>max) {                      # look for max 
        max=d
        maxc=c
    }
}
END {                                # in the end or exit
    for(c in va)                     # get all values in value array
        print va[c]                  # and output them
}' file

输出(排名不分先后,数组实现相关):

50
10
41
40
20

执行时间是线性的,最坏的情况是值数组的大小乘以记录数,所以仍然是线性的(对吗?:)。