How do I pick a fractional sample from an array? - ruby

I know that ruby has myarray.sample(i) to sample i elements from an array. My problem is that the number of elements are not integers.
i.e I would like a method mysample such that if I call myarray.mysample(1.5) 10 times, the number of elements I get should be close to 15.
With sample, I will get either 10 or 20 depending on the int conversion. Similarly, if I call myarray.mysample(.25) I want it to return an element with 0.25 probability (that is, it should return one element one times out of four, and three times out of four, it should return an empty array/nil).
How do I do this?
My attempt so far:
def mysample(array,s)
ints = array.sample(s.floor)
if (Random.rand > s - s.floor)
ints << array.sample
end
return ints
end
Is there a better way?

Basing my answer off of this:
if I call myarray.mysample(1.5) 10 times, the number of elements I get should be close to 15.
Extending Array yields the following:
class Array
def mysample(num)
self.sample( ( num + rand() ).floor )
end
end
> [1, 2, 3, 4, 5].mysample(2.5)
=> [1, 3]
> [1, 2, 3, 4, 5].mysample(2.5)
=> [4, 2, 5]
> [1, 2, 3, 4, 5].mysample(0.5)
=> []
> [1, 2, 3, 4, 5].mysample(0.5)
=> [3]
etc.

To optimal argument is there to decide the spread of randomness for numbers above 1.
class Array
def my_sample(number, deviation=0.3)
if number < 1
return sample rand(100) < number * 100 ? 1 : 0
end
speard = (number*deviation).to_i
randomness = rand(-speard..speard)
sample(number+randomness)
end
end
p [1,2,3,4,5,6,7,8,9,10].my_sample(0.5) #=> []
p [1,2,3,4,5,6,7,8,9,10].my_sample(0.5) #=> [3]
p [1,2,3,4,5,6,7,8,9,10].my_sample(5) #=> [9, 2, 1, 4, 10, 7, 3]
p [1,2,3,4,5,6,7,8,9,10].my_sample(5) #=> [7, 2, 3, 8]

Related

Why does my algorithm solution fail validation?

Here is a task i got:
Cards are laid out on the table in a row, each card has a natural number written on it. In one move, it is allowed to take a card either from the left or from the right end of the row. In total, you can make k moves. The final score is equal to the sum of the numbers on the selected cards. Determine what is the maximum score you can get at the end of the game.
Here`s my code:
def card_counter(arr, k):
if len(arr) == k:
return sum(arr)
rang = len(arr) // 2
left = arr[:rang]
right = list(reversed(arr[rang:]))
c = 0
for _ in range(k):
min_arr = left if sum(left) >= sum(
right) and len(left) > 0 else right
c += min_arr.pop(0)
return c
if __name__ == '__main__':
assert card_counter([1, 2, 3, 4, 5], 5) == 15
assert card_counter([0, 0, 0], 1) == 0
assert card_counter([150], 1) == 150
This code works on all variants that I have come up with, including extreme cases. But the system does not accept this option, automatic tests do not pass. Where can there be a mistake?
I cannot comment on the algorithm you implemented as you did not state it in non-algorithm terms and I am not familiar with the language you are using (which I am guessing is Python). I will present a simple solution written in Ruby, hoping that the description I give will make it understood by readers who do not know Ruby.
Suppose
deck = [1, 2, 5, 7, 1, 4, 6, 3]
nbr_moves = 4
ds = deck.size
#=> 8
After removing nbr_moves from the ends,
m = ds - nbr_moves
#=> 4
consecutive cards will remain. In Ruby we could write
best = (0..ds-1).each_cons(m).max_by { |arr| deck.values_at(*arr).sum }
#=> [3, 4, 5, 6]
to obtain the indices of deck, for which the sum of the associated values is maximum:
deck.values_at(*best).sum
#=> 18
where
deck.values_at(*best)
#=> [7, 1, 4, 6]
In view of the value of best ([3, 4, 5, 6]), we need to remove left = best.first #=> 3 elements from the left and nbr_moves - left #=> 1 element from the right. The order of the removals is not relevant.
Note that
enum = (0..ds-1).each_cons(m)
#=> #<Enumerator: 0..7:each_cons(4)>
returns an enumerator. We can convert this enumerator to an array to see the values it will generate.
enum.to_a
#=> [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]
When, for example,
arr = [2, 3, 4, 5]
then
a = deck.values_at(*arr)
#=> [5, 7, 1, 4]
a.sum
#=> 17
Note that, having computed, for example,
t = [deck[i], deck[i+1],..., deck[j]].sum
the sum of
[deck[i+1], deck[i+2],..., deck[j+1]]
is seen to equal
t - deck[i] + deck[j+1]
This suggests a more efficient way to perform the calculations when m is large.

How to handle negative iterator passed in function?

I am working on a manual rotate function in Ruby. But I ran into issue there are negative offsets passed in some examples. Is it possible to iterate from a negative number up to a specified index(not sure what that index would be)?
def my_rotate(arr, offset=1)
if offset < 1
for i in offset
arr.push(arr.shift)
end
else
for i in 1..offset
arr.push(arr.shift)
end
end
arr
end
Following with your code, you can use Array#pop and Array#unshift (which are the opposites of Array#push and Array#shift):
def my_rotate(array, offset=1)
arr = array.dup
if offset < 1
for i in 1..offset.abs
arr.unshift(arr.pop)
end
else
for i in 1..offset
arr.push(arr.shift)
end
end
arr
end
Notice the change in line 5 for i in 1..offset.abs to be able to loop the array, and the addition of line 2 arr = array.dup to prevent the original array from being mutated.
This is pretty much how Array#rotate does it (in C).
Code
class Array
def my_rotate(n=1)
n %= self.size
self[n..-1].concat(self[0,n])
end
end
Examples
arr = [1,2,3,4]
arr.my_rotate 0 #=> [1,2,3,4]
arr.my_rotate #=> [2, 3, 4, 1]
arr.my_rotate 1 #=> [2, 3, 4, 1]
arr.my_rotate 4 #=> [1, 2, 3, 4]
arr.my_rotate 5 #=> [2, 3, 4, 1]
arr.my_rotate 9 #=> [2, 3, 4, 1]
arr.my_rotate -1 #=> [4, 1, 2, 3]
arr.my_rotate -4 #=> [1, 2, 3, 4]
arr.my_rotate -5 #=> [4, 1, 2, 3]
arr.my_rotate -9 #=> [4, 1, 2, 3]
Explanation
The line
n %= self.size
which Ruby's parser expands to
n = n % self.size
converts n to an integer between 0 and self.size - 1. Moreover, it does so for both positive and negative values of n.
The line
self[n..-1].concat(self[0,n])
appends the first n elements of arr to an array comprised of the last arr.size - n elements of arr. The resulting array is then returned by the method.
If you do not wish to add this method to the class Array you could of course define it def my_rotate(arr, n)....

How to take one element out of an array and put in front?

a = [1,2,3]
b = [2,1,3]
What is the best way to get b from a?
My inelegant solution:
x = 2
y = a - [x]
b = y.unshift(x)
a.unshift a.delete(2)
This appends the recently deleted object (here 2).
Beware that, if the object in question appears more than once in the array, all occurences will be deleted.
In case you want only the first occurrence of an object to be moved, try this:
a = [1,2,3,2]
a.unshift a.delete_at(a.index(2))
# => [2, 1, 3, 2]
a.unshift a.slice!(a.index(2)||0)
# => [2, 1, 3]
If there are multiple instances, only the first instance is moved to the front.
If the element doesn't exist, then a is unchanged.
If you wanted to move elements of an array arbitrarily, you could do something like this:
Code
# Return a copy of the receiver array, with the receiver's element at
# offset i moved before the element at offset j, unless j == self.size,
# in which case the element at offset i is moved to the end of the array.
class Array
def move(i,j)
a = dup
case
when i < 0 || i >= size
raise ArgumentError, "From index is out-of-range"
when j < 0 || j > size
raise ArgumentError, "To index is out-of-range"
when j < i
a.insert(j, a.delete_at(i))
when j == size
a << a.delete_at(i)
when j > i+1
a.insert(j-1, a.delete_at(i))
else
a
end
end
end
With Ruby v2.1, you could optionally replace class Array with refine Array. (Module#refine was introduced experimentally in v2.0, but was changed substantially in v2.1.)
Demo
arr = [1,2,3,4,5] #=> [1, 2, 3, 4, 5]
arr.move(2,1) #=> [1, 3, 2, 4, 5]
arr.move(2,2) #=> [1, 2, 3, 4, 5]
arr.move(2,3) #=> [1, 2, 3, 4, 5]
arr.move(2,4) #=> [1, 2, 4, 3, 5]
arr.move(2,5) #=> [1, 2, 4, 5, 3]
arr.move(2,6) #=> ArgumentError: To index is out-of-range

Whats the best way to split an array in ruby into multiple smaller arrays of random size

I have multiple arrays in ruby of variable length from 1 to 40 :
#items is a typical array which could be anywhere from 1 to 40 in length. eg
#items = [1, 2, 3, 4, 5, 6]
I want to randomly split the array into smaller arrays of lengths either 1, 2 or 3 to give a result of (for example)
#items = [[1, 2],[3],[4,5,6]]
or
#items = [[1],[2, 3],[4],[5,6]]
etc
I know you can split the array using #items.each_slice(3)... where 3 is a fixed length. But i want to randomly split large arrays of variable length into array sizes of 1,2 or 3 randomly... Whats the best way to achieve this?
items, #items = #items.dup, []
#items.push(items.shift(rand(1..3))) until items.empty?
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
b = []
until a.empty?
b << a.shift((1..a.size).to_a.sample)
end
# b => [[1, 2], [3, 4, 5, 6, 7], [8, 9], [10]]
# change everytime though
You can limit the sub arrays size by replacing the a.size with 3 or anything you want.
This solution maybe uses too many local variables, but it is non-destructive to the input array and flexible on array window maximum.
def rotateAndTake inputArray, windowSize
rotator, returnArray, breaker = 0, [], true
while breaker do
window = rand(windowSize)+1
if(rotator + window > inputArray.length) then
window = inputArray.length - rotator
breaker = false
end
returnArray << inputArray.rotate(rotator).take(window) if window > 0
rotator += window
end
returnArray
end
Also, I just wanted to write a solution that used the "rotate" method.
Just for yucks, I thought I'd try a pure functional form with no mutating methods for this problem:
( (0..#items.size)
.inject([0]) { |m,_| m + [m.last + 1 + rand(3)] }
.take_while { |i| i < #items.size } + [#items.size] ).
each_cons(2).
map { |s,e| #items[s...e] }
Here's another functional solution:
( [0]+
(1..a.length-1)
.to_a
.sample(rand(a.length))
.sort+
[a.length]
).each_cons(2).map{|i,j| a[i..j-1]}

How to drop the end of an array in Ruby

Array#drop removes the first n elements of an array. What is a good way to remove the last m elements of an array? Alternately, what is a good way to keep the middle elements of an array (greater than n, less than m)?
This is exactly what Array#pop is for:
x = [1,2,3]
x.pop(2) # => [2,3]
x # => [1]
You can also use Array#slice method, e.g.:
[1,2,3,4,5,6].slice(1..4) # => [2, 3, 4, 5]
or
a = [1,2,3,4,5,6]
a.take 3 # => [1, 2, 3]
a.first 3 # => [1, 2, 3]
a.first a.size - 1 # to get rid of the last one
The most direct opposite of drop (drop the first n elements) would be take, which keeps the first n elements (there's also take_while which is analogous to drop_while).
Slice allows you to return a subset of the array either by specifying a range or an offset and a length. Array#[] behaves the same when passed a range as an argument or when passed 2 numbers
this will get rid of last n elements:
a = [1,2,3,4,5,6]
n = 4
p a[0, (a.size-n)]
#=> [1, 2]
n = 2
p a[0, (a.size-n)]
#=> [1, 2, 3, 4]
regard "middle" elements:
min, max = 2, 5
p a.select {|v| (min..max).include? v }
#=> [2, 3, 4, 5]
I wanted the return value to be the array without the dropped elements. I found a couple solutions here to be okay:
count = 2
[1, 2, 3, 4, 5].slice 0..-(count + 1) # => [1, 2, 3]
[1, 2, 3, 4, 5].tap { |a| a.pop count } # => [1, 2, 3]
But I found another solution to be more readable if the order of the array isn't important (in my case I was deleting files):
count = 2
[1, 2, 3, 4, 5].reverse.drop count # => [3, 2, 1]
You could tack another .reverse on there if you need to preserve order but I think I prefer the tap solution at that point.
You can achieve the same as Array#pop in a non destructive way, and without needing to know the lenght of the array:
a = [1, 2, 3, 4, 5, 6]
b = a[0..-2]
# => [1, 2, 3, 4, 5]
n = 3 # if we want drop the last n elements
c = a[0..-(n+1)]
# => [1, 2, 3]
Array#delete_at() is the simplest way to delete the last element of an array, as so
arr = [1,2,3,4,5,6]
arr.delete_at(-1)
p arr # => [1,2,3,4,5]
For deleting a segment, or segments, of an array use methods in the other answers.
You can also add some methods
class Array
# Using slice
def cut(n)
slice(0..-n-1)
end
# Using pop
def cut2(n)
dup.tap{|x| x.pop(n)}
end
# Using take
def cut3(n)
length - n >=0 ? take(length - n) : []
end
end
[1,2,3,4,5].cut(2)
=> [1, 2, 3]

Resources