Algorithm: Find out which objects hold the subset of input array - algorithm

We have some objets (about 100,000 objects), each object has a property with some integers (range from 1 to 20,000, at most 20 elements, no duplicated elements.):
For example:
object_1: [1, 4]
object_2: [1, 3]
object_3: [100]
And the problem is, we input a array of integer (called A), find out which objects hold the subset of A.
For example:
when A = [1], the output should be []
when A = [1, 4], the output should be [object_1]
when A = [1, 3, 4], the output should be [object_1, object_2]
The problem can be described in python:
from typing import List
# problem description
class Object(object):
def __init__(self, integers):
self.integers = integers
def size(self):
return len(self.integers)
object_1 = Object([1, 4])
object_2 = Object([1, 3])
object_3 = Object([100])
def _find_subset_objects(integers): # type: (List[int]) -> List[Object]
raise NotImplementedError()
def test(find_subset_objects=_find_subset_objects):
assert find_subset_objects([1]) == []
assert find_subset_objects([1, 4]) == [object_1]
assert find_subset_objects([1, 3, 4]) == [object_1, object_2]
Is there some algorithm or some data struct is aim to solve this kind of problem?

Store the objects in an array. The indices will be 0 ... ~100K. Then create two helper arrays.
First one with the element counts for every object. I will call this array obj_total(This could be ommited by calling the object.size or something similar if you wish.)
Second one initialized with zeroes. I will call it current_object_count.
For every integer property p where 0 < p <= 20000, create a list of indices where index i in the list means that the element is contained in the i-th object.
It is getting messy and I'm getting lost in the names. Time for the example with the objects that you used in the question:
objects = [[1, 4], [1, 3], [100]]
obj_total = [2, 2, 1]
current_object_count = [0, 0, 0]
object_1_ref = [0, 1]
object_2_ref = [ ]
object_3_ref = [1]
object_4_ref = [0]
object_100_ref = [100]
object_refs = [object_1_ref ,object_2_ref , ... , object_100_ref]
#Note that you will want to do this references dynamically.
#I'm writing them out only for the sake of clarity.
Now we are given the input array, for example [1, 3, 4]. For every element i in the array, we look we look at the object_i_ref. We then use the indices in the reference array to increase the values in the current_object_count array.
Whenever you increase a value in the current_object_count[x], you also check against the obj_total[x] array. If the values match, the object in objects[x] is a subset of the input array and we can note it down.
When you finish with the input array you have all the results.

Related

How to remove duplicate contiguous elements in an array?

Given an array such as
arr = [1,1,1,2,2,3,4,1,2]
I wish to replace each contiguous string of equal elements with a single instance of that element. Here the result would be
[1,2,3,4,1,2]
The three 1s are replaced by a single 1 and the two 2's are replaced by a single 2.
How can I do that for an arbitrary array of Ruby objects?
I know you can do it with chunk_while and map, but there might be other ways:
[1,1,1,2,2,3,4,1,2].chunk_while { |e, f| e == f }.map(&:first)
# [1, 2, 3, 4, 1, 2]
With chunk_while you split the array by chunks whenever the block is evaluated to true, for that the block yields two variables, the "element before" and the "element after", that's to say, for every iteration you're going to get this:
[1, 1]
[1, 1]
[1, 2]
[2, 2]
[2, 3]
[3, 4]
[4, 1]
[1, 2]
After applying the logic in the proc, it'll chunk the receiver whenever the first and second yielded elements are equal, and you get:
[[1, 1, 1], [2, 2], [3], [4], [1], [2]]
After that you can map that result to get only one element from each array - there are many ways, but first is enough.
The chunk_while proc can also be shortened to (&:==), leaving as [1,1,1,2,2,3,4,1,2].chunk_while(&:==).map(&:first).
Similarly and just out of curiosity, you can use slice_when and last to save 2 characters: [1,1,1,2,2,3,4,1,2].slice_when(&:!=).map(&:last).
You can use recursive functions to solve this type problems.
I tried different tests. It works.
mylist = [1,1,1,2,2,3,4,1,2] #Output:[1,2,3,4,1,2]
mylist2 = [1,1,1,1,1,1,2,2,2,2,3,3,5,5,5,4,1,2] # Output:[1,2,3,5,4,1,2]
mylist3 = [-1,-1,-2,3,5,5,5,4,3,8,8,8,9,6,0,0,6,5] # Output: [-1,-2,3,5,4,3,8,9,6,0,6,5]
def myfunc(mylist)
mylist.each_with_index do |val, index|
if val == mylist[index+1]
mylist.delete_at(index+1)
myfunc(mylist) # here we call same function again
end
end
return mylist
end
print myfunc(mylist)
puts
print myfunc(mylist2)
puts
print myfunc(mylist3)
arr = [1,1,1,2,2,3,4,1,2]
arr.each do |i|
(0..arr.length()).each do |j|
if arr[j] == arr[j+1]
arr.delete_at(j)
break
end
end
end
print arr
What is happening here is the entire list is being traversed; matching consecutive elements in each iteration.
Everytime a match is found, delete it and move on to the next iteration (break).
In effect, you will be changing the length of the array in each iteration. hence the break to avoid index errors.
The nested looping is added to handle any no. of consecutive duplicates.

Ruby - pushing values from an array combination to a new array

