A small script: bsearch-nums

November 21, 2006

Sometimes I write little scripts partly in order to play around with a command I haven't used before. Such is the case with today's script, which I have unimaginatively called bsearch-nums. Here it is:

#!/bin/sh
# usage: bsearch-nums START END

lo=$1; hi=$2
while [ $lo -lt $hi ]; do
  mid=`echo $lo $hi +2/p | dc`
  echo "try: $mid"
  echo -n "u/d? "
  read a || exit 0
  case "$a" in
    [uU+]*) lo=`echo $mid 1+p | dc`;;
    [dD-]*) hi=$mid;;
  esac
done
echo
echo "result: $lo"

Every so often, I find myself running down something where the best way to find the answer is a binary search; today's example was finding out which revision of our aliases file introduced a particular alias. But what usually happens is that only the first few steps are a true binary search and then I resort to jumping around to numbers that look vaguely right, because keeping track of the low and high points and cranking the math gets too annoying.

Enter bsearch-nums, which walks you through the numbers for a manual binary search. You get to supply the actual testing (and then tell the program whether the search should go up or down at each step).

As you might guess, the command I was finally trying out was dc, which is one of those hoary old Unix utilities that barely gets any love and attention these days. (Please don't bother to tell me that a modern shell could do all the math with builtins. I'm too old fashioned, and besides, Solaris 8's /bin/sh isn't a modern shell. Although it doesn't do 'echo -n' either, so I guess this script shows where I actually wrote it.)


Comments on this page:

By DanielMartin at 2006-11-22 02:44:10:

That's not showing dc the love. This is showing dc the love:

#! /bin/sh
dc -e "$*" -e '[hi=1/lo=0 ?]sa
               [hi? ]sb
               [lo? ]sc
               [[ANS=]Plllh+2/psmq]sQ
               []sG
               [[hi=]Plhpsh[lo=]Pllpsl]sG
               [clGxlllh1-!>Qlllh+2/psmlaP?z1>Q2%d1>C0<BlAx]sA
               [lmsh]sB
               [lm1+sl]sC
               [clbP?shlcP?sllAx]sD
               [shsllhll>FlAx]sE
               [lhllshsl]sF
               [zdsj2>Dlj1<E]xq'

If you give a high and a low as arguments to the shell script, it'll use them; otherwise, dc will prompt you. Enter odd numbers for "too high" and even numbers for "too low". Just press enter when prompted to exit at that point.

Uses almost the same algorithm for the nitpicky details of binary search as in your post. The main difference is that this stops and gives the answer if lo and hi get within 1 of each other. (which may not be correct if you're looking for something that really happens between integers, like where in the revision history a certain alias got added) Eliminate the second line ending in sG to reduce verboseness, and remove 1- from the line ending is sA to make it only stop when lo = hi.

Note that because this is passed the script through -e, this shell script requires gnu dc, though the dc script itself works with solaris dc too when put into a separate file and invoked as "dc hilo.dc" (Of course, then it always prompts)

Also, yeah, it's unmaintainable, but if you were worried about that you would have used expr instead of dc in your post to begin with.

By DanielMartin at 2006-11-22 10:32:37:

That last script was really much too long and understandable. This fixes both those issues:

#! /bin/sh
dc -e "$*" -e '[lllh]dsgsG
[[lo=]Pllp[hi=]Plhp]sG
[s_[]pP[ANS=]Plllh+2/psmq]sQ [cladP?smP?sl]sD
[smsl]sE [lmllsmsl]sF [lm1+sl]sC [lmsh]sB 1sb 0sc
[lBlCsBsClblcsbsc]sr [? ]sa
[z2>Dz1<Elmll>F[lglGlBxx!>Qx+2/psmlaP?z2>Q2%lb!=rdx]dx]xq'

Also, the multiple different prompts in different situations are gone. All prompts are now a uniform "? ". Because of this, it accepts lo and hi in either order, and swaps them if you enter them in the wrong order. Again, the second line ending in sG can be removed for less debugging information.

By cks at 2006-11-22 23:33:01:

Also, yeah, it's unmaintainable, but if you were worried about that you would have used expr instead of dc in your post to begin with.

I was going to say that I was forced to use dc for the '(lo+hi)/2' calculation because expr can't do it in one pass, but then I read the manpage more closely and realized that actually, yes it can. (I can't say that blogging hasn't been educational!)

I don't mind having poked around dc a bit; it's always been one of those classical Unix utilities that I'd intended to look into someday. (And clearly it has a lot more features than I was expecting it to have.)

Written on 21 November 2006.
« A DiskSuite annoyance: metastat
Why ssh validates host keys using the exact name you supplied »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Tue Nov 21 22:54:02 2006
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.