Closest Number from a Range of Numbers

out of a range of numbers, how can i pick out the number that is the closest to any arbitrary/random number that a user supplies?

say the range of numbers are between 1 - 90000. but that doesn't mean each number exist between 1 - 90000. the range of numbers could be for example:

1, 3, 4, 6, 10, 30, 48, 49, 50, 51......89999, 90000

As you can see, the numbers are in order from smallest to greatest, however, some numbers are missing. Say a user provides a number that is missing in the range of numbers. How do I grab the closest number in that range to what the user provided?

like say the user provided the number 39. From looking at the above range, you can see that the number 35 doesn't exist. So, how do I know to pick the closest number to 39 that does exist in the range? The closest numbers to 39, in the above range, are 30 and 48. How can I do this in a bash shell script?

I hope i'm making sense to you guys.

A typical task for binary search. But if you don't care about efficiency this works (GNU grep and bash):

nums='1, 3, 4, 6, 10, 30, 48, 49, 50, 51, 89999, 90000'

num=39

echo $nums, $num | tr , '\n' | sort -n | grep -C1 $num | xargs |             
  read a b c d; if ((b-a <= c-b));then echo $a; else echo $c; fi
 awk -v VAL=5 -v RS="," '{
        D=(VAL - $1) * (VAL - $1);
        if((!SET) || (D < DIFF))
        {
                DIFF=D;
                X=$1;
                SET=1;
        }
}

END {   printf("Closest was %s\n", X);  }' < data
1 Like

I just put the logic in for printing out 30, based on user input of 39, work on the logic and you can get it to print 48 or whatever the next number is. Off course the script needs improvement in terms of error checking, checking if user did supply $1 etc., left as an exercise for OP :). It will go up and down 100 numbers only, which can be tweaked.

# cat /tmp/23.sh
#!/usr/bin/bash
>/tmp/results
IFS=" ,"
OFS="\n"
v=$1
for i in `cat /tmp/90`
do
for x in {0..100}
do
t=`echo "$v-$x"|bc`
if [[ $t = $i ]]
then
echo $i >> /tmp/results
fi
done
done
echo "`tail -1 /tmp/results`"

here is the running:

# /tmp/23.sh 9
6

# /tmp/23.sh 39
30

That is a useless use of cat. If the file is too large it may silently op off the end bit before feeding it into your program. Since you're using BASH, you can just do:

while read -d "," V
do
        ...
done < filename
1 Like

thanks everyone. this code here does it. but i have a question, the code provides the closest number that PRECEDED the user provided number of 39.

Can it also provide the closest number that comes AFTER 39? that would be awesome if this can be done. :slight_smile:

thanks a million to everyone who has responded.

Slight modification of it for numbers after:

 awk -v VAL=39 -v RS="," '{
        D=(VAL - $1) * (VAL - $1);
        if((VAL<=$1)&&(((!SET) || (D < DIFF))))
        {
                DIFF=D;
                X=$1;
                SET=1;
        }
}

END {   if(!SET)        print "No greater value found";
        else            printf("Closest was %s\n", X);  }' < data

---------- Post updated at 11:52 AM ---------- Previous update was at 11:46 AM ----------

This will do both:

awk -v VAL=39 -v RS="," '{
        D=(VAL - $1) * (VAL - $1);

        if((VAL<=$1)&&(((!SETA) || (D < DIFFA))))
        {
                DIFFA=D;
                XA=$1;
                SETA=1;
        }

        if((VAL>=$1)&&(((!SETB) || (D < DIFFB))))
        {
                DIFFB=D;
                XB=$1;
                SETB=1;
        }
}

END {   if(!SETA)       print "No greater bound";
        else            printf("Closest greater bound was %s\n", XA);

        if(!SETB)       print "No lesser bound";
        else            printf("Closest lesser bound was %s\n", XB);   }' < data

It finds 48 and 30 respectively.

1 Like