A recursive fibonacci function in bash [duplicate] - bash

This question already has answers here:
Recursive Fibonacci in Bash script
(5 answers)
Closed 8 years ago.
I was perusing the sight for some help with my code and came across a thread from about 4 months ago, however the user's final revision doesn't work when posted into bash, and produces some strange results. Here is my version, which also produces some strange results:
#!/bin/bash
fib()
{
ind=$1
if (( ind <= 0 ))
then echo 0
elif (( ind = 1 ))
then echo 1
else
echo $(( $(fib $((ind - 1)) ) + $(fib $((ind - 2)) ) ))
fi
}
echo fibbonacci sequence number $1 is $(fib $1)
so this block of code will end up always outputting 1. ./fib.sh 5 outputs
fibbonacci sequence number 5 is 1
so I tried to write the code a little closer to what the previous asker had,
#!/bin/bash
fib()
{
ind=$1
if (( ind <= 0 ))
then echo 1
else
echo $(( $(fib $((ind - 1)) ) + $(fib $((ind - 2)) ) ))
fi
}
echo fibbonacci sequence number $1 is $(fib $1)
While I don't understand the logic here, it actually starts to output fibonacci numbers,
but now I get a slightly different problem; ./fib.sh 3 outputs:
fibbonacci sequence number 3 is 5
./fib.sh 5 outputs :
fibbonacci sequence number 5 is 13
Well we know that the 3rd fibonacci number is 1, and the 5th is 3 so what gives? The code seems to skip ahead several fibonacci numbers, and I can't figure out what is logically wrong with my code.

Usually when writing Fibonacci sequence logic you have to special-case the first two numbers. That's what the first user has done: special-casing 0 and 1.
You've removed one instance of special-casing and shifted everything by one index, which explains one shift. The other is easy: the code is zero-indexed. That's why everything is "off by two".
What's wrong with the original code? This line:
elif ((ind = 1))
sets ind to 1. Otherwise it's fine.
A simple fix to your code is to replace this line:
if (( ind <= 0 ))
with
if (( ind <= 2 ))
and off you go. That gives you the one-indexed behavior you'd expect:
cternus#astarael ~/foo> for i in `seq 1 10`; do ./foo.sh $i; done
fibbonacci sequence number 1 is 1
fibbonacci sequence number 2 is 1
fibbonacci sequence number 3 is 2
fibbonacci sequence number 4 is 3
fibbonacci sequence number 5 is 5
fibbonacci sequence number 6 is 8
fibbonacci sequence number 7 is 13
fibbonacci sequence number 8 is 21
fibbonacci sequence number 9 is 34
fibbonacci sequence number 10 is 55

#!/bin/bash
function fib(){
if [ $1 -le 0 ]; then
echo 0
elif [ $1 -eq 1 ]; then
echo 1
else
echo $[`fib $[$1-2]` + `fib $[$1 - 1]` ]
fi
}
fib $1

#!/bin/bash
#fibonacci sequence function
fib()
{
ind=$1
if (( ind <= 0 ))
then echo 0
elif (( ind == 2 ))
then echo 1
else
echo $(( $(fib $((ind - 1)) ) + $(fib $((ind - 2)) ) ))
fi
}
echo fibbonacci sequence number $1 is $(fib $1)
So my problem was with the equality check in
elif (( ind = 1 ))
I should have used the double =, and change the 1 to a 2, so it should have been
elif (( ind == 2 ))
Ultimately my correct script should look like this
#!/bin/bash
#fibonacci sequence function
fib()
{
ind=$1
if (( ind <= 1 ))
then echo 0
elif (( ind == 2 ))
then echo 1
else
echo $(( $(fib $((ind - 1)) ) + $(fib $((ind - 2)) ) ))
fi
}
echo fibbonacci sequence number $1 is $(fib $1)
Thanks a bajillion and one to Christian Ternus for the help, I've been programming for a few years now and totally should have seen the equality check error >.<

Interestingly enough, the Korn shell executes #christianternus' script much faster than other shells:
$ for a in sh bash zsh ksh; do echo "shell: $a"; time for i in $(seq 1 20); do $a bin/fib.sh $i; done | md5sum; done
shell: sh
5fece53a38f2df040bfaf9632c2b7f4b -
real 0m29.508s
user 0m3.788s
sys 0m11.785s
shell: bash
5fece53a38f2df040bfaf9632c2b7f4b -
real 0m29.906s
user 0m3.604s
sys 0m11.235s
shell: zsh
5fece53a38f2df040bfaf9632c2b7f4b -
real 0m29.203s
user 0m2.505s
sys 0m14.377s
shell: ksh
5fece53a38f2df040bfaf9632c2b7f4b -
real 0m0.942s
user 0m0.843s
sys 0m0.079s

