loop to check if the multiples of a user defined number are even within a user defined range - bash

hey everyone I am trying to write a script in bash that will take a user-defined number and run its multiples up then check which of those multiples are even and print those only in a user-defined range. and the script seems to be working when an even number is selected as the value but when an odd number is selected it only prints out half of the numbers you wish to see. I think I know why it happens it is to do with my while statement with the $mult -le $range but I am not sure what to do to fix this or how to get it to show the full range for both even and odd based numbers. Any help is appreciated thanks.
code
#!/bin/sh
echo "Please input a value"
read -r val
echo "Please input how many multiples of this number whose term is even you wis>
read -r range
mult=1
while [ $mult -le $range ]
do
term=$(($val*$mult))
if [[ $(($term % 2)) -eq 0 ]]
then echo "$term"
fi
((mult++))
done
echo "For the multiples of $val these are the $range who's terms were even"
output even based number
$ ./test3.sh
Please input a value
8
Please input how many multiples of this number whose term is even you wish to see
4
8
16
24
32
For the multiples of 8 these are the 4 whose terms were even
output odd based number
$ ./test3.sh
Please input a value
5
Please input how many multiples of this number whose term is even you wish to see
10
10
20
30
40
50
For the multiples of 5 these are the 10 whose terms were even

Your current while condition assumes that the number of even multiples of the number val less than or equal to val * range is at least range. In fact, for even numbers, there are precisely range even multiples which are less than or equal to val * range. This is not the case for odd numbers - as you've encountered.
You'll need to introduce a new variable to solve this problem - one that keeps track of the number of even multiples you have encountered thus far. Once you reach the desired number, the loop should terminate.
So, you could set a counter initially
count=0
and check this in your while loop condition
while [ $count -lt $range ]
You would increment count each time you enter the body of the if - i.e. whenever you encounter an even multiple.
This should give you the desired behavior.

Related

bash script problem with understanding how shuf works

I have the following problem understanding this line of code
for NUMBER in $(shuf -i1-$MAX_NUMBER)
Do I understand correctly that I take subsequent numbers up to "$MAX_NUMBER" or the function "shuf -i1-" make any changes?
shuf -i1-$MAX_NUMBER prints a random permutation of the numbers in the range of 1 to $MAX_NUMBER (i.e, not subsequent).
This means that in each iteration of the loop, the value of $NUMBER will be a random value between 1 and $MAX_NUMBER, until all numbers have been used.

Solved: Grep and Dynamically Truncate at Same Time

