Basically, what I'm trying to understand is that how to reassign a variable that was already declared before to a new value.
I want to reassign the variable to a different value in the loop. Then print that sum.
For example in JavaScript,
sum = 0;
for... (loop)
sum = sum + start-point; (loop body)
console.log(sum)
Now I don't seem to be able to get that in bash.
This is my code in bash
echo Enter a number:
read NUMBER
echo Enter a startpoint:
read STARTPOINT
echo Enter how many terms:
read TERMS
sum=0;
for ((n=0;n<$TERMS;n++));
do
STARTPOINT=$((STARTPOINT + NUMBER))
sum=$(($sum + $STARTPOINT))
echo $STARTPOINT
echo $sum
done
All the code is correct except the sum part, and because of it the code doesn't run properly. if I remove that part, it works fine. I just need to sum the outputs of the variable STARTPOINT.
Example
Input
NUMBER = 3 (means to increment 3 in the startpoint)
STARTPOINT = 2
TERMS = 5 (means to add 3 to 2 five times)
Expected output
5
8
11
14
17
And the part that I am having difficulty with is how to add all these numbers, when all added, it should print 55.
In this answer I changed your variable names to be lowercase. This is the conventional naming scheme in bash to avoid accidental name collisions with built-in variables and environment variables.
If I understood correctly, you want to build the following sequence and also sum it up:
startpoint + 1*number + startpoint + 2*number + ... + startpoint+ term*number
In that case you should not change startpoint inside your loop. Use another variable to store the result of startpoint + n*number.
startpoint=2 number=3 terms=5 sum=0
echo "$terms times increment $startpoint by $number:"
for ((n=1; n<=terms; n++));
do
((addend = startpoint + n*number))
echo "$addend"
((sum += addend))
done
echo "The sum is $sum"
However, instead of using a slow loop you could printing the sequence using seq and then calculate its sum using the closed formula for triangular numbers:
startpoint=2 number=3 terms=5
seq $((startpoint+number)) $number $((startpoint+terms*number))
((sum = terms*startpoint + terms*(terms+1)/2 * number))
echo "The sum is $sum"
Related
This question already has answers here:
How to generate random number in Bash?
(25 answers)
Closed 11 months ago.
Normally, I may use the following expression to define a variable in bash as the number
var='3'
How could I associate the variable with the random value, e.g. from 1 to 6, which could be assigned each time in the for loop:
for var ...; do
print $var
done
Assuming that in each iteration, the var should be randomly selected from 1 to 6.
I'm not sure if your question is about generating the random number
$ echo $(( RANDOM % 6 + 1 ))
4 # results may vary
or getting a sequence of random numbers. A C-style for loop would probably be simplest.
# Roll a 6-sided dice 5 times.
for ((n=0; var=RANDOM%6+1, n<5; n++)); do
echo $var
done
The second expression makes use of the , operator, so that both var is assigned to just before the beginning of each loop iteration.
(Or course, there's not much reason to write the loop this way. Be clear, and put the assignment at the top of the body instead.
for ((n=0; n < 5; n++)); do
var=$((RANDOM%6 + 1))
echo $var
done
)
There is no need for for loop actually, you can do it by using RANDOM.
If we take your example into consideration;
To create random number between 1 and 6, you can use something like;
$(( ( $RANDOM % 6 ) + 1))
You can try it with;
random_number=$(( ( $RANDOM % 6 ) + 1)); echo $random_number
I'm trying to create a fractal tree in bash, provided that the user enters N where N is the number of branches.
I need to write the following sequence that gets N as an input:
N = 1; sequence = 50
N = 2; sequence = (50-16),(50+16)
N = 3; sequence = (50-16-8),(50-16+8),(50+16-8),(50+16+8)
N = 4; sequence = (50-16-8-4),(50-16-8+4),(50-16+8-4),(50-16+8+4),(50+16-8-4),(50+16-8+4),(50+16+8-4),(50+16+8+4)
N = 5; sequence = (50-16-8-4-2),(50-16-8-4+2),(50-16-8+4-2),(50-16-8+4+2),(50-16-8+4-2),(50-16-8+4+2),(50-16+8-4-2),(50-16+8-4+2),(50-16+8+4-2),(50-16+8+4+2),(50+16-8-4-2),(50+16-8-4+2),(50+16-8+4-2),(50+16-8+4+2),(50+16+8-4-2),(50+16+8-4+2),(50+16+8+4-2),(50+16+8+4+2)
I'm trying to use for loops and basic mathematics to get this sequence as an array but I'm still failing to get the accurate output, here is my code so far:
#!/bin/bash
N=$1
declare -a sequence=()
temp1=50
temp2=50
for i in $(eval echo "{1..$N}");do
for j in $(eval echo "{1..$N}");do
temp1=$((temp1+2**(5-j)))
temp2=$((temp2-2**(5-j)))
done
sequence+=($temp1)
sequence+=($temp2)
temp1=50
temp2=50
done
echo ${sequence[#]}
I don't know how to alternate between summation and subtraction, how can I approach this?
Ok so I am not really sure what it is that you are doing haha, but I wrote a script that generates the output you described..
N=${1}
sequence=()
math_sequence=()
if [ $N -eq 1 ]
then
math_sequence+=(50)
sequence+=(50)
else
for i in `seq 0 $(bc <<< "(2^(${N}-1)) - 1")`
do
X=50
Y=32
SIGNS=$(echo "obase=2;${i}" | bc | xargs printf "%0$((${N}-1))d\n" | sed 's/0/-/g; s/1/+/g')
MATH="$X"
VAL=$Y
for (( i=0; i<${#SIGNS}; i++ )); do
MATH+="${SIGNS:$i:1}"
VAL=$(bc <<< "$VAL / 2")
MATH+="${VAL}"
done
math_sequence+=( "(${MATH}), " )
sequence+=( $(bc <<< "${MATH}") )
done
fi
echo ${math_sequence[#]}
echo "----------------"
echo ${sequence[#]}
Some tricks I used here..
I saw that the +/- pattern kinda looked like binary counting: ----,---+,--+-,--++...+++-,++++ So I just made a binary counter and used the 0's and 1's as - and +.
bc <<< "${EQUATION}" is much more reliable than $(( ${EQUATION} )). At least I like it better. Works for larger numbers, uses ^ instead of ** for exponents. My fav
I generate two arrays for ya... math_sequence which contains the list of equations, and sequence which contains the actual values. I was not sure which one you actually wanted so I gave you both.
The script is pretty configurable. Just change X and Y in the for loop and you can tweak this thing to make all sorts of numbers.
bash thisScript.sh <N> Will generate the output you described:
N = 1; sequence = 50
N = 2; sequence = (50-16),(50+16)
N = 3; sequence = (50-16-8),(50-16+8),(50+16-8),(50+16+8)
N = 4; sequence = (50-16-8-4),(50-16-8+4),(50-16+8-4),(50-16+8+4),(50+16-8-4),(50+16-8+4),(50+16+8-4),(50+16+8+4)
N = 5; sequence = (50-16-8-4-2),(50-16-8-4+2),(50-16-8+4-2),(50-16-8+4+2),(50-16-8+4-2),(50-16-8+4+2),(50-16+8-4-2),(50-16+8-4+2),(50-16+8+4-2),(50-16+8+4+2),(50+16-8-4-2),(50+16-8-4+2),(50+16-8+4-2),(50+16-8+4+2),(50+16+8-4-2),(50+16+8-4+2),(50+16+8+4-2),(50+16+8+4+2)
I have the following pattern. I want to express 'a' as a function of n.
if n=0 then a=0
if n=1 then a=0
if n=2 then a=3
if n=3 then a=3
if n=4 then a=10
.
.
.
if n=10 then a=10
if n=11 then a=29
.
.
.
if n=29 then a=29
if n=30 then a=66
.
.
.
if n=66 then a=66
if n=67 then a=127
.
.
AS you can see the value of a remains the same till a value matches with n. After which the value of a changes and again this value holds till a<=n. I found the formula with which this pattern occurrs. It is
a = 1^3 + 2 when n<=3
a = 2^3 + 2 when n > 3 and n <=10
and so on.
How to express a as a function of n ?
like f(n) = {___ <condition>
You can apply the inverse of the formula n^3-2, round up, and then apply the formula again, to get the correct sequence. The values for 0, 1 and 2 have to be hard-coded, though.
Note: in languages with typed numbers, make sure the result of the cubic root is a float; if it is converted to int automatically, it will be rounded down in the conversion.
function calculate(n) {
if (n <= 1) return 0;
if (n == 2) return 3;
return Math.pow(Math.ceil(Math.pow(n - 2, 1 / 3)), 3) + 2;
}
for (var i = 0; i < 70; i++) {
document.write(i + "→" + calculate(i) + " ; ");
}
Addendum: as Stefan Mondelaers commented, you have to be careful when relying on floating point maths. The code above makes use of the fact that cubic roots of third powers are always slightly underestimated in JavaScript (at least in all current browsers I tested); e.g. the largest third power within JavaScript's safe integer range is 4,503,569,204,744,000 but instead of its cubic root 165,140 you will get:
document.write(Math.pow(4503569204744000, 1/3));
If you're going to round off the results of floating point calculations, these very small errors may lead to bigger errors. The simplest work-around is indeed to add or subtract a very small value before rounding. For more information see e.g. this question.
I'm trying to split the remainder as evenly as possible where var is not divisible into the array count.
I've tried the following, which gives me a rounded split into the array item. I'm looking for a way to identify the remainder and then split that as evenly as possible into each array index value.
for n in ${!variableLengthArray[#]} ; do
divideCount=$(( ${variableLengthArray[$n]} / $var ))
variableLengthArray[$n]=$(echo "($divideCount+0.5)/1" | bc )
done
EXAMPLE1:
Input:
var=11
variableLengthArray[0]=0
variableLengthArray[1]=0
variableLengthArray[2]=0
Ideal Output:
variableLengthArray[0]=4
variableLengthArray[1]=4
variableLengthArray[2]=3
EXAMPLE2:
Input:
var=33
variableLengthArray[0]=0
variableLengthArray[1]=0
variableLengthArray[2]=0
variableLengthArray[3]=0
variableLengthArray[4]=0
variableLengthArray[5]=0
Ideal Output:
variableLengthArray[0]=6
variableLengthArray[1]=6
variableLengthArray[2]=6
variableLengthArray[3]=5
variableLengthArray[4]=5
variableLengthArray[5]=5
You just need to divide the input by the number of output slots. The shell only does integer division, so you the result will be the number to store in each slot. The remainder of the division tells you how many slots get the result plus one.
As a concrete example,
$ var=11
$ slots=3
$ result=$((var / slots))
$ k=$((var % slots ))
$ for ((i=0; i<k; i++)); do
> variableLengthArray[i]=$(( result + 1 ))
> done
$ for ((i=k; i < slots; i++)); do
> variableLengthArray[i]=$result
> done
Assuming that indexing of your array variable starts from 0 and is contiguous the following code will do what you want:
n=${#variableLengthArray[#]}
ratio=$(($var / $n))
rem=$(($var % $n))
for i in ${!variableLengthArray[#]} ; do
variableLengthArray[$i]=$(( $ratio + ($i < $rem ? 1 : 0) ))
done
I'm trying to generate a random number in between a specified range by using the $RANDOM function in the bash terminal. The problem is that the numbers it is generating don't appear to be random at all. The script I am using is:
RANDOM=$$;
a=$RANDOM
b=9; #Number of scripts in collection -1
c=`expr $a % $b `; #Random number between 0 and b
if (( c==0 ));
then
echo "script 1 (random = $a, mod = $c)";
elif (( c==1 ));
then
echo "stript 2 (random = $a, mod = $c)";
...
else
echo "error (random = $a, mod = $c)";
fi;
If I run this in a for in loop I get, for example:
script 8 (random = 17845, mod = 7)
script 8 (random = 18754, mod = 7)
script 8 (random = 19663, mod = 7)
script 7 (random = 20571, mod = 6)
script 7 (random = 21480, mod = 6)
script 6 (random = 22388, mod = 5)
script 6 (random = 23297, mod = 5)
script 6 (random = 24206, mod = 5)
script 5 (random = 25114, mod = 4)
script 5 (random = 26023, mod = 4)
Which clearly isn't random.
I tried removing the $a and just running
c=`expr $RANDOM % $b`;
and then altering the code to another variation
c=`expr $a \* $b \/ 32767`;
But these (unsurprisingly) returned the same result. What am I doing wrong? Or is this just a slightly irritating limitation for $RANDOM? Any advice would be greatly appreciated.
You kept seeding RANDOM with the same number. Try not to seed it or seed it with a more random item instead:
RANDOM=$$
Apparently $$ doesn't change always as it's always the main PID (not subshell PID) of your shell. If you're actually calling different shells, probably there isn't much difference since the numbers seeded by every PID increments only by ones. So either you could remove that or get another random seed somewhere like /dev/urandom, etc.
One good way to apply a random seed by /dev/urandom:
RANDOM=$(tr -dc 0-9 < /dev/urandom | head -c10)
Another through nanoseconds (seeding larger numbers than these seems to not give a good effect):
RANDOM=$(date '+%N')
Also to make it look more unique among different subprocesses, add BASHPID (better than $$) to your seed:
RANDOM=$(( BASHPID + $(date '+%N') ))
I think the explanation is to be found here:
When you use a modulus operation you are selecting information from
the low order bits of a number and discarding information from the
high order bits... The least significant (right-hand) digits of X are
not very random, so decisions based on the number X should always be
influenced primarily by the most significant digits.
And using this does work better for me (though I only tested a few times):
c=$(($a * $b / 32768))
Here's the revised script:
#!/bin/bash
RANDOM=$$;
a=$RANDOM
b=9; #Number of scripts in collection -1
c=$(($a * $b / 32768))
if (( c==0 )); then
echo "script 1 (random = $a, mod = $c)";
elif (( c==1 )); then
echo "script 2 (random = $a, mod = $c)";
else
echo "error (random = $a, mod = $c)";
fi;
Hope this helps.