Ruby count adjacent duplicate elements in array - ruby

Given an array, say %w[ a b c a a b b c c c]. The method should return 3, as it is the maximum quantity of adjacent duplicates ( 3 'c' )
This is what I came up with so far:
def check_quantity_of_same_adjacent_elements(array)
max = 0
array.each_index do |i|
max += 1 if array[i] == array[i+1]
end
max
end
but it obviously don't work as it returns the number of all duplicates

%w[a b c a a b b c c c].chunk{|e| e}.map{|_, v| v.length}.max #=> 3

Related

Intersection of two dimensional array

Is there a simple way to find the intersection of a two dimensional array? For example:
arr1 = [1,2,3,4,5]
arr2 = [5,6,7,8]
arr3 = [5]
bigarr = [arr1,arr1,arr3]
I know that it's possible to do:
intersection = arr1 & arr2 & arr3 # => 5
intersection = big_arr[0] & big_arr[1] & big_arr[2] # => 5
but the number of elements in big_arr will vary. I was wondering if there was a simple way to intersect all the elements in big_arr regardless of the number of elements.
Use #reduce like
arr1 = [1,2,3,4,5]
arr2 = [5,6,7,8]
arr3 = [5]
bigarr = [arr1,arr2,arr3]
bigarr.reduce(:&) # => [5]
What do you want: a method with a pretty face or one that is first to finish line? My friend #Arup has supplied one; I'll offer the another.
Code
def heavy_lifter(a)
wee_one = a.min_by(&:size)
return [] if wee_one.empty?
wee_loc = a.index(wee_one)
counts = wee_one.each_with_object({}) { |e,h| h.update(e=>1) }
nbr_reqd = 1
a.each_with_index do |b,i|
next if i == wee_loc
b.each do |e|
cnt = counts[e]
case
when cnt.nil?
next
when cnt == nbr_reqd
counts[e] = cnt + 1
when cnt < nbr_reqd
counts.delete(e)
return [] if counts.empty?
end
end
nbr_reqd += 1
end
counts.keys.each { |k| counts.delete(k) if counts[k] < nbr_reqd }
counts.keys
end
Example
a1 = [1,2,3,4,5]
a2 = [5,6,7,8]
a3 = [5]
a = [a1,a2,a3]
heavy_lifter(a)
#=> [5]
Explanation
Here's how the method works:
select the smallest array (wee_one). To simplify the explanation, assume it is the first element of a.
convert wee_one to a counting hash, counts, where counts[e] = 1 for each element of wee_one.
iterate through the remaining arrays.
keys of counts will be removed as arrays are processed.
after all calculations are complete, counts.keys equals the intersection of all arrays.
after nbr_reqd arrays have been processed (including wee_one), counts[k] equals the number of those arrays that have been found to contain k. Obviously, if counts[k] < nbr_reqd, key k can be removed from counts (but we will not remove such keys until our attention is drawn to them, or at the end).
suppose we are now to process the array b at offset nbr_reqd, meaning nbr_reqd arrays have been processed (including wee_one at offset zero). For each element e of b, we obtain cnt = counts[e]. There are four possibilities:
cnt == nil, in which case there is nothing to be done;
cnt < nbr_reqd, in which case key e is removed from counts;
cnt == nbr_reqd, meaning e has been present in all previous arrays processed, in which case we execute counts[k] = cnt + 1; and
cnt == nbr_read+1, meaning e has been present in all previous arrays processed and is a duplicate of another e in b that has already been processed, in which case nothing is to be done.
nbr_reqd is incremented by one and the process is repeated for the next array.
after all arrays have been processed, all that remains is to remove each key k in counts for which counts[k] < nbr_reqd.
Cutie method
def cutie(a)
a.reduce(:&)
end
Test data
def test(mx, *sizes)
sizes.map { |sz| Array.new(sz) { rand(mx) } }
end
For example:
test(10,5,6,7)
#=> [[9, 1, 5, 1, 1], [0, 8, 7, 8, 5, 0], [5, 1, 7, 6, 7, 9, 5]]
Benchmark code
require 'benchmark'
def bench(tst)
Benchmark.bm(12) do |bm|
bm.report 'cutie' do
cutie(tst)
end
bm.report 'heavy_lifter' do
heavy_lifter(tst)
end
end
end
Benchmark results
tst = test(1_000_000, 400_000, 600_000, 800_000)
cutie(tst).size
#=> 81929
cutie(tst).sort == heavy_lifter(tst).size
#=> true
bench(tst)
user system total real
cutie 1.610000 0.030000 1.640000 ( 1.639736)
heavy_lifter 1.800000 0.020000 1.820000 ( 1.824281)
sizes = (700_000..890_000).step(10_000).to_a
#=> [700000, 710000, 720000, 730000, 740000,
# 750000, 760000, 770000, 780000, 790000,
# 800000, 810000, 820000, 830000, 840000,
# 850000, 860000, 870000, 880000, 890000]
tst = test(1_000_000, *sizes)
bench(tst)
user system total real
cutie 14.090000 0.440000 14.530000 ( 14.679101)
heavy_lifter 5.830000 0.030000 5.860000 ( 5.935438)

Is this shuffle algorithm right?

The following is the shuffle algorithm I implemented in ruby:
def shuffle03!(arr)
len = arr.length
for i in 0..len-1
index1 = Random.rand(0..len-1)
index2 = Random.rand(0..len-1)
arr[index1], arr[index2] = arr[index2], arr[index1]
end
end
I tested this algorithm by reckoning:
class ShuffleTest
def initialize(seed)
len = seed.length
#count = {}
for i in 0..len-1
#count[seed[i]] = Array.new(len, 0)
end
end
def test(arr)
for i in 0...arr.length
#count[arr[i]][i] += 1
end
end
def show_count
return #count
end
end
def shuffle03!(arr)
len = arr.length
for i in 0..len-1
index1 = Random.rand(0..len-1)
index2 = Random.rand(0..len-1)
arr[index1], arr[index2] = arr[index2], arr[index1]
end
end
arr = ['a', 'b', 'c', 'd']
st = ShuffleTest.new(arr)
for x in 0..100_0000
shuffle03!(arr)
st.test(arr)
end
st.show_count.each do |k, v|
puts k
p v
end
the result was :
a
[250418, 249105, 249553, 250925]
b
[249372, 250373, 250785, 249471]
c
[250519, 250097, 249369, 250016]
d
[249692, 250426, 250294, 249589]
It semms to be correct. However, I donnot know how to prove it with mathematical statistics. So I'm not sure it is correct.
No, it is not right.
Imagine that you have a four-element list, [A,B,C,D]. Observe that:
There are 4! = 24 possible permutations. For this to be a correct shuffling algorithm, each of these permutations needs to be equally likely.
You are generating 4×2 = 8 random integers, each in the range 0–3, for a total of 48 = 65,536 possible sequences of random numbers. Each of these sequences is equally likely.
65,536 is not divisible by 24, so there is no way that your algorithm can map the 65,536 possible random-number sequences to permutations in a way that assigns an equal number of random-number sequences (and hence an equal probability) to each permutation.
To see this in a test, you can create a variant of your shuffle03! that, instead of using a random-generator, takes a list of eight indices, and uses those. (shuffle03! could then be implemented by generating eight random indices and then calling this variant as a helper-function.) Your test would then iterate over all 4096 possible sequences, and for each one, create a four-element list [A,B,C,D] and then call the variant method to see the resulting permutation. The test can keep count of how often each permutation appears, and use this to find which permutations appear more times than others. What you'll find is:
Permutation # of Occurrences
------------- ------------------
A B C D 4480
A B D C 3072
A C B D 3072
A C D B 2880
A D B C 2880
A D C B 3072
B A C D 3072
B A D C 2432
B C A D 2880
B C D A 2048
B D A C 2048
B D C A 2880
C A B D 2880
C A D B 2048
C B A D 3072
C B D A 2880
C D A B 2432
C D B A 2048
D A B C 2048
D A C B 2880
D B A C 2880
D B C A 3072
D C A B 2048
D C B A 2432
As you can see, elements tend to end up in the same order they started; for example, A B C D is the most common permutation. We can pull out one aspect of this by seeing, for each pair of elements, how often they end up in the same order vs. the opposite order. We find:
Elements Same Order Opposite Order
---------- ------------ ----------------
A and B 33792 31744
A and C 34816 30720
A and D 35840 29696
B and C 33792 31744
B and D 34816 30720
C and D 33792 31744
So some pairs are more likely than others to end up in the opposite order, but each pair is more likely to end up in the same order than to end up in the opposite order.
You can reduce the imbalance by performing more passes, but since no power of 8 is divisible by 24, it will never be possible to make all permutations equally likely.
By the way, if your actual goal here is a good shuffle algorithm (rather than just the learning experience of figuring one out for yourself), then you should use a Fisher–Yates shuffle.
Of course, since you're using Ruby, you can bypass the whole issue by just using Array.shuffle!, which performs the Fisher–Yates shuffle for you.
I would like to suggest a Ruby-way of achieving your objective.
Evidently, you cannot use Array#shuffle but (thankfully!) can use Kernel#rand. (I assume you can't use Array#sample either, as: arr.sample(arr.size) has the same effect as arr.shuffle.)
There are many ways to implement shuffle that are statistically valid (on the assumption that rand(n) produces truly random numbers between 0 and n-1, which of course is not possible, but that's a reasonable assumption). Here's one way:
class Array
def shuffle
arr = self.dup
map { arr.delete_at(rand(arr.size)) }
end
end
Let's try it with:
arr = [4,:a,5,6,'b',7,8]
arr.shuffle #=> [6, 8, "b", 5, 4, :a, 7]
arr.shuffle #=> [5, :a, 8, 4, "b", 7, 6]
arr.shuffle #=> [6, 8, 5, 7, "b", :a, 4]
arr.shuffle #=> [6, 4, 7, 8, 5, :a, "b"]
arr.shuffle #=> [:a, 4, "b", 5, 7, 8, 6]
arr.shuffle #=> ["b", 4, 7, 8, :a, 6, 5]

Finding the index of second smallest number in the list?

How can I find the second smallest number and return its index?
Another approach :
>> a = [1,3,5,6,2,4]
=> [1, 3, 5, 6, 2, 4]
>> a.index(a.sort[1])
=> 4
>>
I can see two options from the top of my head:
Delete the current min, so the new min will be the previous second min
arr = num.delete(num.min)
min_bis = arr.min
Loop through the array, using 2 variables to store the 2 lowest values.
This might be a little trickier but the complexity would only be O(n).
I don't know why you don't want to sort the array, but if it's a performance issue, it's probably one of the best options (to sort it) especially if the array is small.
(Below, Enumerable is a superset of Array, Hash and Range etc.)
Enumerable#sort returns a fresh array containing all the elements of the original object in a sorted order, so you can write a = num.sort[1] (provided that l > 1) to find the second smallest number, without modfying the original input nums.
Then you can feed it to Enumerable#find_index.
http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-sort
http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-find_index
By the way
while (index <l)
nums - gets.to_i
num[index] = nums
index +=1
end
can be written as
nums = (0...l).map{ gets.to_i }
I understand you don't want to sort the array before finding the second-lowest number. But are you willing to use a sorted clone/copy of that array?
If
nums = [1, 5, 1, 9, 3, 8]
Then:
# grab a sorted copy of nums
b = nums.sort
# b = [1, 1, 3, 5, 8, 9]
# delete the lowest number
b.delete(b.min)
# now b = [3, 5, 8, 9]
# so get the index from the original array
nums.index(b.first)
which should return 4 because nums[4] = 3. (You could also use nums.index(b[0]) since b is already sorted.)
If you don't mind being destructive to the original array:
a.delete(a.min)
a.index(a.min)
Here's an approach that does not use sort:
arr = [3,1,2,5,1]
If second_smallest(arr) => 2 is desired:
def second_smallest(arr)
return nil if arr.uniq.size < 2
mn, mx = arr.min, arr.max
arr.map! { |e| e == mn ? mx : e }
arr.index(arr.min)
end
If second_smallest(arr) => 4 is desired:
def second_smallest(arr)
return nil if arr.uniq.size < 2
i1 = arr.index(arr.min)
arr.delete_at(i1)
i2 = arr.index(arr.min)
i2 >= i1 ? i2 + 1 : i2
end
You don't want to use sort as it's O(nlogn). You want to iterate through the array only once (after getting the max):
arr = [123,35,12,34,5,32]
This is a straight forward way of solving the problem:
def second_min_index(arr)
max = arr.max
min, min_index, second_min, second_min_index = max, 0, max, 0
arr.each_with_index do |e, i|
# if something is less than min, second min should become what used to be min
if (e <= min)
second_min, second_min_index, min, min_index = min, min_index, e, i
# if something is less than second min (but not less than min)
# it becomes the second min
elsif (e < second_min)
second_min, second_min_index = e, i
end
end
second_min_index
end
second_min_index(arr)
=> 2
A better and more reusable way of doing this would be via a transform and conquer solution (just in case you get asked for the 3rd smallest number):
def min_index(arr)
min, min_index = arr[0], 0
arr.each_with_index { |e,i| min, min_index = e,i if e < min }
min_index
end
def min_index_excluding(arr, exclude_indexes)
min, min_index = arr[0], 0
arr.each_with_index { |e,i| min, min_index = e,i if (e < min && !exclude_indexes.include?(i)) }
min_index
end
def second_min_index(arr)
min_index_excluding(arr, [min_index(arr)])
end
second_min_index(arr)
=> 2
a_sorted = a.sort
second_min = a_sorted[1]
a.index(second_min)

Ruby, 2 dimensional arrays, make 2d array with size MxN plus change created lines

I'm trying to learn ruby with solving some problems. Here i take a problem from Pascal.
Here is the exact text:
Ask the user to input two positive integers M and N. Make the 2 dimensional array of integers with size MxN, fill the array with 0. Change all odd lines values to sequence 1,2,3,…,M and even lines values to the M,M-1,M-2,…,1.

I read Ruby literature, but have no idea how to start and make 2D array which depends of user input.
So, here is my final code:
require 'matrix'
print "Define M: "
m = gets.to_i
print "Define N: "
n = gets.to_i
matrix = Matrix.build(m, n) do |r, c|
if r.even? and m > c
c+1
elsif r.even?
0
else
m-c
end
end
print matrix.each_slice(matrix.column_size) {|r| p r }
Accept simplicity.
Use matrix class in Ruby.
require 'matrix'
m = 3
n = 4
#considering 1-indexed array/matrices and reversing even/odd conditions
Matrix.build(m, n) do |r, c|
r.even? ? (m > c ? c+1 : 0) : m - c
# if r.even? and m > c
# c+1
# elsif r.even?
# 0
# else
# m-c
# end
end
#=> Matrix[[1, 2, 3, 0], [3, 2, 1, 0], [1, 2, 3, 0]]
Here, whenever n > m, those cells will retain the default value, which is 0 in this case.
Commented the alternative implementation of ternary operation with if/else.

Creating an arbitrary hierarchy of characters

Let's say I have 4 characters, A, P, B, N. I want to be able to compare them such that:
A > P > B > N > A
How would this be accomplished in Ruby?
From your comments, it seems that you are not trying to put these elements in order, but rather define some binary relation between some of them. It's possible to do that in Ruby in many ways, depending on how you intend to use that relation later.
The simplest one is just to define ordered pairs of related elements:
MAP = [
['A', 'P'],
['P', 'B'],
['B', 'N'],
['N', 'A']
]
And then use it whenever you need to "compare" two elements.
def beats? one, other
MAP.member?([one, other])
end
beats? 'A', 'B'
# => false
beats? 'A', 'P'
# => true
beats? 'N', 'A'
# => true
PS. You can generate the map from a string using something like
MAP = 'APBNA'.chars.each_cons(2).to_a
One of the possible solutions is to create a class with, for example, character and weight or something. And implement <=> operator (method) in it.
Don't forget to include Comparable mixin into this class.
class ComparableCharacter
include Comparable
attr_accessor :character, :weight
def <=>(another)
weight <=> another.weight
end
end
a = "APBN"
h = {};(0...a.size).each{|i| h[a[i].chr] = i}
b = ['A','P','A','N', 'B','P']
b.sort_by{|t| h[t] }
Of course this can not work with your example as you have a bad ordering - you can never have A > P > A, but at least it shows you how to sort according to an order you want.
If someone can be interested, this is my proposal (ternary comparison - because comparison is not a binary operation!!!):
class RockPaperScissors
ITEMS = %W(A P B N)
def self.compare(item, other_item)
new(item).compare other_item
end
def initialize(item)
# input validations?
#item = item
end
def compare(other_item)
# input validations?
indexes_subtraction = ITEMS.index(#item) - ITEMS.index(other_item)
case indexes_subtraction
when 1, -1
- indexes_subtraction
else
indexes_subtraction <=> 0
end
end
end
require 'test/unit'
include MiniTest::Assertions
assert_equal RockPaperScissors.compare('A', 'A'), 0
assert_equal RockPaperScissors.compare('P', 'P'), 0
assert_equal RockPaperScissors.compare('B', 'B'), 0
assert_equal RockPaperScissors.compare('N', 'N'), 0
assert_equal RockPaperScissors.compare('A', 'P'), 1
assert_equal RockPaperScissors.compare('P', 'A'), -1
assert_equal RockPaperScissors.compare('P', 'B'), 1
assert_equal RockPaperScissors.compare('B', 'P'), -1
assert_equal RockPaperScissors.compare('B', 'N'), 1
assert_equal RockPaperScissors.compare('N', 'B'), -1
assert_equal RockPaperScissors.compare('N', 'A'), 1
assert_equal RockPaperScissors.compare('A', 'N'), -1
EXPLANATION
Equality: (A, A) comparison
Indexes: iA: 0; iA: 0
iA - iA = 0
A is equal to A, so we could return 0
Majority: (A, P)
Indexes: iA: 0; iP: 1
iA - iP = -1
A > P, so we must obtain 1; we can use the - function: - (-1) -> 1
Minority: (P, A)
Indexes: iP: 1; iA: 0
iP - iA = 1
P < A, so we must obtain -1; we can use the - function: - (1) -> -1
Edge case 1: (N, A)
Indexes: iN: 3, iA: 0
iN - iA = 3
N > A, so we must obtain 1; we can use the <=> function: (3 <=> 0) -> 1
Edge case 2: (A, N)
Indexes: iA: 0, iN: 3
iA - iN = -3
A < N, so we must obtain -1; we can use the <=> function: (3 <=> 0) -> 1
The rest is refactoring: 0 can be converted to 0 with the <=> function.

Resources