assign a random number to the variable [duplicate] - bash

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

Related

How to generate and sum up a sequence

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"

calculating a sequence that alternates between summation and subtraction

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)

Calculate all possible sum combinations in a given array of space delimited numbers using BASH [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Is there a way to calculate all possible sum combinations in a given array of space delimited 'n' numbers using pure BASH?
example: 2 3 4....n
Output: 5 7 6 9 and so on
Use an indicator to tell what elements to sum. You can implement it as an array of ones and zeros, where 1 means include in the sum. To walk around all the combinations, just start with an array of ones of the same length as the array of the numbers, implementing binary decrement is easy. Skip the computation if the indicator has only one 1, as you don't want the numbers alone included. To report each sum just once, use an associative array to hold the sums.
#!/bin/bash
sum () {
sum=0
for n in "$#" ; do
((sum += n ))
done
printf %d "$sum"
}
numbers=(2 3 4)
indicator=()
for _i in "${numbers[#]}" ; do
indicator+=(1)
done
declare -A sums
while si=$(sum "${indicator[#]}") ; (( si > 0 )) ; do
if (( si != 1 )) ; then
sum=0
for ((i=0; i<${#numbers[#]}; ++i)) ; do
(( indicator[i] && (sum+=numbers[i]) ))
done
sums[$sum]=1
fi
# Binary decrement.
i=0
until (( indicator[i] || i > ${#indicator[#]} )) ; do
indicator[i++]=1
done
indicator[i]=0
done
echo "${!sums[#]}"
This is one way you can do it but it requires Python3 :
import itertools
numbers = [2, 3, 4]
print(list(itertools.chain(*list(map(lambda x: x,
[list(map(sum, list(itertools.combinations(numbers,x)))) for x in range(2, len(numbers) +1)]
)))))
Will print :
[5, 6, 7, 9]
I don't think Bash is the right tool for this if you want it to be dynamic and fast.

Divide as evenly as possible the value defined in var into the a variable length array

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

Unix $RANDOM function not random in Bash terminal

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.

Resources