[英]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
作為測試命令。
我認為最好使用通用的二進制搜索函數,然后為您的特定情況編寫自己的代碼。
# 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上的更多解釋
在你的情況下,你會像這樣使用它:
#!/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.