Related

Recursion in bash(fibonacci sequence)

The main problem my code doesn't do echo every time(fibonacci sequence)
#!/bin/bash
function fib(){
if [ $1 -le 0 ]; then
echo 0
elif [ $1 -eq 1 ]; then
echo 1
else
echo $[`fib $[$1 - 2]` + `fib $[$1 - 1]` ]
fi
}
fib $1
i was expecting it will do echo every time. It shows:
~/Bash$ ./fn.sh 12
144
but i need it to show like this:
~/Bash$ ./fn.sh 12
0
1
1
2
3
5
8
13
21
34
55
89
144
Your function is consuming the output of its invocation via backticks (command substitution). Only the last output is sent to the terminal. Your function will only return the n-th number of the Fibonacci sequence.
If you want to return all the numbers of the sequence up to a certain point, you can use a loop:
for i in $(seq "$i"); do
fib "$i"
done
Another method might be:
#!/bin/bash
fibseq () {
echo "$1"
if (($3 > 0)); then fibseq "$2" $(($1 + $2)) $(($3 - 1)); fi
}
if (($1 > 0)); then fibseq 0 1 "$1"; fi
Note that this is just a loop disguised as recursion. This method is much more efficient than the naive recursive version to compute Fibonacci sequences. Arguments to fibseq function: $3 serves as a counter, $1 and $2 are the last two Fibonacci numbers.
As an aside note, you can replace the $(($1 + $2)) with $(bc <<< "$1 + $2") if you want arbitrary-precision arithmetic.
Finally, don't use the $[…] form for arithmetic expansion. Use $((…)) instead.

How to print odd numbers in bash?

for i in {1..100}
do
if [ ($i % 2) -ne 0 ]
then
echo $i
fi
done
Hi! I am learning Bash but I have some probelems with printing the odd numbers in the range 1 to 100, obviously I have some syntax error which I cannot find.
The {x..y} construct allows a 3rd argument to designate the increment value (default is 1), eg:
for i in {1..20..3} # start with 1 and increment by 3 until you reach/pass 20
do
echo $i
done
This generates:
1
4
7
10
13
16
19
For odd vs even you designate the starting number and increment by 2:
# odd numbers
for i in {1..10..2} # start with an odd number and increment by 2
do
echo $i
done
1
3
5
7
9
# even numbers
for i in {2..10..2} # start with an even number and increment by 2
do
echo $i
done
2
4
6
8
10
I think your if statement isn't correct. Here's a small rewrite with a working if:
#!/bin/bash
for i in {1..100}
do
isEvenIfZero=$i%2;
if [[ $isEvenIfZero -ne 0 ]];
then
#echo -n $i #single line
echo $i
fi
done

How do I write a Bash script for a C program that takes 4 numerical inputs? [duplicate]

