Intersection of two dimensional array - ruby

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)

Related

Ruby array five_sort algorithm

I'm trying to solve a problem called five_sort that accepts an array of integers as the argument and places all the fives at the end of the array and leaves all of the other numbers unsorted. For example, [1,2,5,3,2,5,5,7] would be sorted as [1,2,3,2,7,5,5,5].The rules for the problem state that only a while loops can be used and no other methods can be called on the array except [] and []=. Here is my current code:
def five_sort(array)
sorted = false
while sorted == false
idx = 0
while idx < array.length
if array[idx] == 5
array[idx], array[idx + 1] = array[idx + 1], array[idx]
end
idx += 1
end
sorted = true
end
array
end
When running it, it is just in a continuous loop but I can't find out how to fix it. I know that if I just run the second while loop without the while sorted loop, the array would only run once and the fives would only switch places once and the loop would be over. But I don't know how to run the second while loop and stop it once all the fives are at the end.
Can anyone help me figure this one out?
Just a simple O(n) time and O(1) space solution, using a write-index and a read-index.
w = r = 0
while array[w]
r += 1 while array[r] == 5
array[w] = array[r] || 5
w += 1
r += 1
end
While a couple of people have posted alternative approaches, which are all good, I wanted to post something based on your own code to reassure you that you had got pretty close to a solution.
I've added comments to explain the changes I've made:
def five_sort(array)
sorted = false
while sorted == false
idx = 0
# use did_swap to keep track of if we've needed to swap any numbers
did_swap = false
# check if next element is nil as alternative to using Array#length
while array[idx + 1] != nil
# it's only really a swap if the other entry is not also a 5
if array[idx] == 5 and array[idx + 1] != 5
array[idx], array[idx + 1] = array[idx + 1], array[idx]
did_swap = true
end
idx += 1
end
# if we've been through the array without needing to make any swaps
# then the list is sorted
if !did_swap
sorted = true
end
end
array
end
Your array is becoming longer at each loop:
array = [1,2]
array[1], array[2] = array[2], array[1]
puts array.length
Outputs 3.
What you need is to not swap if idx = array.length - 1
if (array[idx] == 5)
array[idx], array[idx+1] = array[idx+1], array[idx] if idx != array.length - 1
end
def five_sort(arr)
i = 0
cnt = 0
while arr[i]
if arr[i] == 5 && arr[i+1]
arr[i..i] = []
cnt += 1
else
i += 1
end
end
cnt.times { arr[-1,2] = [arr[-1],5] }
arr
end
arr = [1,5,3,5,6]
five_sort arr
#=> [1, 3, 6, 5, 5]
arr
#=> [1, 3, 6, 5, 5] # confirms arr is mutated
five_sort [5,5,5,3,6]
#=> [3, 6, 5, 5, 5]
five_sort [5,5,5,5,5]
#=> [5, 5, 5, 5, 5]
five_sort [1,2,3,4,6]
#=> [1, 2, 3, 4, 6]
five_sort []
#=> []
Notes:
As required by the spec, the only methods invoked on arr are [] and []= and no other arrays are created.
if i indexes the last element of the array, arr[i+1] equals nil.
arr[i..i] = [] removes the elementarr[i] from arr.
arr[-1,2] = [arr[-1],5] appends a 5 to arr.

Ruby code looping infinitely

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.

How to iterate through array using nested while loops in Ruby?

