Print Fibonacci series using recursion in bash with only 1 variable - bash

I'd like to know how to print Fibonacci series using recursion in bash with only 1 variable.
From what I've done:
fib()
{
i=$1
if (( $i <= 1 ))
then echo 0
elif (( $i == 2 ))
then echo 1
else
echo $(( $(fib $(($i - 1)) ) + $(fib $(($i - 2)) ) ))
fi
}
echo $(fib $1)
I get the correct output of the final iteration, for example if I enter 10 I will get 34, but I'd like to print the whole sequence of numbers, i.e. all the iterations as well. How can I achieve that?
Another way I tried was by:
#!/bin/bash
arr[0]=0
arr[1]=1
for (( i=0; i<=10; i++ ))
do
echo -n "${arr[0]} "
arr[0]=$((${arr[0]} + ${arr[1]} ))
arr[1]=$((${arr[0]} - ${arr[1]} ))
done
echo ""
But obviously here I've used a for loop, but I don't want to use another variable.

Just for (my kind of) fun, this code prints the Fibonacci numbers from the 0th to the 92nd (as defined in Fibonacci number - Wikipedia) with a recursive function that uses no variables:
#! /bin/bash
function fib
{
echo ${3-0}
(($1 > 0)) && fib $(($1-1)) ${3-0} $((${2-1}+${3-0}))
}
fib 92
Some may claim that using the positional parameters ($1, $2, $3) for this is cheating, but then other solutions could be said to be using two variables ($i and $1).
The code takes under 0.01 seconds to run on my (oldish) Linux machine.
The code should work with numbers up to 92 with Bash version 3 or later on any platform. See Bash Number Limit?. Numbers higher than 93 will cause to code to produce garbage results due to arithmetic overflow.

Variables in bash are global by default. You need to make i local explicitly.
fib () {
local i
i=$1
if (( i <= 1 )); then
echo $i
else
echo $(( $(fib $((i-1)) ) + $(fib $((i - 2)) ) ))
fi
}
(Also, your base cases are a little off if you are starting with 0, and 2 need not be a base case; fib 2 can be derived from the base cases fib 0 and fib 1.)

If you want to print each fibonacci value from 1 to $n, I suggest:
fib_r() {
local i=$1
if (( i < 0 )); then
echo "Error: negative numbers not allowed" >&2
exit 1
elif (( i <= 1 )); then
echo $i
else
echo $(( $($FUNCNAME $((i - 1)) ) + $($FUNCNAME $((i - 2)) ) ))
fi
}
fib() {
local i
for (( i = 1; i <= $1; i++ )); do
fib_r $i
done
}
fib 10
outputs
0
1
1
2
3
5
8
13
21
34
It's still one variable, albeit one per function.
I use the bash variable $FUNCNAME in the recursive function so you don't have to hardcode the function name within itself. I got bit by not updating that line when I renamed the function.
Of course your performance will greatly improve if you cache the results: "fib 16" takes, on my VM, about 3.5 sec without caching and about 0.03 sec with caching.
fib_r() {
local i=$1
if (( i < 0 )); then
echo "Error: negative numbers not allowed" >&2
exit 1
elif [[ -n ${fib_cache[i]} ]]; then
echo "${fib_cache[i]}"
elif (( i <= 1 )); then
echo $i
else
echo $(( $( $FUNCNAME $((i - 1)) ) + $( $FUNCNAME $((i - 2)) ) ))
fi
}
fib_cache=()
fib() {
local i
for ((i=1; i<=$1; i++)); do
fib_cache[i]=$(fib_r $i)
echo "${fib_cache[i]}"
done
}

Related

Find the index of a number in the fibonacci sequence