Given the following:
for(condition which changes $z)
aptitude show $z | grep -E 'Uncompressed Size: |x' | sed s/Uncompressed Size: //";
done
That means 3 items are outputting to screen ($Z, Uncompressed Size, x).
I want all of that to fit on one line, and a line I deem is = 100 characters.
So, ($Z, Uncompressed Size, x) must fit on one line. But X is very long and will have to be truncated. So there is a requirement to add "used" characters by $z and Uncompressed Size, so that x can be truncated dynamically. I love scripting and being able to do this I deem an absolute must. Needless to say all 3 items being output to screen change hence the characters of the first two outputs must be calculated to subtract from the allowed characters for x, and sum of all characters between all 3 items cannot exceed 100 characters.
sed 's/.//5g'
Lmao, sometimes I wish I thought in simpler terms; complicated description + simple solution = simple problem over complicated by interpreter.
Thank you, Barmar
That only leaves sed (100 - amount of characters used by $z which is this function: ${#z}

Unexpected arithmetic result with zero padded numbers

I have a problem in my script wherein I'm reading a file and each line has data which is a representation of an amount. The said field always has a length of 12 and it's always a whole number. So let's say I have an amount of 25,000, the data will look like this 000000025000.
Apparently, I have to get the total amount of these lines but the zero prefixes are disrupting the computation. If I add the above mentioned number to a zero value like this:
echo $(( 0 + 000000025000 ))
Instead of getting 25000, I get 10752 instead. I was thinking of looping through 000000025000 and when I finally get a non-zero value, I'm going to substring the number from that index onwards. However, I'm hoping that there must be a more elegant solution for this.
The number 000000025000 is an octal number as it starts with 0.
If you use bash as your shell, you can use the prefix 10# to force the base number to decimal:
echo $(( 10#000000025000 ))
From the bash man pages:
Constants with a leading 0 are interpreted as octal numbers. A leading 0x or 0X denotes hexadecimal. Otherwise, numbers take the form [base#]n, where the optional base is a decimal number between 2 and 64 representing the arithmetic base, and n is a number in that base.
Using Perl
$ echo "000000025000" | perl -ne ' { printf("%d\n",scalar($_)) } '
25000

Why is $RANDOM not very random?

I've been using $RANDOM to generate a random number between 1-15, in order to generate a little jitter between two systems. For example:
sleep $(( RANDOM %= 15 ))
If I run echo $(( RANDOM %= 15 )) every few minutes, it seems the random numbers are fairly random. But if I start running a script with this call every minute via cron, or even just echo the random number every few seconds, the randomness is goneā€”on my Mac, I end up with not-so-random values like 11 and 6, alternating, or 8, 4, and 2, in sequence. Not very random.
On one of my linux servers (CentOS 6.5 x64), I added the following bash script, which, after the first couple loops, just output 13 over and over again:
#!/bin/bash
for ((n = 0; n < 100; n++))
do
echo $(( RANDOM %= 15 ))
done
My questions:
Why is this happening? I know $RANDOM is unsuitable for encryption, but why is it so bad at generating random numbers in general?
Is there any other easy way to get a more random number (even if needed in rapid succession) via bash script?
Assigning to RANDOM Sets the Seed Value
RANDOM is a special variable that provides a pseudo-random number. By using a modulo operator, you are vastly restricting the possible values, and by assigning to RANDOM you are changing the seed value to some member of your restricted set, which eventually seems to settle on 13.
The following gives me a reasonable distribution:
for i in {1..100}; do echo $(( RANDOM % 15 )); done
By using %= instead of just %, you are setting the seed value. Don't do that.
You can generate a high quality random integer between 1 and 15 using to following command :
echo "$(od -An -N4 -tu4 /dev/urandom) % 15 + 1" | bc
or even better
echo "$(od -An -N4 -tu4 /dev/random) % 15 + 1" | bc
Moreover you should not assign a new seed using %= as it destroys the entropy gathered.
Lastly 100 generations is really not enough to assess the quality of a random number generator. You should at least generate one million values.
Few handy techniques useful to test the quality of a random number generator :
Optimum compression -- 0 %
Data compression or source coding is the process of encoding information using fewer bits (or other information-bearing units) than an unencoded representation would use, through use of specific encoding schemes. If the same data structure is repeated multiple times a short binary representation can stand in for long data structures and thus reduce the size of the compresses file. If our random data is truly random then we should NOT see any compression at all.
Chi square distribution -- between 10% and 90%
The chi-square test is the most commonly used test for the randomness of data, and is extremely sensitive to errors in pseudorandom sequence generators. The chi-square distribution is calculated for the stream of bytes in the file and expressed as an absolute number and a percentage which indicates how frequently a truly random sequence would exceed the value calculated. We interpret the percentage as the degree to which the sequence tested is suspected of being non-random. If the percentage is greater than 90% or less than 10%, the sequence is almost certainly not random.
Arithmetic mean -- 127.5. 15/2 in your case
This is simply the result of summing the all the bytes in the file and dividing by the file length. If the data is close to random, this should be about 127.5 . If the mean departs from this value then the values are consistently high or low.
Monte Carlo value for Pi -- 3.14159265
Each successive sequence of six bytes is used as 24 bit X and Y co-ordinates within a square. If the distance of the randomly-generated point is less than the radius of a circle inscribed within the square, the six-byte sequence is considered a hit. The percentage of hits can be used to calculate the value of Pi. For very large streams the value will approach the correct value of Pi if the sequence is close to random.
Serial correlation coefficient -- 0.0
This quantity measures the extent to which each byte in the file depends upon the previous byte. For random sequences, this value (this can be positive or negative) will, of course, be close to zero.
source : https://calomel.org/

Getting free range inside another range

editing my question from the beginning since it was not clear enough:
Suppose you have a range from 1 to 1000.
Consider that some numbers in this range are reserved (this is dynamic).
E.g. 5, 45,670, 350. (i)
I want to get a 5-number continuous block of numbers inside range 1..1000, ensuring that this block of numbers does not include any reserved numbers. If this exists of course.
If (i) is the list of allocated numbers, the first block is 6,7,8,9,10. It can't be 1,2,3,4,5 because 5 is reserved.
I think it's more clear now :)
I think it should be a for loop examining all numbers from 1 to 995, and checking for each number whether start number is reserved - if not, examine if the 4 following numbers are also reserved. If not, we have a block. If yes, continue to the next unallocated number and check the 4 digits following up Again and again. When first free block is met, break the loop and store it!
#!/bin/sh
rs=(5 45 670 350)
for co in {1..1000}
do
oa+=($co)
for ec in ${rs[*]}
do
let co==ec && unset oa
done
let ${#oa[*]}==5 && break
done
echo ${oa[*]}

Resources