Generate a random number with a seed between a range in ruby - ruby

I'm trying to get the banner image on my website to change once a day. I can see ruby has srand which will let me input a number for the date and return the same result every time it is called on the same day but this returns a very long number. I also saw rand lets me use a range like rand(a..b).
Is there any way I can use srand with a range like I can with rand?

You can create a special/designated random number generator with any seed value you like:
special = Random.new 42 # create a new instance of Random seeded with 42
20.times { p special.rand(5..10) } # generate 20 random ints in range 5 to 10
Your special instance of Random is independent of kernel#rand unless you use srand to initialize it with the same seed value.

You could use the date's Julian day number as the seed:
require 'date'
Date.new(2017, 6, 1).jd #=> 2457906
Date.new(2017, 6, 2).jd #=> 2457907
Date.new(2017, 6, 3).jd #=> 2457908
This can then be used to generate a random daily index:
def number_of_banners
10
end
def daily_banner_index(date = Date.today)
Random.new(date.jd).rand(number_of_banners)
end
daily_banner_index
#=> 8
Or a random daily element from an array:
def banners
%w(foo_banner bar_banner baz_banner)
end
def daily_banner(date = Date.today)
banners.sample(random: Random.new(date.jd))
end
daily_banner
#=> "bar_banner"

To avoid breaking the random number generator for the rest of my application I went with
(Date.today.to_s.gsub('-','').to_i) % number_of_banners
While not exactly random it should work well enough for this case but I would be interested in better solutions.

Related

How to generate a random number in Ruby from a specific minimum range to a maximum variable?

How to create a random number generator in Ruby? I know there are ways to do it like rand(0..9) to generate a random number from 0 to 9, or rand(0…9) to generate a random number from 0 to 8. But what if I say rand(0.. *variable*) to generate a random number from 0 to the value of that variable?
Look at the code below for a clearer example:
Suppose I’m creating a number guessing game
print “Enter a number: “
max_num = gets
max_num.to_i #converting it into an integer to print it out with strings
random_num = rand(0.. **max_num**)
max_num.to_i does not change the string to an integer. The string is still a string. The method to_i returns an integer that's not the string.
irb> s = "123"
irb> p s.to_i
123
irb> p s
"123"
What you need to do is to assign the return value to a variable (even the same variable will do) and use that variable, like
max_num = max_num.to_i
random_num = rand(0..max_num)
Actually, in Ruby, though there are lots of methods that can change the internal state of the receiver (like Array#map! and String#strip!), there's no method that can change the type of the receiver.

Sample an array with seed value to get consistent result

I am sampling several arrays and would like to add a seed value to get a consistent result every time its run, now and in the future.
My example:
constant_seed_value = 123456789
["a","b","c"].sample(seed: constant_seed_value ) should return "a" when run every time.
Just pass a Random.new with your seed to sample:
%w[a b c].sample(1, random: Random.new(123456789))
#=> ["a"]
See Array#sample and Random
You will need to make the seed a constant (or write it to a file and read it each time the program is run). I assume you don't care what the seed actually is.
If a side-calculation produces
Random.new_seed
#=> 44220669194288225494276674522501875094
you will write
SEED = 44220669194288225494276674522501875094
See Random::new_seed.
When the program is run you must initialize the seed to this value.
Random.srand(SEED)
#=> 129123040985656142450143558000073927364
See Random::srand.
Now let's compute some pseudo-random values.
arr = (1..1000).to_a
arr.sample(4)
#=> [762, 619, 41, 997]
rand
#=> 0.9619996498741139
rand
#=> 0.7952214967836931
Now restore the initial seed, as we would do if we re-ran the program.
Random.srand(SEED)
#=> 190782386885144604306184636344084340916
If we repeat the initial constuction of pseudo-random values we see they are the same as when we first computed them.
arr.to_a.sample(4)
#=> [762, 619, 41, 997]
rand
#=> 0.9619996498741139
rand
#=> 0.7952214967836931

Infinity is returned when calculating average in array

Why does the following method return infinity when trying to find the average volume of a stock:
class Statistics
def self.averageVolume(stocks)
values = Array.new
stocks.each do |stock|
values.push(stock.volume)
end
values.reduce(:+).to_f / values.size
end
end
class Stock
attr_reader :date, :open, :high, :low, :close, :adjusted_close, :volume
def initialize(date, open, high, low, close, adjusted_close, volume)
#date = date
#open = open
#high = high
#low = low
#close = close
#adjusted_close = adjusted_close
#volume = volume
end
def close
#close
end
def volume
#volume
end
end
CSV.foreach(fileName) do |stock|
entry = Stock.new(stock[0], stock[1], stock[2], stock[3], stock[4], stock[5], stock[6])
stocks.push(entry)
end
Here is how the method is called:
Statistics.averageVolume(stocks)
Output to console using a file that has 251 rows:
stock.rb:32: warning: Float 23624900242507002003... out of range
Infinity
Warning is called on the following line: values.reduce(:+).to_f / values.size
When writing average functions you'll want to pay close attention to the possibility of division by zero.
Here's a fixed and more Ruby-like implementation:
def self.average_volume(stocks)
# No data in means no data out, can't calculate.
return if (stocks.empty?)
# Pick out the `volume` value from each stock, then combine
# those with + using 0.0 as a default. This forces all of
# the subsequent values to be floating-point.
stocks.map(&:volume).reduce(0.0, &:+) / values.size
end
In Ruby it's strongly recommended to keep variable and method names in the x_y form, like average_volume here. Capitals have significant meaning and indicate constants like class, module and constant names.
You can test this method using a mock Stock:
require 'ostruct'
stocks = 10.times.map do |n|
OpenStruct.new(volume: n)
end
average_volume(stocks)
# => 4.5
average_volume([ ])
# => nil
If you're still getting infinity it's probably because you have a broken value somewhere in there for volume which is messing things up. You can try and filter those out:
stocks.map(&:value).reject(&:nan?)...
Where testing vs. nan? might be what you need to strip out junk data.

How do I create an exponential distribution in Ruby with the distribution gem?

I need the mean to be 2, with starting at 1 up to 10,000 random numbers.
require 'distribution'
mean = 2
# generate a rng with exponential distribution
rng = Distribution::Exponential.rng(mean)
Well, I'm not much of a statistician, so I'm a little lost in the code here.
Can you tell me, then, is the first parameter to rng (it is named l) supposed to stand for "limit"?
If so, I don't really know, I'm getting the same sort of results that you must be getting, but again, I'm not a statistician or even a mathematician, I just like to code, so I am probably not of too much help.
EDIT: So, again, I dont really know what is going on or is supposed to be going on here. Here is what gives me a relatively close mean of 2 (I got this just by messing around with the l value):
require 'distribution'
rng=Distribution::Exponential.rng(0.5)
sum=0
1000.times { sum += rng.call }
sum /= 1000
puts sum
Seems to give a value generally between 1.9 and 2.1. Hope this helps :)
PRINT EACH VALUE:
require 'distribution'
rng = Distribution::Exponential.rng(0.5)
values = [] # we'll store each value in an array
# calculate the results
how_many = 1000 # let's store 1000 in a variable since we'll reuse the value
how_many.times { values << rng.call } # rng is now a Proc, so we use call to get our value
# print out the results
values.each_with_index { |v,i| puts "#{i+1}: #{v}" } # print each value one by one
puts
puts "Total sum: #{values.inject(&:+)}"
puts "Mean: #{values.inject(&:+)/how_many}"
Again, not sure if this is exactly "right" or what you're looking for, but it definitely seems to approach 2. Try a bigger number than 1000, like 100000.