I want to find the index of a number in the fibonacci sequence using recursion.
Example : 13 (my input) is the 8th (output) number of the fibonacci sequence
Here is my function :
#!/bin/bash
function fib() {
number=1
cpt=1
if [[ $number -lt $1 ]]
then
compteur=$(($cpt+ 1))
echo $(( $(fib $(($number - 1)) ) + $(fib $(($number - 2)) ) ))
fi
echo $cpt
}
fib $1
I have this error : Wrong symbol near the +
Then I don't know if my logic for the algorithm is good. I don't know if my syntax of how we are calling the function in the recursion in bash is great.
Any help would be appreciated. Thanks !
With recursion, would you please try the following:
#!/bin/bash
# "fib n" returns the nth Fibonacci number (n starts from 0)
fib() {
local n=$1
if (( n == 0 )); then
echo "0"
elif (( n == 1 )); then
echo "1"
else
echo "$(( $(fib "$(( n - 1 ))" ) + $(fib "$(( n - 2 ))" ) ))"
fi
}
input=$1 # user's input as a 1st argument to this script
for (( i = 0; ; i++ )); do # increment the index i starting from 0
f=$(fib i)
(( f >= input)) && break # if "fib i" >= input, then exit the loop
done
if (( f == input )); then # the input is a Fibonacci number
echo "$i"
else
echo "$i (input $input is not a Fibonacci number.)"
fi
By convention, the index starts with "0", then 13 is the 7th Fibonacci number.
Btw if you increase the input number, you'll see the execution of the script takes a while (e.g. please try with 987 as an input).
Here is an alternative without the recursion:
#!/bin/bash
f0=0
f1=1
input=$1
for (( i = 0; ; i++ )); do
(( f0 >= input )) && break
f2=$(( f1 + f0 ))
f0=$f1
f1=$f2
done
if (( f0 == input )); then
echo "$i"
else
echo "$i (input $input is not a Fibonacci number.)"
fi
Although the recursion is mathematically simple and elegant, the number
of computation depends on the implementation. Fibonacci sequence is a
typical example which requires a consideration.
The 1st script includes two problems:
In the 1st recursion, the fib function calls two fib functions.
In the 2nd recursion, four functions are called. The number of computation
reaches O(2**N).
The intermediate values such as fib(n-1) and fib(n-2) are discarded and
calculated again in the next invocation.

storing the contents or a for loop into an array and then retrieving it for if statements

