繁体   English   中英

Bash 脚本二进制搜索

[英]Bash Script Binary Search

编写一个 bash 脚本来进行二分查找。 将文件中的学生姓名和成绩读入数组。 提示用户输入学生姓名。 在数组中查找名称并显示等级。 文件中的数据如下:

Ann:A
Bob:C
Cindy:B
Dean:F
Emily:A
Frank:C
Ginger:D
Hal:B
Ivy:A
Justin:F
Karen:D

我已经完成了以下操作,但我坚持下一步该做什么

#!/bin/bash
 echo "please enter students Name: "
 read student
 echo "$student + $Grade"
 ((i=0))
 while read students[$i] ; do
 ((i++))

 done < students.dat
 first=0
 last=$(students[@])


 ((mid=0))
 Name=`echo ${students[$mid]} | cut -d: -f1`
 Grade=`echo ${students[$mid]} | cut -d: -f2`
 echo $Name
 echo $Grade

二分搜索需要搜索的最大和最小边界。 从零开始很好,但你的最后一个变量有点偏离。 尝试: last=$(($#students[@]} - 1)) - 1 会将您的数组置于正确的大小(数组从零开始,然后再减一。)

之后尝试以下伪代码:

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

我不擅长 bash 脚本,所以我不会尝试修复(如果它甚至需要修复)你的大部分语法。 如果您弄清楚语法,伪代码应该可以帮助您完成大部分工作。

此解决方案假定您正在寻找命令的第一次成功执行,而不是数组中的元素。

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"

这始终打印命令执行成功的第一个值,这与 @lovasoa 解决方案不同,后者在大约一半的配置中相差一个。 您可以使用seq 1 100 | while read o; do SCRIPT; done来验证这一点seq 1 100 | while read o; do SCRIPT; done seq 1 100 | while read o; do SCRIPT; done seq 1 100 | while read o; do SCRIPT; done其中SCRIPT是上述算法, test $o -gt $mid作为测试命令。

我认为最好使用通用的二进制搜索函数,然后为您的特定情况编写自己的代码。

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
}

它使用二进制搜索重复调用作为其最后一个参数给出的函数,以找到返回 true 的最后一个值。 Github上的更多解释

通过 bash 数组进行二分搜索

在你的情况下,你会像这样使用它:

#!/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]}"

试试这个,让我得到你的反馈。

#!/bin/bash
##CREATE AN ARRAY VARIABLE TO STORE DATA FOUND IN STUDENT.TXT AT STARTUP
#NAMESARRAY STORE ALL NAMES
declare -a namesarray
#GRADESARRAY STORE ALL GRADES
declare -a gradesarray

#GLOBALMATCHINDEX STORES THE ARRAY INDEX WHERE NAME IS FOUND.... NAMES ARRAY START FROM 0
globalmatchindex=-1

#FUNCTION "CONTAINS" SEARCH THROUGH NAMESARRAY VAIRIABLE TO FIND INPUT FROM USER
function contains(){
    #CREATE 2 VARIABLES "e" AND "match"
    local e match="$1"
    shift
    #VARIABLE matchindex IS A LOCAL VARIABLE IN THE "CONTAINS" FUNCTION THAT TEMPORARILY STORES THE VALUE OF THE INDEX WHERE INPUTED NAME IS FOUND IN namesarray VARIABLE
    local matchindex=0
    #LOOP THROUGH namesarray GLOBAL VARIABLE WHICH WAS PASSED AS A PARAMETER TO THE "CONTAINS" FUNCTION
    for e;
    do  
        #CHECK IF A MATCHING STRING IS FOUND IN THE namesarray GLOBAL VARIABLE WHICH WAS PASSED AS A PARAMETER
        if [ "$e" == "$match" ]; then
            #SET THE VALUE OF globalmatchindex GLOBAL VARIABLE TO THE CURRENT LOOP INDEX ALIAS matchindex
            globalmatchindex=$matchindex
            #EXIT LOOP AND CONTINUE PROCESS
            break
        fi
    #INCREMENT LOCAL matchindex VARIABLE FOR THE NEXT ROUND OF LOOP
    matchindex=$((matchindex+1))
    done
}
#FUNCTION "CONTAINS" END HERE

#linenumber GLOBAL VARIABLE STORES THE CURRENT LINE NUMBER IN students.txt FILE
linenumber=0
#A LOOP THAT READ ENTIRE student.txt FILE
while read line; do
    #SINCE THE NAMES AND GRADES ARE SEPARATED BY ":" CHARACTER, WE USE A STRING SPLIT METHOD TO SEPARATE NAME FROM GRADE
    IFS=':'
    #READ EACH LINE AS ARRAY TO "LINEARRAY" VARIABLE. "LINEARRAY" VARIABLE CONTAINS CONTENT LIKE SO "LINEARRAY[0]='JAMES'", "LINEARRAY[1]='A'"
    read -ra LINEARRAY <<< "$line"
    #STORE THE FIRST STRING IN namesarray GLOBAL VARIABLE
    namesarray[$linenumber]=${LINEARRAY[0]}
    #STORE THE SECOND STRING IN gradesarray GLOBAL VARIABLE
    gradesarray[$linenumber]=${LINEARRAY[1]}
    linenumber=$((linenumber+1))
done < students.txt

while true; do
    echo "Enter Student name:"
    read studentname
    contains "$studentname" "${namesarray[@]}"
    if [ $globalmatchindex -gt -1 ]; then
        echo "Hello ${namesarray[$globalmatchindex]} your grade is ${gradesarray[$globalmatchindex]}"
    else
        echo "Student not found."
    fi
    globalmatchindex=-1

done

student.txt 文件的内容如下。

Ann:A
Bob:C
Cindy:B
Dean:F
Emily:A
Frank:C
Ginger:D
Hal:B
Ivy:A
Justin:F
Karen:D

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM