populate array with random numbers - ruby

I have the following array:
nums = [10, 20, 30, 40, 50]
and another array that must have 3 items:
arr = [1]
How can I get one or two items from nums array (random) to populate arr that must have 3 elements?

Use Array.sample
arr.concat num.sample(2)

nums = [10, 20, 30, 40, 50]
arr = [1]
2.times { arr << nums.sample }
Instead of sample(2) it's better to use sample without argument I think
Because sample with argument returns array with not repeated elements (with not repeated indices to be more precise)
That is, it is more random :)
Read more

You can shuffle the array and take the first three elements to ensure you don't have repeats.
arr = nums.shuffle.take(3)
If, for instance, you were dealing with a situation like a card game where you need to draw from a shuffled deck more than once, you might want to store the shuffled array, and then pop from it.
shuffled = arr.shuffle
arr = shuffled.pop(3)
# shuffled now only has the remaining two elements

Here are two ways to obtain a statistically random sample (subject to the impossibility of computing a truly random number).
The first is to select three elements at random and accept it if it contains at most two distinct elements. If it contains three distinct elements reject it and try again.
def doit(arr)
loop do
a = Array.new(3) { arr.sample }
return a if a.uniq.size <= 2
end
end
nums = [10, 20, 30, 40, 50]
doit nums #=> [40, 40, 10]
doit nums #=> [20, 50, 20]
doit nums #=> [50, 50, 20]
The second way is to make a probability calculation. Firstly, I assume that nums contains unique elements. (That assumption is not a deal-breaker but it avoids the messiness of dealing with the more complex case.)
n = nums.size
= 5
First compute the probability of drawing one distinct element (e.g., [30, 30, 30]).
P[N1] = (1/n)(1/n)
= (1/5)(1/5)
= 1/25
= 0.04
Note the first element drawn can be anything, then the second and third draws must match the first draw, the probability of each being 1/5.
Next compute the probability of drawing three distinct elements:
P[N3] = ((n-1)/n)*((n-2)/n)
= (n-1)*(n-2)/n**2
= 4*3/5**2
= 12/25
= 0.48
Again, the first element can be anything. The probability of the second element drawn being different is (n-1)/n and the probability of the third element drawn being different than the first two
is (n-1)/n.
Lastly, if N2 is the event in which exactly two of the three elements drawn are unique we have
P[N1] + P[N2] + P[N3] = 1
so
P[N2] = 1 - P[N1] - P[N3]
= 1 - 0.04 - 0.48
= 0.48
We therefore may compute
P[N1|N1 or N2] = P[N1 and (N1 or N2)]/P[N1 or N2]
P[N1|N1 or N2] = P[N1]/P[N1 or N2]
= 0.04/(0.04 + 0.48)
= 0.04/0.52
= 0.0769
Therefore,
P[N2|N1 or N2] = 1 - P[N1|N1 or N2]
= 1 - 0.0769
= 0.9231
We therefore may write the following to obtain a statistically random sample.
def doit(arr)
if rand < 0.0769
n = arr.sample
[n, n, n]
else
n1 = arr.sample
n2 = (arr - [n1]).sample
[n1, n2, n2]
end
end
doit nums #=> [40, 40, 40]
doit nums #=> [10, 20, 20]
doit nums #=> [10, 20, 20]

Related

Check if numbers summed in array match input parameter

I'm following Ruby practice problem from a website and I'm completely stuck on figuring out a solution to this problem. Basically, given a function has_sum?(val, arr), return true if any combination of numbers in the array (second parameter) can be added together to equal the first parameter, otherwise return false. So:
has_sum?(5, [1, 2, 3, 4]) # true
has_sum?(5, [1, 2, 6]) # false
I'm completely stuck and not quite sure how to accomplish this... Here's what I have so far.
def has_sum?(val, arr)
arr.each_with_index do |idx, v|
# ??? no idea what to do here except add the current num to the next in the list
end
end
Any help would be greatly appreciated - thanks!
An array can produce a sum when there is a subset of any length that adds up to that sum:
def has_sum?(val, arr)
(arr.size + 1).times
.flat_map { |i| arr.combination(i).to_a }
.any? { |s| s.inject(:+) == val }
end
has_sum?(5, [5])
# => true
has_sum?(5, [1, 2, 3])
# => true
has_sum?(5, [1, 1, 1, 1, 1, 1])
# => true
has_sum?(5, [1, 2, 7])
# => false
This is not very efficient as it generates all the possibilities before testing. This should terminate sooner:
def has_sum?(val, arr)
(arr.size + 1).times.any? { |i|
arr.combination(i).any? { |s| s.inject(:+) == val }
}
end
Even more efficiently, a recursive implementation, with the idea that a sum of an empty array is zero (and has_sum(nonzero, []) should return false); for a larger array, we pop off its head, and see if the sum of the rest of the array is okay if we count, or don't count, the head element. Here, we don't do the useless summing of the whole array over and over again:
def has_sum?(val, arr)
if arr.empty?
val.zero?
else
first, *rest = arr
has_sum?(val, rest) || has_sum?(val - first, rest)
end
end
This solution employs dynamic programming. I assume that zeroes have been removed from the array. If all numbers in the array are positive, we can also remove elements that are larger than the target sum.
Code
def sum_to_target(arr, target)
h = arr.each_index.with_object({}) do |i,h|
v = arr[i]
h.keys.each do |n|
unless h.key?(n+v) # || (n+v > target)
h[n+v] = v
return reconstruct(h, target) if n+v == target
end
end
h[v] = v unless h.key?(v)
return reconstruct(h, target) if v == target
end
nil
end
def reconstruct(h, target)
a = []
loop do
i = h[target]
a.unshift i
target -= i
return a if target == 0
end
a
end
Additional efficiency improvements are possible if arr contains only postive values.1
Examples
#1
sum_to_target [2,4,7,2], 8
#=> [2, 4, 2]
#2
arr = [64, 18, 64, 6, 39, 51, 87, 62, 78, 62, 49, 86, 35, 57, 40, 15, 74, 10, 8, 7]
a = sum_to_target(arr, 461)
#=> [64, 18, 39, 51, 87, 62, 78, 62]
Let's check that.
a.reduce(:+)
#=> 461
#3
a = sum_to_target([-64, 18, 64, -6, 39, 51, -87, 62, -78, 62, 49, 86, 35, 57,
40, 15, -74, 10, -8, -7], 190)
#=> [18, 64, -6, 39, 51, -87, 62, 49]
a.reduce(:+)
#=> 190
#4
arr = 1_000.times.map { rand 1..5_000 }
#=> [3471, 1891, 4257, 2265, 832, 1060, 3961, 875, 614, 2308, 2240, 3286,
# ...
# 521, 1316, 1986, 4099, 1398, 3803, 4498, 4607, 2262, 3941, 4367]
arr is an array of 1,000 elements, each a random number between 1 and 5,000.
answer = arr.sample(500)
#=> [3469, 2957, 1542, 950, 4765, 3126, 3602, 755, 4132, 4281, 2374,
# ...
# 427, 4238, 4397, 2717, 912, 1690, 3626, 169, 3607, 4084, 3161]
answer is an array of 500 elements from arr, sampled without replacement.
target = answer.reduce(:+)
#=> 1_226_020
target is the sum of the elements of answer. We will now search arr for a collection of elements that sum to 1,226,020 (answer being one such collection).
require 'time'
t = Time.now
#=> 2016-12-12 23:00:51 -0800
a = sum_to_target(arr, target)
#=> [3471, 1891, 4257, 2265, 832, 1060, 3961, 875, 614, 2308, 2240, 3286,
# ...
# 3616, 4150, 3222, 3896, 631, 2806, 1932, 3244, 2430, 1443, 1452]
Notice that a != answer (which is not surprising).
a.reduce(:+)
#=> 1226020
(Time.now-t).to_i
#=> 60 seconds
For this last example, methods that use Array#combination would have to wade though as many as
(1..arr.size).reduce(0) { |t,i| t + arr.combination(i).size }.to_f
#~> 1.07+301
combinations.
Explanation
Let
arr = [2,4,7,2]
target = 8
Suppose we temporarily redefine reconstruct to return the hash passed to it.
def reconstruct(h, target)
h
end
We then obtain the following:
h = sum_to_target(arr, target)
#=> {2=>2, 6=>4, 4=>4, 9=>7, 13=>7, 11=>7, 7=>7, 8=>2}
h is defined as follows.
Given an array of non-zero integers arr and a number n, if n is a key of h there exists an array a containing elements from arr, in the same order, such that the elements of a sum to n and the last element of a equals h[n].
which, admittedly, is a mouthful.
We now use the reconstruct (as defined in the "Code" section) to construct an array answer that will contain elements from arr (without repeating elements) that sum to target.
reconstruct(h, target) #=> [2, 4, 2]
Initially, reconstruct initializes the array answer, which it will build and return:
answer = []
h will always contain a key equal to target (8). As h[8] #=> 2 we conclude that the last element of answer equals 2, so we execute
answer.unshift(2) #=> [2]
The problem is now to find an array of elements from arr that sum to 8 - 2 #=> 6. As h[6] #=> 4, we conclude that the element in answer that precedes the 2 we just added is 4:
answer.unshift(4) #=> [4, 2]
We now need 8-2-4 #=> 2 more to total target. As h[2] #=> 2 we execute
answer.unshift(2) #=> [2, 4, 2]
Since 8-2-4-2 #=> 0 we are finished and therefore return answer.
Notice that 4 precedes the last 2 in arr and the first 2 precedes the 4 in arr. The way h is constructed ensures the elements of answer will always be ordered in this way.
Now consider how h is constructed. First,
h = {}
As arr[0] #=> 2, we conclude that, using only the first element of arr, all we can conclude is:
h[2] = 2
h #=> {2=>2}
h has no key equal to target (8), so we continue. Now consider arr[1] #=> 4. Using only the first two elements of arr we can conclude the following:
h[2+4] = 4
h #=> {2=>2, 6=>4}
and since h has no key 4,
h[4] = 4
h #=> {2=>2, 6=>4, 4=>4}
h still has no key equal to target (8), so we press on and examine arr[2] #=> 7. Using only the first three elements of arr we conclude the following:
h[2+7] = 7
h[6+7] = 7
h[4+7] = 7
h #=> {2=>2, 6=>4, 4=>4, 9=>7, 13=>7, 11=>7}
and since h has no key 7:
h[7] = 7
h #=> {2=>2, 6=>4, 4=>4, 9=>7, 13=>7, 11=>7, 7=>7}
We added four elements to h, but since arr contains only positive numbers, those with keys 9, 13 and 11 are of no interest.
Since h still does not have a key equal to target (8), we examine the next element in arr: arr[3] #=> 2. Using only the first four elements of arr we conclude the following:
h[4+2] = 2
h[6+2] = 2
Here we stop, since 6+2 == target #=> true.
h #=> {2=>2, 6=>2, 4=>4, 9=>7, 13=>7, 11=>7, 7=>7, 8=>2}
Notice that we did not compute h[2+2] = 2 since h already has a key 4. Further, had arr contained additional elements we still would have terminated the construction of the hash at this point.
Had we modified the code to take advantage of the fact that arr contains only positive values, the final hash would have been:
h #=> {2=>2, 6=>2, 4=>4, 7=>7, 8=>2}
If this is still not clear, it might be helpful to run the code for this example with included puts statements (e.g., puts "i=#{i}, h=#{h}, v=#{v}" after the line v = arr[i] in sum_to_target, and so on).
1 The line unless h.key?(n+v) can be changed to unless h.key?(n+v) || (n+v > target) if it is known that the array contains no negative elements. (Doing so reduced the solution time for example #4 by 4 seconds.) One could also compute #all_positive = arr.all?(&:positive?) and then make that line conditional on #all_positive.
I would do nested loops.
for x = 0 to length of array
for y = x + 1 to length of array
if number at x + number at y = sum return true
return false
Basically it will check the sum of each number with each of the numbers after it.
EDIT: this will only sum 2 numbers at a time. If you want to be able to sum any amount of numbers this wont work.

How to 'reverse sum' in Ruby?

I have no clue how to call this in correct math-terms. Consider a method which takes two digits:
def num_of_sum(total, group_count)
end
where total is an integer and group_count is an integer.
How would I get a 'nicely' grouped Array of integers of group_count-length which sum up till total.
My spec would look like:
describe "number to sum of" do
it "grabs all numbers" do
expect(num_of_sum(10, 2)).to eq([5,5])
expect(num_of_sum(10, 3)).to eq([3,3,4])
expect(num_of_sum(20, 3)).to eq([6,7,7])
expect(num_of_sum(100, 3)).to eq([33,33,34])
expect(num_of_sum(100, 2)).to eq([50,50])
end
end
I tried this, which works:
def num_of_sum(total, in_groups_of)
result = []
section_count ||= (total.to_f / in_groups_of.to_f).round
while(total > 0)
total -= section_count
if (total - section_count) < 0 && (total + section_count).even?
section_count += total
total -= total
end
result << section_count
end
result
end
But, for instance, this spec doesn't work:
expect(num_of_sum(67,5)).to eq([13,13,13,14,14])
I need the array to contain numbers that are as close to each other as possible. But the array is limited to the length of the group_count.
Does someone know what the mathemetical name for this is, so I can search a bit more accurately?
The mathematical term for this is an integer partition
A more direct approach to this is to observe that if you do integer division (round down) of the total by the number of groups, then your sum would be short by total mod number_of_groups, so you just need to distribute that amount across the array:
def even_partition(total, number_of_groups)
quotient, remainder = total.divmod(number_of_groups)
(number_of_groups-remainder).times.collect {quotient} +
remainder.times.collect { quotient + 1}
end
def n_parts(num, groupcount)
div, mod = num.divmod(groupcount)
Array.new(groupcount-mod, div) + Array.new(mod, div+1)
end
n_parts(100,3) => [33, 33, 34]
Docs to Array.new and Fixnum.divmod
A naive implementation is like this:
Let's take example of (20, 3). You want three numbers as a result.
20 / 3 # => 6
This is your "base" value. Create an array of three sixes, [6, 6, 6]. That'll get you 18. Now you have to distribute remaining 2 as equally as possible. For example, enumerate array elements and increment each one by 1, until you have no value to distribute. Result is [7, 7, 6]. Good enough, I think.
Possible (working) implementation:
def breakdown(total, group_count)
avg_value, extra = total.divmod(group_count)
result = Array.new(group_count, avg_value)
extra.times do |i|
result[i] += 1
end
result
end
breakdown(10, 2) == [5, 5] # => true
breakdown(10, 3) == [4, 3, 3] # => true
breakdown(20, 3) # => [7, 7, 6]
I have no clue how it’s called, but here is a solution:
def num_of_sum sum, count
result = [i = sum / count] * count # prepare an array e.g. [3,3,3] for 10,3
result[sum - i * count..-1] + # these should be left intact
result[0...sum - i * count].map { |i| i + 1 } # these are ++’ed
end
Hope it helps.
Another way:
def floors_then_ceils(n, groups)
floor, ceils = n.divmod(groups)
groups.times.map { |i| (i < groups-ceils) ? floor : floor + 1 }
end
floors_then_ceils(10, 3)
#=> [3, 3, 4]
floors_then_ceils(9, 3)
#=> [3, 3, 3]
Alternatively, groups.times.map... could be replaced with:
Array.new(groups-ceils, floor).concat(Array.new(ceils, floor+1))

Array of integers into array of ranges

I'm trying to figure out how I can change array of integers into array of ranges.
For example I want to take this array:
ar = [0, 49, 14, 30, 40, 23, 59, 101]
into
ar = [0..49, 14..30, 40..23, 59..101]
Given array always will be even. I want to take each two values as borders of ranges.
I have tried to seperate it for two arrays. One with odd indexes second with even.
a = ar.select.with_index{|_,i| (i+1) % 2 == 1}
b = ar.select.with_index{|_,i| (i+1) % 2 == 0}
I don't have an idea how to use them to create ranges, also I would like to avoid creating redundant variables like a and b.
I don't want to sort any values. Range 40..23 is intentional.
ar.each_slice(2).map { | a, b | a..b }
I would do as #undur_gongor's suggests, but here's another way:
e = ar.to_enum
b = []
loop do
b << (e.next..e.next)
end
b
#=> [0..49, 14..30, 40..23, 59..101]
new_ar = []
ar.each_slice(2) do |r|
new_ar << Range.new(r[0], r[1])
end

Ruby count duplicates in diagonal rows of matrix

I'm implementing gomoku game in Ruby, this is a variation of tic-tac-toe played on 15x15 board, and the first player who places 5 O's or X's in horizontal, vertical or diagonal row wins.
First, I assigning Matrix to a variable and fill it with numbers from 0 to 224, so there are no repetitions and I could count them later
gomoku = Matrix.zero(15)
num = 0
15.times do |i|
15.times do |j|
gomoku[i, j] = num
num += 1
end
end
then players take turns, and after every turn I check a win with the method win?
def win? matrix
15.times do |i|
return true if matrix.row_vectors[i].chunk{|e| e}.map{|_, v| v.length}.max > 4 # thanks to sawa for this way of counting adjacent duplicates
return true if matrix.column_vectors[i].chunk{|e| e}.map{|_, v| v.length}.max > 4
end
return false
end
I know, that I'm probably doing it wrong, but my problem isn't that, though suggestions are welcome. The problem is with diagonal rows. I don't know how to count duplicates in diagonal rows
diagonal_vectors = (-10 .. 10).flat_map do |x|
i = x < 0 ? 0 : x
j = x < 0 ? -x : 0
d = 15 - x.abs
[
d.times.map { |k|
gomoku[i + k, j + k]
},
d.times.map { |k|
gomoku[i + k, 14 - j - k]
}
]
end
With this, you can apply the same test sawa gave you.
EDIT: What this does
When looking at diagonals, there's two kinds: going down-left, and going down-right. Let's focus on down-right ones for now. In a 15x15 matrix, there are 29 down-right diagonals: one starting at each element of the first row, one starting at each element of the first column, but taking care not to count the one starting at [0, 0] twice. But some diagonals are too short, so we want to only take those that start on the first eleven rows and columns (because others will be shorter than 5 elements). This is what the first three lines do: [i, j] will be [10, 0], [9, 0] ... [0, 0], [0, 1], ... [0, 10]. d is the length of a diagonal starting at that position. Then, d.times.map { |k| gomoku[i + k, j + k] } collects all the elements in that diagonal. Say we're working on [10, 0]: d is 5, so we have [10, 0], [11, 1], [12, 2], [13, 3], [14, 4]; and we collect values at those coordinates in a list. Simultaneously, we'll also work on a down-left diagonal; that's the other map's job, which flips one coordinate. Thus, the inner block will return a two-element array, which is two diagonals, one down-left, one down-right. flat_map will take care of iterating while squishing the two-element arrays so that we get one big array of diagonals, not array of two-element arrays of diagonals.

How do I populate an array with random numbers?

I am trying to populate an array of four elements with positive integers that are less than 9.
Here is my code:
generated_number=Array.new(4)#create empty array of size 4
generated_number.each do |random| #for each position in the array create a random number
random=rand(10)
end
puts generated_number
I don't understand what I'm missing.
You can pass a range to rand()
Array.new(4) { rand(1...9) }
I think you're over complicating things.
generated_numbers = 4.times.map{Random.rand(8) } #=> [4, 2, 6, 8]
edit: For giggles I put together this function:
def rand_array(x, max)
x.times.map{ Random.rand(max) }
end
puts rand_array(5, 20) #=> [4, 13, 9, 19, 13]
This is how I solved it for an array with 10 elements:
n=10
my_array = Array.new(n)
i = 0
loop do
random_number = rand(n+1)
my_array.push(random_number)
i += 1
break if i >= n
end
for number in my_array
puts number
This is something I did for a school final, numbers aren't exactly the same but you can change numbers and such:
numbers_array = []
10.times do
numbers_array.push(rand(1..100))
end
puts numbers_array

Resources