In my code below it seems that the i variable in the 2nd while loop is not incrementing within the 2nd while loop, but it is incrementing in the first while loop. So i is always equal to zero in the 2nd while loop, however I need it to increment +1 each pass.
Here is my code:
# Code required to read in the values of k,n and candies.
n = gets.to_i
k = gets.to_i
candy = Array.new(n)
for i in 0..n-1
candy[i] = gets.to_i
end
#create loop to calculate max-min and compare to lowest max-min value calculated thus far.
arr = []
i = 0
candy = candy.sort
ans = nil
#iterate through candy array to create n minus k sub-arrays of size k
while i < n-k
m = 0
#create sub-array of size k
while m < k
arr << candy[i + m].to_i
m += 1
end
#find the difference between the max and the min value in the sub-array
arrcheck = (arr[k-1]) - (arr[0])
i += 1
#if ans is nil set the ans variable to arrcheck, else if arrcheck is less than the ans set ans to arrcheck
if ans == nil
ans = arrcheck
elsif arrcheck < ans
ans = arrcheck
end
end
### Compute answer from k, n, candies
puts ans
Since the i in the 2nd loop is not incrementing it is just checking the same sub-array n minus k times without advancing through the entire candy array.
I see two mistakes in your code:
while i < n - k should be while i <= n - k
arr = [] must be moved into the while loop
Fixed code:
while i <= n - k
m = 0
arr = []
# ...
Although your code works with the above fix, it's not very idiomatic. I'd write something like:
print 'number of candies: '
n = gets.to_i
print 'sub-array size: '
k = gets.to_i
candies = []
(1..n).each do |i|
print "candy #{i}: "
candies << gets.to_i
end
puts candies.sort.each_cons(k).map { |a| a.last - a.first }.min
Let's examine the last line:
candies = [1, 7, 10, 2]
k = 2
candies #=> [1, 7, 10, 2]
.sort #=> [1, 2, 7, 10]
.each_cons(k) #=> [[1, 2], [2, 7], [7, 10]]
.map { |a| a.last - a.first } #=> [1, 5, 3]
.min #=> 1

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)

How do I find .index of a multidimensional array

Tried web resources and didnt have any luck and my visual quick start guide.
If I have my 2d/multidimensional array:
array = [['x', 'x',' x','x'],
['x', 'S',' ','x'],
['x', 'x',' x','x']]
print array.index('S')
it returns nil
So then I go and type:
array = ['x', 'S',' ','x']
print array.index('S')
it returns the value I am looking for 1
My first guess something is being called wrong in the .index() and it needs two arguments one for both row and column? Anyways how do I make .index work for a multidimensional array? This is step one for solving my little maze problem
This will do it:
array = [['x', 'x',' x','x'],
['x', 'S',' ','x'],
['x', 'x',' x','x']]
p array.index(array.detect{|aa| aa.include?('S')}) # prints 1
If you also want 'S's index in the sub array you could:
row = array.detect{|aa| aa.include?('S')}
p [row.index('S'), array.index(row)] # prints [1,1]
You can use the method Matrix#index:
require 'matrix'
Matrix[*array].index("S")
#=> [1, 1]
a.each_index { |i| j = a[i].index 'S'; p [i, j] if j }
Update: OK, we can return multiple matches. It's probably best to utilize the core API as much as possible, rather than iterate one by one with interpreted Ruby code, so let's add some short-circuit exits and iterative evals to break the row into pieces. This time it's organized as an instance method on Array, and it returns an array of [row,col] subarrays.
a = [ %w{ a b c d },
%w{ S },
%w{ S S S x y z },
%w{ S S S S S S },
%w{ x y z S },
%w{ x y S a b },
%w{ x },
%w{ } ]
class Array
def locate2d test
r = []
each_index do |i|
row, j0 = self[i], 0
while row.include? test
if j = (row.index test)
r << [i, j0 + j]
j += 1
j0 += j
row = row.drop j
end
end
end
r
end
end
p a.locate2d 'S'
You could find first in which is the absolute position by flattening the array:
pos = array.flatten.index('S')
Then get the number of columns per row:
ncols = array.first.size
then
row = pos / ncols
col = pos % ncols
Non-Ruby specific answer: You're trying to print 'S' in both examples, but only the latter has 'S' in the array. The first has ['x', 'S', ' ', 'x']. What you will need to do (If Ruby doesn't do this for you) is look at each member in the array and search that member for 'S'. If 'S' is contained in that member then print it.
array = [['x', 'x',' x','x'],
['x', 'S',' ','x'],
['x', 'x',' x','x']]
class Array
def my_index item
self.each_with_index{|raw, i| return i if raw.include? item}
return
end
end
p array.my_index("S") #=>1
p array.my_index("Not Exist Item") #=> nil
Specifies both indexes of the first occurrence of element for one pass on subarrays
a = [[...],[...],[...],[...]]
element = 'S'
result_i = result_j = nil
a.each_with_index do|row, i|
if (j = row.index(element))
result_i, result_j = i, j
break
end
end

Resources