I'm playing on HackerRank, and I'd like to solve the Bill division exercise in BASH. It is a really simple problem, I've already solved it in multiple languages.
My initial Bash solution was this:
#!/bin/bash
billDivision() {
local n="${1}"
local k="${2}"
local b_charged="${3}"
shift 3
local bills=( "${@}" )
(( b_actual = -bills[ k ] ))
for bill in "${bills[@]}"; do
(( b_actual += bill ))
done
(( b_actual /= 2 ))
(( b_overcharge = b_charged - b_actual ))
if(( b_overcharge <= 0 )); then
echo "Bon Appetit"
else
echo "${b_overcharge}"
fi
}
read nk
n="$( echo "${nk}" | cut -d " " -f 1 )"
k="$( echo "${nk}" | cut -d " " -f 2 )"
read ar
IFS=" "; read -ra arr <<< "${ar}"
read b
result="$( billDivision "${n}" "${k}" "${b}" "${arr[@]}" )"
echo "${result}"
It works for all tests except 4 and 5, which fail with Time limit exceeded. I know that Bash uses sparse arrays with some linked-list-like internal representation that is not optimal for speed of access. So I thought, the reason must be that the array is too long in these cases. I checked one of them, and it confirms my assumption. Therefore, I tried to get rid of the array by iterating over the elements of a space separated string. I even inlined the function:
#!/bin/bash
read nk
n="$( echo "${nk}" | cut -d " " -f 1 )"
k="$( echo "${nk}" | cut -d " " -f 2 )"
read bills
read b_charged
(( counter = 0 ))
for bill in ${bills}; do
if(( counter++ != k )); then
(( b_actual += bill ))
fi
done
(( b_actual /= 2 ))
(( b_overcharge = b_charged - b_actual ))
if(( b_overcharge <= 0 )); then
echo "Bon Appetit"
else
echo "${b_overcharge}"
fi
I got the same result, time limit exceeded in test cases 4 and 5.
What is the trick to make Bash process long lists faster? Or fast enough for HackerRank?
You're wasting time adding up bills. This should work fine:
read n k
read -a bill
read charged
IFS=+
actual=$(((${bill[*]}-bill[k])/2))
if ((charged == actual)); then
echo Bon Appetit
else
echo $((charged-actual))
fi