I'm having a bit of trouble, id like to store the contents of the values from my for loop into an array or something and then recall these values to be tested against the if statements and then printed accordingly. My current code gives me a good output however if I use large numbers like 1 and 50 as input values it gives me multiple rows of "Divisible by xValue" instead of just one of each value. Thanks in advance
if (( $# > 2 )); then
echo "Only your first two values will be used"
for((i=($1+($1%2));i<($2-3);i+=2));do #for loop lists all even numbers between two input values
if (( i % 7 == 0 )); then #checks if even number divisible by 7
echo " $i : Divisible by 7 " # prints number and labels
elif (( $i % 11 == 0 )); then #else if checks if divisible by 7
echo " $i : Divisible by 11 " #prints number and labels
elif (( $i % 13 == 0 )); then #if divisible by 13
echo " $i : Divisible by 13 "
fi #closes the if statement
printf "%d%s, " "$i"
done
echo "$i"
Use +=() in bash to add a new element into an array.
#! /bin/bash
set -eu
if (( $# > 2 )); then
echo "Only your first two values will be used"
fi
even=()
for ((i=$1; i<=$2; ++i)) ; do
if (( i % 2 == 0 )) ; then
even+=($i)
fi
done
for e in "${even[#]}" ; do
for d in 7 11 13 ; do
if (( e % d == 0 )); then
echo "$e: Divisible by $d"
break
fi
done
printf "%d%s, " "$e"
done

Quicksort implemented in shell script doesn't work

I am learning shell scripting and trying to implement quick sort using it.
But it doesn't work, actually it acting weird.
The script:
#!/bin/bash
declare -a data=()
declare -r size=10
declare -i steps=0
for i in $(seq 0 $size); do
data[$i]=$(expr $RANDOM % $size)
done
function partition() {
pivot=${data[$1]}
left=$(expr $1 + 1)
right=$2
while true; do
while [[ $left -le $right && ${data[$left]} -le $pivot ]]; do
left=$(expr $left + 1)
steps=$(expr $steps + 1)
done
while [[ $right -ge $left && ${data[$right]} -ge $pivot ]]; do
right=$(expr $right - 1)
steps=$(expr $steps + 1)
done
if [[ $left -gt $right ]]; then
break
fi
temp=${data[$left]}
data[$left]=${data[$right]}
data[$right]=$temp
done
temp=${data[$1]}
data[$1]=${data[$right]}
data[$right]=$temp
echo $right
}
function quickSort() {
if [[ $1 -lt $2 ]]; then
local partitionPoint=$(partition $1 $2)
quickSort $1 $(expr $partitionPoint - 1)
quickSort $(expr $partitionPoint + 1) $2
fi
}
# involve the algorithm
quickSort 0 $(expr $size - 1)
echo "Steps: $steps"
echo ${data[#]}
I tried to log some variable but it's just weird I can't figure out what's going on.
When I comment out all the code in the two functions and 'manually' update elements of data variable, it did changed.
I tried to log some variables and they all changing.
But the final output remains untouched.
Or maybe it eventually reversed all the flipping but I don't know.
I can't figure it out.
At last I compare my python implementation line by line. No mistakes. But it just not working.
Am I miss something?
Variable scope or something?
Any advice will be appreciated.
There are several smaller issues in this code, but the biggest issue is here:
partitionPoint=$(partition $1 $2)
This is problematic because $( ... ) runs ... in a subshell -- a separate, fork()ed-off process, consequently with its own variable scope.
If you instead return your result via indirect assignment, making it:
partition "$1" "$2" partitionPoint
and inside the function using:
printf -v "$3" %s "$right"
...to assign the value to the variable so named, things work much better.
#!/bin/bash
PS4=':$LINENO+'; set -x
data=()
size=10
steps=0
for ((i=0; i<size; i++)); do
data[$i]=$((RANDOM % size))
done
partition() {
local pivot left right dest temp
pivot=${data[$1]}
left=$(($1 + 1))
right=$2
dest=$3
while true; do
while (( left <= right )) && (( ${data[$left]} <= pivot )); do
left=$(( left + 1 ))
steps=$(( steps + 1 ))
done
while (( right >= left )) && (( ${data[$right]} >= pivot )); do
right=$(( right - 1 ))
steps=$(( steps + 1 ))
done
(( left > right )) && break
temp=${data[$left]}
data[$left]=${data[$right]}
data[$right]=$temp
done
: '$1='"$1" right="$right" 'data[$1]='"${data[$1]}" 'data[$right]='"${data[$right]}"
temp=${data[$1]}
data[$1]=${data[$right]}
data[$right]=$temp
printf -v "$dest" %s "$right"
}
quickSort() {
local partitionPoint
if (( $1 < $2 )); then
partition "$1" "$2" partitionPoint
quickSort "$1" "$(( partitionPoint - 1 ))"
quickSort "$((partitionPoint + 1))" "$2"
fi
}
# involve the algorithm
quickSort 0 "$(( size - 1 ))"
echo "Steps: $steps"
printf '%s\n' "${data[#]}"

How to process value from for loop in shell

I have to a perform logic like this.
I have a array.
expression for this in shell
[(first no + arrlen) - ( index +1 ) - ge 10 ]
I have code this like this but it's not working
#!/bin/bash
array=(4 5 6 7 8 9)
for i in ${array[#]}
do
echo $i
done
echo "${#array[#]}"
l=${#array[#]}
count=0
for (( i=0; i < ${#array[#]}; i++ ))
do
if [ ($(`expr $i + $l`) - $(`expr ${!array[#]} + 1`)) -ge 10 ]
then
count=`expr $count + 1`
else
echo
fi
done
Your code could look like this:
#!/bin/bash
array=(4 5 6 7 8 9)
for i in "${array[#]}"; do
echo "$i"
done
length=${#array[#]}
first=${array[0]}
count=0
for (( i=0; i < length; i++ )); do
if (( (first + length) - (i + 1) >= 10 )); then
((count++))
else
echo "something"
fi
done
Don't use expr, use (( )) for arithmetic expressions
Quote expansions: "$i", "${array[#]}", ...
${!array[#]} expands to ALL indexes of your array, not the current index

Recursive function in bash

I want to do a function that will return the factorial of a number in bash
Here's the current code that doesn't work, can anyone tell me what's wrong and how to correct it? I just started learning bash and I don't know that much.
#!/bash/bin
factorial()
{
let n=$1
if (( "$n" <= "1" ))
then return 1
else
factorial n-1
return $n*$?
fi
return 0
}
factorial 5
echo "factorial 5 = $?"
There are several syntax and a quite obvious logic one (return 0)
A working version is below:
#!/bin/bash
factorial()
{
if (( $1 <= 1 )); then
echo 1
else
last=$(factorial $(( $1 - 1 )))
echo $(( $1 * last ))
fi
}
factorial 5
You are missing:
return is bad (should use echo)
shbang line (is /bin/bash not /bash/bin)
Can't do arithmetic outside of (( )) or $(( )) (or let, but (( )) is preferred)
#!/bin/bash
function factorial()
{
if (( $1 < 2 ))
then
echo 1
else
echo $(( $1 * $(factorial $(( $1 - 1 ))) ))
fi
}
This will work better.
(It works up to 25, anyway, which should be enough to prove the point about recursion.)
For higher numbers, bc would be the tool to use, making the ninth line above:
echo "$1 * $(factorial $(( $1 - 1 )))" | bc
but you have to be a bit careful with bc --
$ factorial 260
38301958608361692351174979856044918752795567523090969601913008174806\
51475135399533485285838275429773913773383359294010103333339344249624\
06009974551133984962615380298039823284896547262282019684886083204957\
95233137023276627601257325925519566220247124751398891221069403193240\
41688318583612166708334763727216738353107304842707002261430265483385\
20637683911007815690066342722080690052836580858013635214371395680329\
58941156051513954932674117091883540235576934400000000000000000000000\
00000000000000000000000000000000000000000
was quite a strain on my poor system!
echo-ing a result may be the only way to get a result for n > 5, but capturing the echo'ed result requires a subshell, which means the recursion will get expensive fast. A cheaper solution is to use a variable:
factorial() {
local -i val=${val:-($1)}
if (( $1 <= 1 )); then
echo $val
return
fi
(( val *= $1 - 1 ))
factorial $(( $1 - 1 ))
}
If you want to be extra sure that val is unset when you start, use a wrapping function:
factorial() {
local -i val=$1
_fact() {
if (( $1 <= 1 )); then
echo $val
return
fi
(( val *= $1 - 1 ))
_fact $(( $1 - 1 ))
}
_fact $1
}
For comparison:
# My Method
$ time for i in {0..100}; do factorial $(( RANDOM % 21 )); done > /dev/null
real 0m0.028s
user 0m0.026s
sys 0m0.001s
# A capturing-expression solution
$ time for i in {0..100}; do factorial $(( RANDOM % 21 )); done > /dev/null
real 0m0.652s
user 0m0.221s
sys 0m0.400s
clear cat
fact()
{
i=$1
if [ $i -eq 0 -o $i -eq 1 ]
then
echo 1
else
f=`expr $i \- 1`
f=$(fact $f)
f=`expr $i \* $f`
echo $f
fi
}
read -p "Enter the number : " n
if [ $n -lt 0 ]
then
echo "ERROR"
else
echo "THE FACTORIAL OF $n : $(fact $n) "
fi
Another one implementation using echo instead of return
#!/bin/bash
factorial()
{
if [ $1 -le 1 ]
then
echo 1
else
echo $[ $1 * `factorial $[$1-1]` ]
fi
}
echo "factorial $1 = " `factorial $1`
#-----------------factorial ------------------------
# using eval to avoid process creation
fac=25
factorial()
{
if [[ $1 -le 1 ]]
then
eval $2=1
else
factorial $[$1-1] $2
typeset f2
eval f2=\$$2
((f2=f2*$1))
eval $2=$f2
fi
}
time factorial $fac res
echo "factorial =$res"
Compute factor recursively under bash
In addition to all answer, I would like to suggest:
Store computed factorials in order to avoid re-compute
Using an array to store computed factorials, could improve a lot your function, if you plan to run this function many times!!
For this, we need to reverse recursion way, in order to store each computed factorial:
declare -ia _factorials=(1 1)
factorial() {
local -i _req=$1 _crt=${2:-${#_factorials[#]}} _next=_crt+1 \
_factor=${3:-${_factorials[#]: -1}}
if [ "${_factorials[_req]}" ]; then
printf %u\\n ${_factorials[_req]}
else
printf -v _factorials[_crt] %u $((_factor*_crt))
factorial $1 $_next ${_factorials[_next]}
fi
}
Then
factorial 5
120
Ok, and:
declare -p _factorials
declare -ai _factorials=([0]="1" [1]="1" [2]="2" [3]="6" [4]="24" [5]="120")
Then if I do set -x to trace operations and ask for higher value:
set -x
factorial 10
+ factorial 10
+ local -i _req=10 _crt=6 _next=_crt+1 _factor=120
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 720
+ factorial 10 7
+ local -i _req=10 _crt=7 _next=_crt+1 _factor=720
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 5040
+ factorial 10 8
+ local -i _req=10 _crt=8 _next=_crt+1 _factor=5040
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 40320
+ factorial 10 9
+ local -i _req=10 _crt=9 _next=_crt+1 _factor=40320
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 362880
+ factorial 10 10
+ local -i _req=10 _crt=10 _next=_crt+1 _factor=362880
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 3628800
+ factorial 10 11
+ local -i _req=10 _crt=11 _next=_crt+1 _factor=3628800
+ '[' 3628800 ']'
+ printf '%u\n' 3628800
3628800
Where recursions was done from 5 to 10, and factorial 5 was directly used.
declare -p _factorials
declare -ai _factorials=([0]="1" [1]="1" [2]="2" [3]="6" [4]="24" [5]="120"
[6]="720" [7]="5040" [8]="40320" [9]="362880" [10]="3628800")
Avoid forks: replace echo by setting a variable:
declare -ia _factorials=(1 1)
factorialtovar() {
local -i _req=$2 _crt=${3:-${#_factorials[#]}} _next=_crt+1 \
_factor=${4:-${_factorials[#]: -1}}
if [ "${_factorials[_req]}" ]; then
printf -v $1 %u ${_factorials[_req]}
else
printf -v _factorials[_crt] %u $((_factor*_crt))
factorialtovar $1 $2 $_next ${_factorials[_next]}
fi
}
Then
factorialtovar myvar 7 ; echo $myvar
5040
declare -p myvar _factorials
declare -- myvar="5040"
declare -ai _factorials=([0]="1" [1]="1" [2]="2" [3]="6" [4]="24" [5]="120" [6]="720" [7]="5040")
and
set -x
factorialtovar anothervar 10
+ factorialtovar anothervar 10
+ local -i _req=10 _crt=8 _next=_crt+1 _factor=5040
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 40320
+ factorialtovar anothervar 10 9
+ local -i _req=10 _crt=9 _next=_crt+1 _factor=40320
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 362880
+ factorialtovar anothervar 10 10
+ local -i _req=10 _crt=10 _next=_crt+1 _factor=362880
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 3628800
+ factorialtovar anothervar 10 11
+ local -i _req=10 _crt=11 _next=_crt+1 _factor=3628800
+ '[' 3628800 ']'
+ printf -v anothervar %u 3628800
set +x
+ set +x
echo $anothervar
3628800
To complete #Marc Dechico solution, instead of eval, passing reference is preferable nowadays (bash >= 4.3) :
factorial_bruno() {
local -i val="$1"
local -n var="$2" # $2 reference
_fact() {
if (( $1 <= 1 )); then
var="$val"
return
fi
((val*=$1-1))
_fact $(($1-1))
}
_fact "$1"
}
declare -i res
factorial_bruno 20 res
printf "res=%d\n" "$res"
If we compare the time on a 1,000 runs of #kojiro's, #techno's (catching result for them, as after all we want the result), #Marc Dechici's, and my solution, we get :
declare -i res
TIMEFORMAT=$'\t%R elapsed, %U user, %S sys'
echo "Kojiro (not catching result) :"
time for i in {1..1000}; do factorial_kojiro $((i%21)); done >/dev/null
echo "Kojiro (catching result) :"
time for i in {1..1000}; do res=$(factorial_kojiro $((i%21))); done
echo "Techno (not catching result) :"
time for i in {1..1000}; do factorial_techno $((i%21)); done >/dev/null
echo "Techno (catching result, 100% data already cached) :"
time for i in {1..1000}; do res=$(factorial_techno $((i%21))); done
_factorials=(1 1)
echo "Techno (catching result, after cache reset) :"
time for i in {1..1000}; do res=$(factorial_techno $((i%21))); done
echo "Marc Dechico :"
time for i in {1..1000}; do factorial_marc $((i%21)) res; done
echo "This solution :"
time for i in {1..1000}; do factorial_bruno $((i%21)) res; done
Kojiro (not catching result) :
0.182 elapsed, 0.182 user, 0.000 sys
Kojiro (catching result) :
1.510 elapsed, 0.973 user, 0.635 sys
Techno (not catching result) :
0.054 elapsed, 0.049 user, 0.004 sys
Techno (catching result, 100% data already cached) :
0.838 elapsed, 0.573 user, 0.330 sys
Techno (catching result, after cache reset) :
2.421 elapsed, 1.658 user, 0.870 sys
Marc Dechico :
0.349 elapsed, 0.348 user, 0.000 sys
This solution :
0.161 elapsed, 0.161 user, 0.000 sys
It is interesting to notice that doing an output (echo/printf) in a function and catching the result with res=$(func...) (subshell) is always very expensive, 80% of the time for Kojiro's solution, >95% for Techno one...
EDIT: By adding a cache with similar solution (#techno new solution - the difference is the use of printf -v instead of using variable reference), we can further improve response time if we need to calculate many times the factorial. With integer limit within bash, that would mean we need to calculate many times the same factorial (probably useful for benchmarks like this one :-)
Avoiding forks!
The only answer here addressing correctly the process thread is kojiro's answer, but I dislike using a dedicated variable for this.
For this, we could use arguments to transmit steps or intermediary results:
factorial() {
if [ $1 -gt 1 ]; then
factorial $(( $1 - 1 )) $(( ${2:-1} * $1 ))
else
echo $2
fi
}
There is no forks and result is passed as 2nd argument.
Avoiding fork (bis)
Then in order to avoid forks, I prefer storing result into a variable instead of using echo (or other printf):
setvarfactorial() {
if [ $2 -gt 1 ]; then
setvarfactorial $1 $(( $2 - 1 )) $(( ${3:-1} * $2 ))
else
printf -v "$1" %u $3
fi
}
Usage:
factorial 5
120
setvarfactorial result 5
echo $result
120
When goal is to store result into a variable, this second method is a lot more system friendly and quicker than using
result=$(factorial 5)
# or same
result=`factorial 5`
Comparison between with or without forks.
There is no variable set, just running 10x factorial 20:
Tested on my old raspberry-pi:
time for i in {1..10};do factorial 20;done|uniq -c
10 2432902008176640000
real 0m0.376s
user 0m0.159s
sys 0m0.035s
time for i in {1..10};do kojirofactorial 20;done|uniq -c
10 2432902008176640000
real 0m0.348s
user 0m0.150s
sys 0m0.023s
time for i in {1..10};do danielfactorial 20;done|uniq -c
10 2432902008176640000
real 0m5.859s
user 0m1.015s
sys 0m1.732s
If kojiro's version seem a little quicker than mine, difference doesn't really matter, but other answer will take more than 10x more time!!
To see of forks are done:
set -x
danielfactorial 5
+ danielfactorial 5
+ (( 5 <= 1 ))
++ factorial 4
++ (( 4 <= 1 ))
+++ factorial 3
+++ (( 3 <= 1 ))
++++ factorial 2
++++ (( 2 <= 1 ))
+++++ factorial 1
+++++ (( 1 <= 1 ))
+++++ echo 1
++++ last=1
++++ echo 2
+++ last=2
+++ echo 6
++ last=6
++ echo 24
+ last=24
+ echo 120
120
vs:
factorial 5
+ factorial 5
+ '[' 5 -gt 1 ']'
+ factorial 4 5
+ '[' 4 -gt 1 ']'
+ factorial 3 20
+ '[' 3 -gt 1 ']'
+ factorial 2 60
+ '[' 2 -gt 1 ']'
+ factorial 1 120
+ '[' 1 -gt 1 ']'
+ echo 120
120
#!/bin/bash
fact() {
(( $1 == 0 )) && echo 1 || echo "$(( $1 * $(fact $(($1-1)) ) ))"
}
fact $1

Resources