This question already has answers here:
How to zero pad a sequence of integers in bash so that all have the same width?
(15 answers)
Closed 2 years ago.
I am writing a bash script for a c program, where the program asks for a 4 numerical pin inputs. However, when I wrote the script, the output seems to run in a loop, but it doesn't break where it gets identified as the correct number the program will accept.
#!/bin/bash
RANGE=9000
count=${RANDOM:0:4}
while [[ "$count" -le $RANGE ]]
do
number=$RANDOM
(( "number %= $RANGE" ))
echo $number
if [[ "$count" == "$RANGE" ]]; then
break
fi
done
When I run it, I can see some numbers in the output that returned as 2 or 3 digits, instead of 4. So in theory, what I want to do is find a random number that is 4 digits that the program will take, but I don't know what is the random number, so essentially it is a brute force, or just me manually guessing the pin number.
If all you need is a random 4-digit number, you can do that with:
printf -v number "%04d" $((RANDOM % 10000))
The $RANDOM gives you a random number 0..32767, the % 10000 translates that to the range 0..9999 (not perfectly distributed, but should be good enough for most purposes), and the printf ensures leading zeros are attached to it (so you'll see 0042 rather than 42, for example).
You can test it with the following script:
(( total = 0 ))
(( bad = 0 ))
for i in {1..10000} ; do
printf -v x "%04d" $((RANDOM % 10000))
(( total += 1 ))
[[ "${x}" =~ ^[0-9]{4}$ ]] || { echo Bad ${x}; (( bad += 1 )); }
done
(( good = total - bad ))
echo "Tested: ${total}, bad ${bad}, good ${good}"
which should give you:
Tested: 10000, bad 0, good 10000

bash scripting if statement, comparing decimal

Kind of new to bash scripting and is having trouble with the below code.
I am trying to compare the array number with the number you have input from the "read ans" the problem is mostly comparing decimal numbers
BGpercent=(0 99 99.3 99.6 99.8 100)
BGpoint=(0 1 2 3 4 5)
read ans
for (( c=${#BGpercent[#]}; c>=0; c-- ))
do
echo "${BGpercent[$c]}"
if [ "${BGpercent[$c]}" <= "$ans" ];
then
result=${BGpoint[$c]}
break
fi
done
echo $result | bc -lstrong text
Error - ./testscript.sh: =: No such file or directory
I guess the problem is in your if check (floating point comparison).
#!/bin/bash
BGpercent=(0 99 99.3 99.6 99.8 100)
BGpoint=(0 1 2 3 4 5)
read ans
for (( c=$[ ${#BGpercent[#]} - 1 ] ; c>=0; c-- ))
do
if (( $(echo "${BGpercent[$c]} <= $ans" |bc -l) ));
then
result=${BGpoint[$c]}
break
fi
done
Also, the variable c value must be decremented in the beginning, else it will contain an invalid index value. I am not sure what you intent to do with the last line (echo $result | bc -lstrong text)
BGpercent=(0 99 99.3 99.6 99.8 100)
BGpoint=(0 1 2 3 4 5)
echo " Write in your number:"
read number
for (( c=${#BGpercent[#]}-1; c>-1; c-- ))
do
echo ${BGpercent[$c]}
bool=echo "if (${BGpercent[$c]} <= ${number}) 1" | bc
if [ "$bool"1 -eq 11 ] ; then
result=${BGpoint[$c]}
break
fi

Is there a way to calculate factorials of #s w/o a pre-determined amount of user input? i.e. will calculate 1!-7! w/o initializing 7 diff. variables

Is there a way to get rid of having to create 4 different do-while statements for each variable? For example, I would like to calculate 1!-7! w/o initializing 7 diff. variables and initializing 7 different variables. Is this possible?
Below is an example of how I would like the program to initiate and what the output should be
./filename.sh 1 2 3 4 5 6 7
The factorial 1! is 1
The factorial 2! is 2
The factorial 3! is 6
The factorial 4! is 24
…
Code:
#!/bin/bash
on1=0 #initiating the 4 variables
on2=0 #possible to compress these into 1 variable?
on3=0
on4=0
fact1=1
fact2=1
fact3=1
fact4=1
echo -n "Enter numbers to find factorial : " # gets user input of 4 #s
read n1 n2 n3 n4
on1=$n1
on2=$n2
on3=$n3
on4=$n4
while [ $n1 -ge 1 ] # calculates first factorial
do #possible to only make 1 loop?
fact1=`expr $fact1 \* $n1`
n1=`expr $n1 - 1`
done
while [ $n2 -ge 1 ] # calculates second factorial
do
fact2=`expr $fact2 \* $n2`
n2=`expr $n2 - 1`
done
while [ $n3 -ge 1 ] #calculates third factorial
do
fact3=`expr $fact3 \* $n3`
n3=`expr $n3 - 1`
done
while [ $n4 -ge 1 ] # calculates fourth factorial
do
fact4=`expr $fact4 \* $n4`
n4=`expr $n4 - 1`
done
echo "The factorial $on1! is $fact1" # outputs the four factorials
echo "The factorial $on2! is $fact2" #possible to make only 1 echo?
echo "The factorial $on3! is $fact3"
echo "The factorial $on4! is $fact4"
Use a loop to process each of the arguments.
#!/bin/bash
while [ $# -gt 0 ]
do
n=$1
on=$n
fact=1
while [ $n -ge 1 ]
do
fact=$(expr $fact \* $n)
n=$(expr $n - 1)
done
echo "The factorial of $on is $fact"
shift # go to the next argument
done
Just for fun, I tried a recursive solution:
#!/bin/zsh
function fact {
if (($1 < 2))
then
echo 1
else
expr $1 '*' $(fact $(($1-1)))
fi
}
for arg
do
fact $arg
done
I first thought, it would blow up soon due to stack overflow, but it calculated happily even pretty large values (such as 200!) in quite short time (in zsh; didn't try it with other shells)!

Resources