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)
Related
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"
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.
A give number x is 'good' if the sum of any two consecutive digit of the number x are between k and 2k.
I need to find an algorithm that for a given number k and a given number n, find how many 'good' n-digit numbers exist.
I made an implementation for this in PHP, but the complexity is to big (i am searching for all those 'good' number and counting them, so the complexity is O(10^n)).
<?php
$n = 5;
$k = 5;
$min = $k*1;
$max = $k*2;
$counter = 0;
for ($i = pow(10, $n-1); $i<pow(10,$n); $i++)
{
$number = $i;
$prev = $number % 10;
$number = $number / 10;
while($number >= 10)
{
$crnt = $number % 10;
$number = $number / 10;
if ( ($crnt+$prev) > $min AND ($crnt+$prev) < $max ) {
echo "good number: $i\n";
$counter++;
}
$prev = $crnt;
}
}
echo "counter: ".$counter."\n";
?>
Can someone confirm me if this can be the solution:
n=100 // given
k=10 // given
counter = 0;
for(i=10; i<100; i++)
{
if( (i/10)+(i%10) > k ) && ( (i/10)+(i%10) < 2*k )
counter++;
}
total = counter^(n-1)
All those calls to pow certainly won't be helping.
What you can do is make a mapping of all the two-digit numbers that are 'good'. Once you have your mapping, all you need to do is check that every pair of digits in your number is good. You can do this by successive division by 10 and modulo 100.
Something like this would do the trick, provided you don't give it a negative number, and assuming you've set up your $good array.
function isgood( $num ) {
while( $num >= 100 && $good[$num%100] ) {
$num /= 10;
}
return $good[$num%100];
}
The next most obvious thing to do is memoize larger sequences. This is a dynamic programming principle. We've already memoized small sequences by storing the 'goodness' of 2-digit sequences. But you could easily use those to generate sequences of 3, 4, 5, 6 digits... Whatever your available memory allows. Use the memos you already have in order to generate the sequences with one extra digit.
So, if you built up memoisation for up to 5-digit numbers, then you divide by 1000 every time, and get a great speedup.
Finding the number of "not good numbers" with n digits and top digit d is a straightforward dynamic programming problem.
10^n is the number of "good numbers" plus "not good numbers".
I will give you no more help than that.
Your algorithm counts how many 2 digit integers are NOT good. Then it returns this value to the power of n - 1. This should get you the number of n digit numbers that are NOT good. If you subtract this value from the total amount of n digit integers, you should get what you want. Or we could avoid doing this by changing the signs:
for($i=10; $i<100; $i++) {
if( ($i/10) + ($i%10) > $k && ($i/10) + ($i%10) < 2*$k ) {
$cnt++;
}
}
$result = pow($cnt, $n-1);
This should get you the number of good n digit integers, but let's see if that's really the case.
Well, cnt will give the number of good 2 digit integers. So, on the first two positions, we can put any of these cnt:
0 1 2 3 4 5 ...
x y
Then, what about positions 1 and 2? Well, position 1 is fixed by the first placement.
0 1 2 3 4 5 ...
x y
y z
So we have to prove that there are cnt possibilities for z, and I don't see why this should be the case, so I would say that the algorithm is wrong. Your algorithm will probably overcount.
for X in {18..2500} ; is one line of my script, which means to pick number one by one like: 18,19,20,21,22,23....till 2500
However I find I only need even number right now: 18,20,22,24.....2500
Then what should I do by a slight modify of the line?
Thanks
edit:
It's bash...
My script is now changed to:
#!/bin/bash
TASK=1101;
NUM=9;
TEND=1100;
for X in {18..2500};{
if (X % 2 == 0);
do
echo "$X echo \"Wait until $NUM job is done\" $NUM" ;
NUM=$((NUM+2)) ;
X=$((X+1)) ;
TEND=$((TEND+100)) ;
echo "$X -t $TASK-$TEND jobs.sh" ;
TASK=$((TASK+100)) ;}
done
but got errors like:
line 15: syntax error near unexpected token `do'
You can specify the increment:
for X in {18..2500..2}
A sequence expression takes the form {x..y[..incr]}, where x and y are
either integers or single characters, and incr, an optional increment, is
an integer.
Or
for X in `seq 18 2 2500`
This is not C++. This is a bash script.
Your for-loop needs to start with a do:
for X in {18..2500}; do
Your if-statement syntax looks off. It should probably be something like this, note the then:
if [[ $((X % 2)) == 0 ]]; then
if-blocks end with:
fi
And the for-do block ends with:
done
Better still... do away with the if statement and use Bash's for-loop construct to generate only even numbers:
for ((X = 18; X <= 2500; X += 2)); do
echo "$X echo \"Wait until $NUM job is done\" $NUM" ;
# ...
done
Try the modulus operator. In almost all languages, it'll look something like this:
if (x % 2 == 0) // …Do something
That is valid C code, but can easily be applied to other languages.
You can think of the mod operator as a division sign placed in the same location, but rather than returning the results of the division it returns the remainder. Therefore in this code, if the remainder of a divide-by-two is 0, then it divides evenly by two, and so it's even by definition.
If your language has a for(;;) syntax you can
for (X = 18; X <= 2500; X += 2)
There are a couple things you can do:
use the modulus function for your language:
for x in {18..2500} {
if (x mod 2=0) {
do something;}
step through your For loop 2 at a time:
for x in {18..2500} step 2 {
do something;}