在一列数字中,找到与某个目标值最接近的值

heu*_*cus 2 bash awk

假设我在列中有一些数值数据,例如

11.100000 36.829657 6.101642
11.400000 36.402069 5.731998
11.700000 35.953025 5.372652
12.000000 35.482082 5.023737
12.300000 34.988528 4.685519
12.600000 34.471490 4.358360
12.900000 33.930061 4.042693
13.200000 33.363428 3.738985
13.500000 32.770990 3.447709
13.800000 32.152473 3.169312
Run Code Online (Sandbox Code Playgroud)

我还有一个目标值和一个列索引.给定这组数据,我想找到具有指定索引的列中与目标值最接近的值.

例如,如果我的目标值11.6在列中1,则应输出脚本11.7.如果有两个与目标值等距的数字,则应输出较高的值.

我有一种感觉,awk具有执行此操作的必要功能,但欢迎任何在bash脚本中工作的解决方案.

Ken*_*ent 7

试试这个:

awk -v c=2 -v t=35 'NR==1{d=$c-t;d=d<0?-d:d;v=$c;next}{m=$c-t;m=m<0?-m:m}m<d{d=m;v=$c}END{print v}' file
Run Code Online (Sandbox Code Playgroud)

-v c=2-v t=35可动态值.它们是列idx(c)和目标值(t).在上面的行中,参数是第2列和目标25.它们可以是shell变量.

基于给定输入数据的上述行的输出是:

kent$  awk -v c=2 -v t=35 'NR==1{d=$c-t;d=d<0?-d:d;v=$c;next}{m=$c-t;m=m<0?-m:m}m<d{d=m;v=$c}END{print v}' f
34.988528

kent$  awk -v c=1 -v t=11.6 'NR==1{d=$c-t;d=d<0?-d:d;v=$c;next}{m=$c-t;m=m<0?-m:m}m<d{d=m;v=$c}END{print v}' f
11.700000
Run Code Online (Sandbox Code Playgroud)

编辑

如果有两个与目标值等距的数字,则应输出较高的值

上面的代码没有检查这个要求....下面的代码应该工作:

awk -v c=1 -v t=11.6 '{a[NR]=$c}END{
        asort(a);d=a[NR]-t;d=d<0?-d:d;v = a[NR]
        for(i=NR-1;i>=1;i--){
                m=a[i]-t;m=m<0?-m:m
                if(m<d){
                    d=m;v=a[i]
                }
        }
        print v
}' file
Run Code Online (Sandbox Code Playgroud)

测试:

kent$  awk -v c=1 -v t=11.6 '{a[NR]=$c}END{
        asort(a);d=a[NR]-t;d=d<0?-d:d;v = a[NR]
        for(i=NR-1;i>=1;i--){
                m=a[i]-t;m=m<0?-m:m
                if(m<d){
                    d=m;v=a[i]
                }
        }
        print v
}' f
11.700000
Run Code Online (Sandbox Code Playgroud)

简短的解释.

我不会解释每行代码,它的作用.只是告诉我们做这项工作的想法.

  • 首先读取给定列中的所有元素,保存在数组中
  • 对数组进行排序.
  • 从数组中获取最后一个元素(最大数字).将它分配给var v,并计算它与给定目标之间的差异,将其保存(绝对值)d
  • 从数组循环的第二个元素到第一个元素.如果元素和目标之间的差值(绝对值)小于d,则d用diff 覆盖,也将当前元素保存到v
  • print v,循环后,v就是答案.

一些说明:

  • 有优化逻辑的空间.例如,我们不必循环遍历整个阵列.只是比较d(abs),如果新的diff> d,我们可以停止循环.
  • 由于排序,这个算法是O(nlogn).事实上这个问题可以解决O(n).如果您的输入数据很大,并且情况最糟糕(例如,您的列的范围值500-99999999999,但您的目标是1),您可能希望避免排序.但我认为你的表现不是问题.