How do I generate a random 10 digit number in ruby?

Additionally, how can I format it as a string padded with zeros?
To generate the number call rand with the result of the expression "10 to the power of 10"
rand(10 ** 10)
To pad the number with zeros you can use the string format operator
'%010d' % rand(10 ** 10)
or the rjust method of string
rand(10 ** 10).to_s.rjust(10,'0')
I would like to contribute probably a simplest solution I know, which is a quite a good trick.
rand.to_s[2..11]
=> "5950281724"
This is a fast way to generate a 10-sized string of digits:
10.times.map{rand(10)}.join # => "3401487670"
The most straightforward answer would probably be
rand(1e9...1e10).to_i
The to_i part is needed because 1e9 and 1e10 are actually floats:
irb(main)> 1e9.class
=> Float
DON'T USE rand.to_s[2..11].to_i
Why? Because here's what you can get:
rand.to_s[2..9] #=> "04890612"
and then:
"04890612".to_i #=> 4890612
Note that:
4890612.to_s.length #=> 7
Which is not what you've expected!
To check that error in your own code, instead of .to_i you may wrap it like this:
Integer(rand.to_s[2..9])
and very soon it will turn out that:
ArgumentError: invalid value for Integer(): "02939053"
So it's always better to stick to .center, but keep in mind that:
rand(9)
sometimes may give you 0.
To prevent that:
rand(1..9)
which will always return something withing 1..9 range.
I'm glad that I had good tests and I hope you will avoid breaking your system.
Random number generation
Use Kernel#rand method:
rand(1_000_000_000..9_999_999_999) # => random 10-digits number
Random string generation
Use times + map + join combination:
10.times.map { rand(0..9) }.join # => random 10-digit string (may start with 0!)
Number to string conversion with padding
Use String#% method:
"%010d" % 123348 # => "0000123348"
Password generation
Use KeePass password generator library, it supports different patterns for generating random password:
KeePass::Password.generate("d{10}") # => random 10-digit string (may start with 0!)
A documentation for KeePass patterns can be found here.
Just because it wasn't mentioned, the Kernel#sprintf method (or it's alias Kernel#format in the Powerpack Library) is generally preferred over the String#% method, as mentioned in the Ruby Community Style Guide.
Of course this is highly debatable, but to provide insight:
The syntax of #quackingduck's answer would be
# considered bad
'%010d' % rand(10**10)
# considered good
sprintf('%010d', rand(10**10))
The nature of this preference is primarily due to the cryptic nature of %. It's not very semantic by itself and without any additional context it can be confused with the % modulo operator.
Examples from the Style Guide:
# bad
'%d %d' % [20, 10]
# => '20 10'
# good
sprintf('%d %d', 20, 10)
# => '20 10'
# good
sprintf('%{first} %{second}', first: 20, second: 10)
# => '20 10'
format('%d %d', 20, 10)
# => '20 10'
# good
format('%{first} %{second}', first: 20, second: 10)
# => '20 10'
To make justice for String#%, I personally really like using operator-like syntaxes instead of commands, the same way you would do your_array << 'foo' over your_array.push('123').
This just illustrates a tendency in the community, what's "best" is up to you.
More info in this blogpost.
I ended up with using Ruby kernel srand
srand.to_s.last(10)
Docs here: Kernel#srand
Here is an expression that will use one fewer method call than quackingduck's example.
'%011d' % rand(1e10)
One caveat, 1e10 is a Float, and Kernel#rand ends up calling to_i on it, so for some higher values you might have some inconsistencies. To be more precise with a literal, you could also do:
'%011d' % rand(10_000_000_000) # Note that underscores are ignored in integer literals
('%010d' % rand(0..9999999999)).to_s
or
"#{'%010d' % rand(0..9999999999)}"
I just want to modify first answer. rand (10**10) may generate 9 digit random no if 0 is in first place. For ensuring 10 exact digit just modify
code = rand(10**10)
while code.to_s.length != 10
code = rand(11**11)
end
Try using the SecureRandom ruby library.
It generates random numbers but the length is not specific.
Go through this link for more information: http://ruby-doc.org/stdlib-2.1.2/libdoc/securerandom/rdoc/SecureRandom.html
Simplest way to generate n digit random number -
Random.new.rand((10**(n - 1))..(10**n))
generate 10 digit number number -
Random.new.rand((10**(10 - 1))..(10**10))
This technique works for any "alphabet"
(1..10).map{"0123456789".chars.to_a.sample}.join
=> "6383411680"
Just use straightforward below.
rand(10 ** 9...10 ** 10)
Just test it on IRB with below.
(1..1000).each { puts rand(10 ** 9...10 ** 10) }
To generate a random, 10-digit string:
# This generates a 10-digit string, where the
# minimum possible value is "0000000000", and the
# maximum possible value is "9999999999"
SecureRandom.random_number(10**10).to_s.rjust(10, '0')
Here's more detail of what's happening, shown by breaking the single line into multiple lines with explaining variables:
# Calculate the upper bound for the random number generator
# upper_bound = 10,000,000,000
upper_bound = 10**10
# n will be an integer with a minimum possible value of 0,
# and a maximum possible value of 9,999,999,999
n = SecureRandom.random_number(upper_bound)
# Convert the integer n to a string
# unpadded_str will be "0" if n == 0
# unpadded_str will be "9999999999" if n == 9_999_999_999
unpadded_str = n.to_s
# Pad the string with leading zeroes if it is less than
# 10 digits long.
# "0" would be padded to "0000000000"
# "123" would be padded to "0000000123"
# "9999999999" would not be padded, and remains unchanged as "9999999999"
padded_str = unpadded_str.rjust(10, '0')
rand(9999999999).to_s.center(10, rand(9).to_s).to_i
is faster than
rand.to_s[2..11].to_i
You can use:
puts Benchmark.measure{(1..1000000).map{rand(9999999999).to_s.center(10, rand(9).to_s).to_i}}
and
puts Benchmark.measure{(1..1000000).map{rand.to_s[2..11].to_i}}
in Rails console to confirm that.
An alternative answer, using the regexp-examples ruby gem:
require 'regexp-examples'
/\d{10}/.random_example # => "0826423747"
There's no need to "pad with zeros" with this approach, since you are immediately generating a String.
This will work even on ruby 1.8.7:
rand(9999999999).to_s.center(10, rand(9).to_s).to_i
A better approach is use Array.new() instead of .times.map. Rubocop recommends it.
Example:
string_size = 9
Array.new(string_size) do
rand(10).to_s
end
Rubucop, TimesMap:
https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Performance/TimesMap
In my case number must be unique in my models, so I added checking block.
module StringUtil
refine String.singleton_class do
def generate_random_digits(size:)
proc = lambda{ rand.to_s[2...(2 + size)] }
if block_given?
loop do
generated = proc.call
break generated if yield(generated) # check generated num meets condition
end
else
proc.call
end
end
end
end
using StringUtil
String.generate_random_digits(3) => "763"
String.generate_random_digits(3) do |num|
User.find_by(code: num).nil?
end => "689"(This is unique in Users code)
I did something like this
x = 10 #Number of digit
(rand(10 ** x) + 10**x).to_s[0..x-1]
Random 10 numbers:
require 'string_pattern'
puts "10:N".gen

Resources