Bash Script Binary Search

ぐ巨炮叔叔 提交于 2019-11-28 14:24:08

A binary search needs the max and min boundaries of the search. Starting at zero is great, but your last variable is a little off. Try: last=$(($#students[@]} - 1)) the - 1 will put your array at the correct size (arrays start at zero and go to one less of their size.)

After that try the following pseudo code:

while (last is <= first) 
  middle = midway point between first and last

  // make sure that your comparing just the names "Ann",
  // not your whole string "Ann:A"
  if (students[middle] == student)
    exit loop
  else if (students[middle] < student)
    first = middle + 1
  else if (students[middle] > student)
    last = middle - 1

I'm not great at bash scripting, so I won't try and fix (if it even needs fixing) most of your syntax. The pseudo code should get you most of the way there if you figure out the syntax.

I think it's best to use a generic binary search function then to code your own for your particular case.

Binary search function in bash

# Returns the largest i for which `command i` succeeds (exits with a null exit code)
function dichotomic_search {

  min=$1
  max=$2
  command=$3

  while [ $min -lt $max ]; do
    # Compute the mean between min and max, rounded up to the superior unit
    current=`expr '(' "$min" + "$max" + 1 ')' / 2`
    if $command $current
      then min=$current
      else max=`expr $current - 1`
    fi
  done

  echo $min
}

It calls the function given as its last argument repetitively using binary search to find the last value for which it returns true. More explanations on Github

Binary search through a bash array

In your case, you would use it like that:

#!/usr/bin/env bash

source dichotomic.sh
arr=(Ann:C Bob:A Cindy:B Dean:E Emily:A Karen:A Zob:A)

function is_smaller {
  element=$(echo ${arr[$2]} | cut -f1 -d :)
  if [[ "$element" > "$1" ]]
    then false
    else true
  fi
}

read target
highest_index=`expr ${#arr[@]} - 1`
index=$(dichotomic_search 0 $highest_index "is_smaller $target")
echo "${arr[$index]}"

This solution assumes that you are looking for the first successful execution of a command, rather than an element in an array.

lo=1
hi=100
while [ $(expr $hi - $lo) -ne 1 ]; do
  mid=$(expr $lo + '(' $hi - $lo ')' / 2)

  # Your command here
  test 44 -gt $mid

  if [ $? -eq 0 ]; then lo=$mid; else hi=$mid; fi
done
echo "$lo"

This always print the first value for which the execution of your command succeeds, unlike @lovasoa solution that is off by one in about half of the configurations. You can validate that by using seq 1 100 | while read o; do SCRIPT; done where SCRIPT is the above algorithm with test $o -gt $mid as the tested command.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!