I am trying to print all the different sums of all combinations in this array [1,2,3]. I want to first push every sum result to a new array b, then print them using b.uniq so that non of the sum results are repeated.
However, with the code I have, the 3 repeats itself, and I think it is because of the way it is pushed into the array b.
Is there a better way of doing this?
a = [1,2,3]
b = []
b.push a
b.push a.combination(2).collect {|a,b| (a+b)}
b.push a.combination(3).collect {|a,b,c| (a+b+c)}
puts b.uniq
p b #[[1, 2, 3], [3, 4, 5], [6]]
Can someone please help me with this? I am still new in ruby.
Because an Array of arbitrary length can be summed using inject(:+), we can create a more general solution by iterating over the range 1..n, where n is the length of the Array.
(1..(a.size)).flat_map do |n|
a.combination(n).map { |c| c.inject(&:+) }
end.uniq
#=> [1, 2, 3, 4, 5, 6]
By using flat_map, we can avoid getting the nested Array result, and can call uniq directly on it. Another option to ensure uniqueness would be to pass the result to a Set, for which Ruby guarantees uniqueness internally.
require "set"
sums = (1..(a.size)).flat_map do |n|
a.combination(n).map { |c| c.inject(&:+) }
end
Set.new(sums)
#=> #<Set: {1, 2, 3, 4, 5, 6}>
This will work for an any Array, as long as all elements are Fixnum.
If all you want is an array of the possible sums, flatten the array before getting the unique values.
puts b.flatten.uniq
What is happening is uniq is running over a multi-dimensional array. This causes it to look for duplicate arrays in your array. You'll need the array to be flattened first.

map! isn't changing the original array

#key contains an array of four different three-digit integers. someArray has about ten three-digit ints. I have a method that has an array I'm attempting to modify. I'm using map! to accomplish this:
def multiply()
count = 0
#someArray.map! do |map|
if #key[count]
map = map * #key[count]
count = count + 1
else
count = 0
map = map * #key[count]
count = 1
end
end
print #someArray
end
I'm getting a few unexpected results.This prints [1,2,3,4,1,2,3,4,1,2,3,4,1,2]. Why wouldn't this print the map * #key value instead of the count?
.map uses the return value from the block. Your return value is either count = 1 or count = count + 1.
You cannot assign over top of the block's input variable, that has absolutely no effect.
Correctly written, your block would look something like this:
#someArray.map! do |i|
if #key[count]
i *= #key[count]
count = count + 1
else
i *= #key[0]
count = 1
end
i
end
As an aside, this is a slightly inappropriate use of map. There are far better ways of combining the elements of two arrays, even if one of those arrays is shorter.
Given two inputs:
someArray = [1,2,3,4,5,6,7,8,9,0]
key = [2,4,6]
You can combine these two arrays into one array of pairs, using .cycle to produce an enumerator that will wrap around so that both arrays are functionally the same length:
pairs = someArray.zip(key.cycle)
# => [[1, 2], [2, 4], [3, 6], [4, 2], [5, 4], [6, 6], [7, 2], [8, 4], [9, 6], [0, 2]]
Then, you can map the resulting array, multiplying the pairs:
pairs.map { |a,b| a * b }
# => [2, 8, 18, 8, 20, 36, 14, 32, 54, 0]
So, in all, your method would be:
def multiply()
#someArray.zip(#key.cycle).map { |a,b| a * b }
end

Set all values of a column in a multidimensional array

I have the following code that adds zero to values of a specific row in a multidimensional array:
def self.zero_row(matrix, row_index)
matrix[row_index].each_with_index do |item, index|
matrix[row_index][index] = 0
end
return matrix
end
I am wondering how I would go in order to make zeros all the values given a specific column_index.
def self.zero_column(matrix, col_index)
#..
end
To follow the same pattern as your other method you could do something like this:
def self.zero_column(matrix, col_index)
matrix.each_with_index do |item, row_index|
matrix[row_index][col_index] = 0
end
end
Would this fit the bill?
def self.zero_column(matrix, col_index)
matrix = matrix.transpose
matrix[col_index].map!{0}
matrix.transpose
end
Similarly, you could simplify your zero_row method
def self.zero_row(matrix, row_index)
matrix[row_index].map!{0}
matrix
end
If you need to deal with columns frequently, then I would say it is a design flaw to use a nested array. Nesting array has almost no benefit, and just makes things complicated. You should better have a flat array. It is much easier to manipulate columns equally as rows with flat arrays.
If you want a 3 by 2 matrix, then you can initialize it simply as an array with the length 3 * 2 like:
a = [1, 2, 3, 4, 5, 6]
Then, you can refer to the second column (index 1) or row as respectively:
a.select.with_index{|_, i| i % 2 == 1} # => [2, 4, 6]
a.select.with_index{|_, i| i / 2 == 1} # => [3, 4]
Rewriting all values of that column or row to 0 would be respectively:
a.each_index{|i| a[i] = 0 if i % 2 == 1} # => a: [1, 0, 3, 0, 5, 0]
or
a.each_index{|i| a[i] = 0 if i / 2 == 1} # => a: [1, 2, 0, 0, 5, 6]
Switching between an operation on a column and another on a row would be a matter of switching between % and /; you can see symmetry/consistency. If you need to keep the information regarding column length 2 within the array, then just assign it as an instance variable of that array.

cycle through array using index

I have an array
arr = [1,2,3,4,5]
and I'm wondering if there is a way to cycle through it so something like:
i = 2
arr[3+n]
would return 1, rather than nil
Is that possible using the index, or even with next?
It's called cycle:
c = [1,2,3,4,5].cycle
10.times{p c.next}
Perform a modulo on the index using the array size:
arr = [1, 2, 3, 4, 5]
arr[5 % arr.size] #=> 1

Resources