I would like to create four strings, each with a random length, but their total length should be 10. So possible length combinations could be:
3 3 3 1
or
4 0 2 2
Which would then (respectively) result in strings like this:
111 222 333 4
or
1111 33 44
How could I do this?
$RANDOM will give you a random integer in range 0..32767.
Using some arithmetic expansion you can do:
remaining=10
for i in {1..3}; do
next=$((RANDOM % remaining)) # get a number in range 0..$remaining
echo -n "$next "
((remaining -= next))
done
echo $remaining
Update: to repeat the number N times, you can use a function like this:
repeat() {
for ((i=0; i<$1; i++)); do
echo -n $1
done
echo
}
repeat 3
333
Here is an algorithm:
Make first 3 strings with random length, which is not greater than sum of lenght (each time substract it). And rest of length - it's your last string.
Consider this:
sumlen=10
for i in {1..3}
do
strlen=$(($RANDOM % $sumlen)); sumlen=$(($sumlen-$strlen)); echo $strlen
done
echo $sumlen
This will output your lengths, now you can create strings, suppose you know how
alternative awk solution
awk 'function r(n) {return int(n*rand())}
BEGIN{srand(); s=10;
for(i=1;i<=3;i++) {a=r(s); s-=a; print a}
print s}'
3
5
1
1
srand() to set a randomized seed, otherwise will generate the same random numbers each time.
Here you can combine the next task of generating the strings into the same awk script
$ awk 'function r(n) {return int(n*rand())};
function rep(n,t) {c="";for(i=1;i<=n;i++) c=c t; return c}
BEGIN{srand(); s=10;
for(j=1;j<=3;j++) {a=r(s); s-=a; printf("%s ", rep(a,j))}
printf("%s\n", rep(s,j))}'
generated
1111 2 3 4444
Related
I want to generate a random nunmber between some values but except some values.
I have this:
shuf -i 1-9 -n 1
Or this:
$(( ( RANDOM % 10 ) + 1 ))
But i want to exclude the number 3 and the number 8, for example.
Practical General Solution
Instead of specifying a range of numbers you can specify each valid number individually using the -e option (at least for shuf from GNU coreutils) or you can specify the numbers in an (anonymous) file. Since the last option is more portable (works also for shuffle on BSD), we will use that. Now we only have to generate output with the numbers 1, 2, 4, 5, 6, 7, 9. Here are some examples:
shuf -n 1 <(seq 9 | grep -Fxv -e{3,8})
shuf -n 1 <(printf %s\\n 1 2 4 5 6 7 9)
shuf -n 1 <(printf %s\\n 1 2 {4..7} 9)
shuf -n 1 <(seq 2; seq 4 7; echo 9)
Just For Fun
For each finite set S={i1,i2,…in}⊂ℕ of integers you can come up (by hand) with a polynomial f such that f(x)=ix for all x∈{0,1,…,n-1}.
In general, polynomial f has degree n-1. Sometimes we can use rounding to reduce the degree. In your concrete case of S={1,2,4,5,6,7,9} we can use f(x) = floor(1.25x+1.5). Here is an implementation in bash using bc (the (…) / 1 rounds down).
bc <<< "($RANDOM % 7 * 1.25 + 1.5) / 1"
A benefit of this method is that it works purley with built-ins if we scale all numbers inside the formula such that they become integers (here we scaled by a factor of 4).
echo $(( ($RANDOM % 7 * 5 + 6) / 4 ))
Here is one way:
until N=$(( ( RANDOM % 10 ) + 1 )); (( $N != 3 && $N != 8 )); do true; done; echo $N
One could argue that this way is imperfectly efficient, but in the absence of an obvious, simple alternative, it should suit.
A function?
rnd() {
local n=$(( $RANDOM % 10 ))
while [[ $n =~ [038] ]]
do n=$(( $RANDOM % 10 ))
done
echo $n
}
Then you can just say x=$( rnd ).
A simple way to select a random value from any set of values is to put the values in an array and select a random item from the array. In this case:
values=(1 2 {4..7} 9)
random_value=${values[RANDOM % ${#values[*]}]}
Also see Select a random item from an array.
The code above has some limitations. First, it doesn't work if the number of values to select from is greater than 32768, because the maximum value of $RANDOM is 32767. (The size of the array could become a problem for numbers greater than that anyway.) Second, the value returned by RANDOM % ${#values[*]} is (usually slightly) biased towards the lower array indices. See Why do people say there is modulo bias when using a random number generator? for more information. If that matters to you, see the rand function in BashFAQ/026 (How can I randomize/shuffle the order of lines in a file? ...) for Bash code that generates unbiased (pseudo)random numbers in a restricted range.
Consider a three line input file containing four unique numbers (1,2,3,4) such that each line represents the position of one number relative to another number.
So for example in the following input set, 4 is next to 2, 2 is next to 3, and 1 is next to 4.
42
23
14
So given that how would a script assemble all four numbers in such a way that it maintains each numbers known relationship?
In other words there are two answers 1423 or 3241 but how to arrive at that programmatically?
Not very sensible or efficient, but fun (for me, at least) :-)
This will echo all the permutations using GNU Parallel:
parallel echo {1}{2}{3}{4} ::: {1..4} ::: {1..4} ::: {1..4} ::: {1..4}
And add some grepping on the end:
parallel echo {1}{2}{3}{4} ::: {1..4} ::: {1..4} ::: {1..4} ::: {1..4} | grep -E "42|24" | grep -E "23|32" | grep -E "14|41"
Output
1423
3241
Brute forcing the luck:
for (( ; ; ))
do
res=($(echo "42
23
14" | shuf))
if ((${res[0]}%10 == ${res[1]}/10 && ${res[1]}%10 == ${res[2]}/10))
then
echo "success: ${res[#]}"
break
fi
echo "fail: ${res[#]}"
done
fail: 42 14 23
fail: 42 23 14
fail: 42 14 23
success: 14 42 23
For 3 numbers, this approach is acceptable.
Shuf shuffles the input lines and fills the array res with the numbers.
Then we take to following numbers and test, if the last digit of the first matches the first digit of the next, and for the 2nd and 3rd number accordingly.
If so, we break with a success message. For debugging, a failure message is better than a silent endless loop.
For longer chains of numbers, a systematic permutation might be better to test and a function to check two following numbers, which can be called by index or better a loop would be suitable.
I am trying to create a script which calculates the average over a number of rows.
This number would depend on the number of samples that I have, which varies.
An example of these files is here:
24 1 2.505
24 2 0.728
24 3 0.681
48 1 2.856
48 2 2.839
48 3 2.942
96 1 13.040
96 2 12.922
96 3 13.130
192 1 50.629
192 2 51.506
192 3 51.016
The average is calculated on the 3rd column and,
the second column indicates the number of samples, 3 in this particular case.
Therefore, I should obtain 4 values here.
One average value per 3 rows.
I have tried something like:
count=3;
total=0;
for i in $( awk '{ print $3; }' ${file} )
do
for j in 1 2 3
do
total=$(echo $total+$i | bc )
done
echo "scale=2; $total / $count" | bc
done
But it is not giving me the right answer, instead I think it calculates an average per each group of three rows.
The average is calculated on the 3rd column and,
the second column indicates the number of samples, 3 in this particular case.
Therefore, I should obtain 4 values here.
One average value per 3 rows.
I have tried something like:
count=3;
total=0;
for i in $( awk '{ print $3; }' ${file} )
do
for j in 1 2 3
do
total=$(echo $total+$i | bc )
done
echo "scale=2; $total / $count" | bc
done
But it is not giving me the right answer, instead I think it calculates an average per each group of three rows.
Expected output
24 1.3046
48 2.879
96 13.0306
192 51.0503
You can use the following awk script:
awk '{t[$2]+=$3;n[$2]++}END{for(i in t){print i,t[i]/n[i]}}' file
Output:
1 17.2575
2 16.9988
3 16.9423
This is better explained as a multiline script with comments in it:
# On every line of input
{
# sum up the value of the 3rd column in an array t
# which is is indexed by the 2nd column
t[$2]+=$3
# Increment the number of lines having the same value of
# the 2nd column
n[$2]++
}
# At the end of input
END {
# Iterate through the array t
for(i in t){
# Print the number of samples along with the average
print i,t[i]/n[i]
}
}
Apparently I brought a third view to the problem. In awk:
$ awk 'NR>1 && $1!=p{print p, s/c; c=s=0} {s+=$3;c++;p=$1} END {print p, s/c}' file
24 1.30467
48 2.879
96 13.0307
192 51.0503
In bash, i need to add (sum) one to a five digit number. The 5 digit number is extracted from a string.
Ex:
To get the 5 digits from the string that resides in a txt file:
#!/bin/bash
SUBSTRING_LATEST=$(echo $MYFILE | cut -c15-19)
That line returns: 00016
I'm trying to sum 1 to that number and get the final result like 00017
Tried this, but no luck
SUM_FINAL=$((SUBSTRING_LATEST + 1))
But that returns 15
I really appreciate any help
Thanks
You can use base 10 arithmetic otherwise any number starting with a 0 is interpreted as octal number:
s='00016'
printf "%05d" $((10#$s + 1))
00017
Using octal arithmetic you will get:
printf "%05d" $(($s + 1))
00015
How can print a value, either 1, 2 or 3 (at random). My best guess failed:
#!/bin/bash
1 = "2 million"
2 = "1 million"
3 = "3 million"
print randomint(1,2,3)
To generate random numbers with bash use the $RANDOM internal Bash function:
arr[0]="2 million"
arr[1]="1 million"
arr[2]="3 million"
rand=$[ $RANDOM % 3 ]
echo ${arr[$rand]}
From bash manual for RANDOM:
Each time this parameter is
referenced, a random integer between 0
and 32767 is generated. The sequence
of random numbers may be initialized
by assigning a value to RANDOM. If
RANDOM is unset,it loses its
special properties, even if it is
subsequently reset.
Coreutils shuf
Present in Coreutils, this function works well if the strings don't contain newlines.
E.g. to pick a letter at random from a, b and c:
printf 'a\nb\nc\n' | shuf -n1
POSIX eval array emulation + RANDOM
Modifying Marty's eval technique to emulate arrays (which are non-POSIX):
a1=a
a2=b
a3=c
eval echo \$$(expr $RANDOM % 3 + 1)
This still leaves the RANDOM non-POSIX.
awk's rand() is a POSIX way to get around that.
64 chars alpha numeric string
randomString32() {
index=0
str=""
for i in {a..z}; do arr[index]=$i; index=`expr ${index} + 1`; done
for i in {A..Z}; do arr[index]=$i; index=`expr ${index} + 1`; done
for i in {0..9}; do arr[index]=$i; index=`expr ${index} + 1`; done
for i in {1..64}; do str="$str${arr[$RANDOM%$index]}"; done
echo $str
}
~.$ set -- "First Expression" Second "and Last"
~.$ eval echo \$$(expr $RANDOM % 3 + 1)
and Last
~.$
Want to corroborate using shuf from coreutils using the nice -n1 -e approach.
Example usage, for a random pick among the values a, b, c:
CHOICE=$(shuf -n1 -e a b c)
echo "choice: $CHOICE"
I looked at the balance for two samples sizes (1000, and 10000):
$ for lol in $(seq 1000); do shuf -n1 -e a b c; done > shufdata
$ less shufdata | sort | uniq -c
350 a
316 b
334 c
$ for lol in $(seq 10000); do shuf -n1 -e a b c; done > shufdata
$ less shufdata | sort | uniq -c
3315 a
3377 b
3308 c
Ref: https://www.gnu.org/software/coreutils/manual/html_node/shuf-invocation.html