I want to generate the most probable number depending on the latest lottery results I have on a CSV.
I have this script:
h = Hash.new
f = File.open('numbers.csv', "r")
f.each_line { |line|
numbers = line.split
numbers.each { |w|
if h.has_key?(w)
h[w] = h[w] + 1
else
h[w] = 1
end
}
}
# sorteamos el hash por valor, y lo pintamos segĂșn la concurrencia
h.sort{|a,b| a[1]<=>b[1]}.each { |elem|
puts "\"#{elem[0]}\" tiene #{elem[1]} concurrencia"
}
That will tell me which numbers have the most ocurrencies.
I want to "sample" a number depending on these results that has the most probability.
How can I achieve this? Thanks!
I don't think Ruby has a built-in elegant way of doing this. You can treat your hash as a bunch of bins, where the number of occurrences of each number is the bin size. Then you can calculate the total bin width, get a random sample, and iterate to find out which bin the sample lands in.
def weighted_sample h
weight = h.values.reduce(:+)
sample = rand weight
h.each do |n, w|
return n if sample < w
sample -= w
end
end
Array.new(10) { weighted_sample({1 => 8, 2 => 4, 3 => 2}) }
# [2, 2, 1, 1, 1, 3, 1, 1, 1, 1]
Tip: there's a much nicer way to build that hash
h = Hash.new 0
# ...
h[w] += 1
Related
So I made a random number generator which is supposed to count the frequency of the numbers and display them in sorted order. I'm trying to use .sort but I can't figure out where to put it to sort the values of the hash in order. What I have so far:
MIN_VALUE = 1
count = 0
puts "Enter a number of random integers to generate"
resp = gets.to_i
p "number of integers generated is #{resp}"
puts "Now enter the maximum value the integers can be"
max_value = gets.to_i
p "max value is set to #{max_value}"
size = Array.new(resp)
while (count < resp)
int_value = (rand(MIN_VALUE..max_value))
size.push(int_value)
count = count + 1
end
puts size
freq = Hash.new(0)
size.each { |x| freq[x] += 1 }
freq.map{ |key, value| "#{key}x#{value}" }.join(',')
freq.each do |key,value|
puts "Frequency of #{key} is: #{value}"
end
Any help is greatly appreciated!
More or less the same soup, generating random numbers in an Integer#times loop:
upper_number = 10
sample_size = 100
freq = Hash.new(0) # initializing the hash with a default value of zero, for counting
sample_size.times { freq[rand((1..upper_number))] += 1 } # here the loop generating and counting
freq #=> {5=>13, 7=>10, 1=>11, 2=>13, 8=>13, 9=>6, 3=>6, 6=>9, 10=>11, 4=>8}
Then you can sort by frequencies (reverse order: -v) and by sample value (k), [-v, k]:
freq.sort_by{ |k, v| [-v, k] }.to_h #=> {2=>13, 5=>13, 8=>13, 1=>11, 10=>11, 7=>10, 6=>9, 4=>8, 3=>6, 9=>6} # for this run
freq.sum { |_, v| v} #=> 100 # of course
Suppose
arr = [4, 1, 3, 4, 2, 5, 1, 3, 4, 3, 4]
You can use the form of Hash::new that takes an argument, called its default value (which often, as here, is zero), to obtain the frequency of the elements of arr:
freq = arr.each_with_object(Hash.new(0)) { |n,h| h[n] += 1 }
#=> {4=>4, 1=>2, 3=>3, 2=>1, 5=>1}
We see that
freq[1]
#=> 2
freq[99]
#=> 0
The second result follows because freq was defined to have a default value of 0. All that means is that if freq does not have a key k, freq[k] returns zero (and that does not alter freq).
Here are solutions to two possible interpretations of your question. Both use the method Enumerable#sort_by.
Sort the unique values of arr by decreasing frequency
freq.sort_by { |_,v| -v }.map(&:first)
#=> [4, 3, 1, 2, 5]
Sort the values of arr by decreasing frequency
arr.sort_by { |n| -freq[n] }
#=> [4, 4, 4, 4, 3, 3, 3, 1, 1, 2, 5]
Replace -v and -freq[n] with v and freq[n] to sort by increasing frequency.
I've used the local variable _ to represent the keys in the first interpretation to signify that it is not used in the block calculation. This is common practice.
class Triplet
def initialize(array,sum)
#array = array.sort()
#array_size = array.size()
#sum = sum
#result = []
end
def get_triplet
#array[0..-3].each_with_index do |arr, ind|
pointer_one = ind + 1
pointer_two = #array_size - 1
while (pointer_one < pointer_two)
temp_sum = #array[pointer_one] + #array[pointer_two] + arr
if(temp_sum == #sum)
#result.push([#array[pointer_one], #array[pointer_two], arr])
elsif temp_sum < #sum
pointer_one = pointer_one +1
else
pointer_two = pointer_two -1
end
end
end
end
def get_result
#result.each do |res|
puts res
end
end
end
puts "Enter the array of numbers"
array = gets.chomp
array = array.split(' ')
array_integer = array.map{|a| a.to_i}
puts array_integer
puts "Enter the sum"
sum = gets.chomp
puts sum
t1 = Triplet.new(array_integer,sum.to_i)
t1.get_triplet
t1.get_result
Can anyone suggest me the fix so that it doesn't loop infinitly. It is program to find triplet in array whose sum is #sum. Its looping in get_triplet method. Initialize method sets the array,array size. get_triplet method should store all three number whose sum is #sum in result array.
Usually a tangle of code like this is a sign something's not right, and in this case the source of the problem is not knowing about the combination method. Here's a functionally equivalent solution:
def triplet(list, target)
list.combination(3).find do |a,b,c|
a + b + c == target
end
end
For example:
arr = [ 1, 2, 3, 4, 5, 6, 7, 8 ]
p triplet(arr, 6)
# => [1, 2, 3]
p triplet(arr, 4)
# => nil
p triplet(arr, 10)
# => [1, 2, 7]
The algorithm used in your code looks problematic, or at least implemented incorrectly, and is also strictly limited to triplets. This code is far more generic and uses a proven, tested algorithm, so it's probably better suited to solving your particular problem.
a = Array.new(200) { rand(1..100) }.sort!
puts a.count{ |a| (a > 0 && a < 11) }
puts a.count{ |b| (b > 10 && b < 21) }
puts a.count{ |c| (c > 20 && c < 31) }
puts a.inspect
So I can just repeat this all the way to 100 which works but I'm looking for a short cut to hopefully count all the digits pairs with the single line at the top(Add onto the single line). Hopefully each 10 (1-10, 11-20, etc) has a different variable that I can call later on. Or at-least be able to convert it.
make variables like
b = a.count{ |c| (c > 20 && c < 31) }
so later i could use it to make a string like
puts "#" * b
so the end result of it all will look like below
1-10 #########################
11-20 ##############################
21-30 ######################
each # representing how much 1-10 there were.
counts = 10.times.map { |i| (i + 1)...(i + 11) }.map { |range|
a.count { |x| range.include? x }
}
counts[0]
# => 17 (number of numbers in 1...11)
counts[1]
# => 20 (number of numbers in 11...21)
counts[2]
# => 22 (number of numbers in 21...31)
# ...
EDIT: Or even easier, by exploiting a simple mathematical property:
counts = a.group_by { |x| (x - 1) / 10 }.map { |k, v| v.count }
Hopefully each 10 (1-10, 11-20, etc) has a different variable that I can call later on.
Why would you want different variables when you can have an array?
EDIT2: Whole program, in 1-4 lines:
array = 200.times.map { rand(1..100) }.sort
array.group_by { |x| (x - 1) / 10 }.each do |k, v|
puts "%2d-%2d %s" % [k * 10 + 1, k * 10 + 10, "#" * v.count]
end
Translation: Do two hundred iterations, and construct an array, such that each element is a random number between 1 and 100. Then group the array by the result of division by 10 (adjusting by 1 so we don't get ranges of 0-9, 10-19...); iterating on the hash containing the division result and items, print the bounds (reconstructing the start and the end from the division result) and also a number of hashes corresponding to the number of items in each group.
EDIT3: As Stefan notes, we can use chunk because the array is sorted, for a bit of a speedup. group_by above doesn't care about the array being sorted. You can simply replace group_by with chunk even though they return different types, simply because iterating on hashes works the same way as iterating on array of two-element arrays.
array = 200.times.map { rand(1..100) }.sort
array.chunk { |x| (x - 1) / 10 }.each do |k, v|
puts "%2d-%2d %s" % [k * 10 + 1, k * 10 + 10, "#" * v.count]
end
I suggest using a counting hash.
(a = (Array.new(20){|x| x=(rand(1..100)) })).sort!
#=> [3, 12, 17, 17, 20, 31, 32, 43, 47, 50, 62, 65, 70, 83, 87, 89, 92, 94, 97, 98]
g = a.each_with_object(Hash.new(0)) do |n,h|
interval_first = 1+10*((n-1)/10)
h[interval_first..interval_first+9] += 1
end
#=> {1..10=>1, 11..20=>4, 31..40=>2, 41..50=>3, 61..70=>3, 81..90=>3, 91..100=>4}
Notice that
g[21..30] #=> 0
g[51..60] #=> 0
even though g does not have keys 21..30 and 51..60.
If intervals with values of zero are to be included in g, this is one way that could be done:
g = 10.times.map { |i| [1+10*i..10+10*i, 0] }.to_h
#=> {1..10=>0, 11..20=>0, 21..30=>0, 31..40=>0, 41..50=>0, 51..60=>0,
# 61..70=>0, 71..80=>0, 81..90=>0, 91..100=>0}
a.each_with_object(g) do |n,h|
interval_first = 1+10*((n-1)/10)
h[interval_first..interval_first+9] += 1
end
# => {1..10=>1, 11..20=>4, 21..30=>0, 31..40=>2, 41..50=>3, 51..60=>0,
# 61..70=>3, 71..80=>0, 81..90=>3, 91..100=>4}
See Hash::new.
Note that pre-sorting the array of random numbers is of no benefit with this method. That probably holds true with most approaches one might consider.
(0..100).step(10).each_cons(2).map do |min, max|
ar.count { |number| number.between?(min.next, max) }
end
Is there a more elegant way to achieve this below:
Input:
array = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0]
Output:
4
My algo:
streak = 0
max_streak = 0
arr.each do |n|
if n == 1
streak += 1
else
max_streak = streak if streak > max_streak
streak = 0
end
end
puts max_streak
Similar to w0lf's answer, but skipping elements by returning nil from chunk:
array.chunk { |x| x == 1 || nil }.map { |_, x| x.size }.max
Edit: Another way to do this (that is less generic than Stefan's answer since you would have to flatten and split again if there was another number other than 0 and 1 in there, but easier to use in this case):
array.split(0).max.count
You can use:
array.chunk { |n| n }.select { |a| a.include?(1) }.map { |y, ys| ys.count}.max
ref: Count sequential occurrences of element in ruby array
You can use Enumerable#chunk:
p array.chunk{|x| x}.select{|x, xs| x == 1}.map{|x, xs| xs.size }.max
This is more concise, but if performance was important, I'd use your approach.
Edit: If you're in Ruby 2.2.2, you can also use the new Enumerable#slice_when method (assuming your input array consists of only 0s and 1s):
array.slice_when{|x,y| x < y }.map{|slice| slice.count 1 }.max
How about
array = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0]
array.split(0).group_by(&:size).max.first #=> 4
The only bad thing - split(0)
Note: This only works with rails's ActiveSupport(extends Array with #split)
For ruby-only implementation
array.join.split("0").group_by(&:size).max.first #=> 4
I've been going at this problem for a few hours, and I can't see why I can't get it to run properly. The end game to this method is having 2 numbers in an array equaling zero when added together. Here is my code:
def two_sums(nums)
i = 0
j = -1
while i < nums.count
num_1 = nums[i]
while j < nums.count
num_2 = nums[j]
if num_1 + num_2 == 0
return "There are 2 numbers that sum to zero & they are #{num_1} and #{num_2}."
else
return "Nothing adds to zero."
end
end
i += 1
j -= 1
end
end
The problem I'm having is unless the first and last number in the array are the positive and negative of the same number, this will always return false.
For example, if I had an array that was [1, 4, 6, -1, 10], it should come back true. I'm sure my 2 while statement is the cause of this, but I can't think of a way to fix it. If someone could point me in the right direction, that would be helpful.
You can find the first pair that adds up to 0 like this:
nums.combination(2).find { |x, y| x + y == 0 }
#=> returns the first matching pair or nil
Or if you want to select all pairs that add up to 0:
nums.combination(2).select { |x, y| x + y == 0 }
#=> returns all matching pairs or an empty array
Therefore you can implement your method like this:
def two_sums(nums)
pair = nums.combination(2).find { |x, y| x + y == 0 }
if pair
"There are 2 numbers that sum to zero & they are #{pair.first} and #{pair.last}."
else
"Nothing adds to zero."
end
end
Or if you want to find all pairs:
def two_sums(nums)
pairs = nums.combination(2).select { |x, y| x + y == 0 }
if pairs.empty?
"Nothing adds to zero."
else
"The following pairs sum to zero: #{pairs}..."
end
end
Here's another way:
Code
def sum_to_zero(arr)
arr.group_by { |e| e.abs }
.values
.select { |a| (a.size > 1 && a.first == 0) || a.uniq.size > 1 }
end
Examples
sum_to_zero [1, 4, 6, -1, 10] #=> [[1, -1]]
sum_to_zero [1, 4, 1, -2, 10] #=> []
sum_to_zero [1, 0, 4, 1, 0, -1] #=> [[1, 1, -1], [0, 0]]
This method is relatively fast. Let's try it with an array of 200,000 elements, each a random number between -500,000 and 500,000.
require 'time'
t = Time.now
arr = Array.new(200_000) { rand(1_000_001) - 500_000 }
arr.size #=> 200000
sum_to_zero(arr).size #=> 16439
Time.now - t
#=> 0.23 (seconds)
sum_to_zero(arr).first(6)
#=> [[-98747, 98747],
# [157848, -157848],
# [-459650, 459650],
# [176655, 176655, -176655],
# [282101, -282101],
# [100886, 100886, -100886]]
If you wish to group the non-negative and negative values that sum to zero:
sum_to_zero(arr).map { |a| a.partition { |e| e >= 0 } }.first(6)
#=> [[[98747], [-98747]],
# [[157848], [-157848]],
# [[459650], [-459650]],
# [[176655, 176655], [-176655]],
# [[282101], [-282101]],
# [[100886, 100886], [-100886]]]
If you only want a single value for each group (a non-negative value, say):
sum_to_zero(arr).map { |a| a.first.abs }.first(6)
#=> [98747, 157848, 459650, 176655, 282101, 100886]
I think the most Ruby way would be:
nums.combination(2).any? { |x,y| (x+y).zero? }
Here's a way that should work well for large arrays. The methods above which go through every possible combination of two numbers are perfectly fine for small cases but will be very slow and memory hungry for arrays with lots of elements.
def two_sums nums
h = Hash.new
nums.each do |n|
return true if h[-n]
h[n] = true
end
false
end
Well, given it's tagged as #ruby, here's the most "ruby way" I could think of tackling this problem:
def two_sums(arr)
numbers = arr.combination(2).select { |a| a.reduce(:+) == 0 }.flatten
if numbers.empty?
"Nothing adds to zero."
else
"There are 2 numbers that sum to zero & they are #{numbers.first} and #{numbers.last}."
end
end
array.combination(2).select{|x|x[0] + x[1] == 0}