Efficient approach to distributing elements of an array into chunks - ruby

Given an array of length N, how I can I equally distribute the elements of the array into another array of arbitrary length?
For instance, I have 3 items in an array and I'd like it distributed evenly across another array of 9 slots.
[1, 2, 3]
should result in (something close to)
[[], [], [1], [], [], [2], [], [], [3]]
However, if I have 9 items to distribute to an array length of 2, it should result in
[[1,2,3,4], [5,6,7,8,9]]
Thanks!
NOTE: The position of the resulting array items could differ according to the algorithm, but the intent is to get some level of uniform distribution. In the first example, the 0th item could be [1]. In the second example, the 0th item could have [1,2,3,4,5].

Here's an easy way to do that:
def distribute(arr, slots)
n = arr.size
a = Array.new(slots) { [] }
arr.each_with_index { |e,i| a[i*slots/n] << e }
a
end
distribute([1,2,3], 9)
#=> [[1], [], [], [2], [], [], [3], [], []]
distribute([*(1..9)], 2)
#=> [[1, 2, 3, 4, 5], [6, 7, 8, 9]]
You could change the distributions that result by modifying i*slots/n.

So there are two totally different use cases here, one where you have to build an array of length n, the other where you need to split into an array of length n.
This feels like a homework assignment but I don't really have enough off these two use cases to see a pattern (unless I'm missing something huge).
Test cases:
it 'splits on n vals' do
arr = [1,2,3]
expect(chunk(arr, 9)).to eq [[], [], [1], [], [], [2], [], [], [3]]
end
it 'splits on n vals' do
arr = [1,2,3,4,5,6,7,8,9]
expect(chunk(arr,2)).to eq [[1,2,3,4,5],[6,7,8,9]]
end
Code:
def chunk(arr, num)
if num < arr.length
return arr.each_slice( (arr.size/num.to_f).round).to_a
end
array = []
len = arr.length
(0..num).each do |i|
if (i % len == 0) && i != 0
array[i-1] = [arr.first]
array[i] = []
arr.shift
else
array[i] = []
end
end
array.pop
array
end

Related

How to improve algorithm efficiency for nested loop

Given a list of integers and a single sum value, return the first two values (from the left) that add up to form the sum.
For example, given:
sum_pairs([10, 5, 2, 3, 7, 5], 10)
[5, 5] (at indices [1, 5] of [10, 5, 2, 3, 7, 5]) add up to 10, and [3, 7] (at indices [3, 4]) add up to 10. Among them, the entire pair [3, 7] is earlier, and therefore is the correct answer.
Here is my code:
def sum_pairs(ints, s)
result = []
i = 0
while i < ints.length - 1
j = i+1
while j < ints.length
result << [ints[i],ints[j]] if ints[i] + ints[j] == s
j += 1
end
i += 1
end
puts result.to_s
result.min
end
It works, but is too inefficient, taking 12000 ms to run. The nested loop is the problem of inefficiency. How could I improve the algorithm?
Have a Set of numbers you have seen, starting empty
Look at each number in the input list
Calculate which number you would need to add to it to make up the sum
See if that number is in the set
If it is, return it, and the current element
If not, add the current element to the set, and continue the loop
When the loop ends, you are certain there is no such pair; the task does not specify, but returning nil is likely the best option
Should go superfast, as there is only a single loop. It also terminates as soon as it finds the first matching pair, so normally you wouldn't even go through every element before you get your answer.
As a style thing, using while in this way is very unRubyish. In implementing the above, I suggest you use ints.each do |int| ... end rather than while.
EDIT: As Cary Swoveland commented, for a weird reason I thought you needed indices, not the values.
require 'set'
def sum_pairs(arr, target)
s = Set.new
arr.each do |v|
return [target-v, v] if s.include?(target-v)
s << v
end
nil
end
sum_pairs [10, 5, 2, 3, 7, 5], 10
#=> [3, 7]
sum_pairs [10, 5, 2, 3, 7, 5], 99
#=> nil
I've used Set methods to speed include? lookups (and, less important, to save only unique values).
Try below, as it is much more readable.
def sum_pairs(ints, s)
ints.each_with_index.map do |ele, i|
if ele < s
rem_arr = ints.from(i + 1)
rem = s - ele
[ele, rem] if rem_arr.include?(rem)
end
end.compact.last
end
One liner (the fastest?)
ary = [10, 0, 8, 5, 2, 7, 3, 5, 5]
sum = 10
def sum_pairs(ary, sum)
ary.map.with_index { |e, i| [e, i] }.combination(2).to_a.keep_if { |a| a.first.first + a.last.first == sum }.map { |e| [e, e.max { |a, b| a.last <=> b.last }.last] }.min { |a, b| a.last <=> b.last }.first.map{ |e| e.first }
end
Yes, it's not really readable, but if you add methods step by step starting from ary.map.with_index { |e, i| [e, i] } it's easy to understand how it works.

Ruby: reverse, mutating list

I'm trying to write a method, which reverses a list, but not using .reverse.
Here is my code:
def reverse(list)
a = list.length
while a >= 0
list << list[a]
a = a - 1
end
list
end
print reverse([1,2,3])
My expected result isn't [3,2,1] but [1, 2, 3, nil, 3, 2, 1]
Do you have any advice how to not repeat the original list once again, but only mutate it?
This mutates the original array as requested. nil is eliminated by being aware that the last element of the list is at list[list.length-1].
def reverse(list)
a = list.length-1
while a >= 0
list << list[a]
list.delete_at(a)
a = a - 1
end
list
end
p reverse([1, 2, 3]) #=> [3, 2, 1]
A more Ruby way could be as follows:
arr.sort_by!.with_index { |_,i| -i }
I understand the list is to be reversed in place (mutated). Below are two ways to do that.
If the list is not to be mutated, simply operate on a copy:
def non_mutating_reverse(list)
reverse(list.dup)
end
#1
Use parallel assignment (sometimes called multiple assignment).
def reverse(list)
(list.size/2).times { |i| list[i], list[-1-i] = list[-1-i], list[i] }
list
end
list = [1,2,3]
reverse list #=> [3, 2, 1]
list #=> [3, 2, 1]
Notice that when the size of the list is odd (as in this example), the middle element is not moved.
#2
def reverse(list)
list.replace(list.size.times.with_object([]) { |i,a| a.unshift(list[i]) })
end
list = [1,2,3]
reverse list #=> [3, 2, 1]
list #=> [3, 2, 1]

Simple Way to Check if the Ruby array is a matrix?

I am working on a project that involves checking if the input is a n-dimensional matrix(and find its dimensions) and raise error if not. For example
arr = [ [[1,2],[3,4]], [[5,6],[7,8]], [[9,10],[11,12]] ]
is a matrix of dimensions [3 2 2]. What would be the simplest generic way to do that ?
A recursive solution, but not pretty easy to understand.
arr1 = [[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]]
arr2 = [[[1, 2], [4]], [6, [7, 8]]]
def dimensions(m)
if m.any? { |e| e.is_a?(Array) }
d = m.group_by { |e| e.is_a?(Array) && dimensions(e) }.keys
[m.size] + d.first if d.size == 1 && d.first
else
[m.size]
end
end
dimensions(arr1) #=> [3, 2, 2]
dimensions(arr2) #=> nil
Explaination
The algorithm checks first for nested arrays, m.any? { |e| e.is_a?(Array) }.
If there aren't nested arrays then you have just one dimension and it returns the size of the given array via [m.size] within the else block.
dimensions([1,2,3]) #=> [3]
If there is at least one nested array then you have to ensure that all elements are arrays and the arrays have the same dimensions. This check is done via d = m.group_by { |e| e.is_a?(Array) && dimensions(e) }.keys which groups all elements by its dimensions.
[[5, 6], [7, 8]].group_by { |e| ... }.keys
#=> [[2]], all nested array dimensions are equal [2]
[[1, 2], [4]].group_by { |e| ... }.keys
#=> [[1], [2]], different dimensions
[6, [7, 8]].group_by { |e| ... }.keys
#=> [false, [2]], an element isn't an array
The algorithm takes only the valid results of the group_by with if d.size == 1 && d.first and adds the dimensions of the nested arrays to the result via [m.size] + d.first.
If there are more than one key element or only nil which means all nested arrays are invalid then it returns nil implicitly.
That's all.
Ruby actually has a Matrix class, maybe use that?
Matrix[[[1,2],[3,4]], [[5,6],[7,8]], [[9,10],[11,12]]]
#=> Matrix[[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]]
Matrix[[1,2], [3]]
# ExceptionForMatrix::ErrDimensionMismatch: row size differs (1 should be 2)
Look for patterns in your data
If you look at your example of
[[[1,2],[3,4]], [[5,6],[7,8]], [[9,10],[11,12]]]
and dimension [3, 2, 2] you can read the dimension element-by-element in the following way:
An array of 3 items and each of the items is ...
an array of 2 items and each of the subitems is ...
an array of 2 items.
This suggest that a dimension can be computed by calling Array#size on each level of depth.
Compute the dimension
The method above can be implemented as:
def unchecked_matrix_dimension(matrix)
dimension = []
while matrix.is_a?(Array)
dimension << matrix.size
matrix = matrix[0]
end
dimension
end
This code looks at elements in the first position only so [[1], []] is reported as having dimension of [2, 1] but it's not a valid matrix at all.
Wishful coding
Assume for a moment that we have a function matrix_dimension?(matrix, dimension) that returns true if matrix is of the specified dimension and false otherwise. We can use it to detect invalid matrices like this:
def matrix_dimension(matrix)
dimension = unchecked_matrix_dimension(matrix)
if matrix_dimension?(matrix, dimension)
dimension
else
nil
end
end
It turns out that writing matrix_dimension? is easy!
Wishes come true
We can define matrix_dimension? in a recursive fashion:
If dimension == [] then we expect a scalar value.
If dimension == [d_1] then we expect an array of d_1 submatrices of dimension [] (i.e. scalars).
If dimension == [d_1, d_2] then we expect an array of d_1 submatrices of dimension [d_2] (i.e. arrays of d_2 scalars).
In general, if dimension == [d_1, ..., d_n] then we expect an array of d_1 elements and each of these elements should be of dimension [d_2, ..., d_n. In Ruby:
def matrix_dimension?(matrix, dimension)
if dimension == []
!matrix.is_a?(Array)
else
matrix.size == dimension[0] &&
matrix.all? { |submatrix| matrix_dimension?(submatrix, dimension[1..-1]) }
end
end
With this definition of matrix_dimension? our matrix_dimension function will return the dimension, if the argument is a valid n-dimension matrix, or nil otherwise.
Complete code
def unchecked_matrix_dimension(matrix)
dimension = []
while matrix.is_a?(Array)
dimension << matrix.size
matrix = matrix[0]
end
dimension
end
def matrix_dimension(matrix)
dimension = unchecked_matrix_dimension(matrix)
if matrix_dimension?(matrix, dimension)
dimension
else
nil
end
end
def matrix_dimension?(matrix, dimension)
if dimension == []
!matrix.is_a?(Array)
else
matrix.size == dimension[0] &&
matrix.all? { |submatrix| matrix_dimension?(submatrix, dimension[1..-1]) }
end
end
Without use of Matrix class:
input = [ [[1,2],[3,4]], [[5,6],[7,8]], [[9,10],[11,12]] ]
m3 = input.map { |a| a.map(&:size) }
m2 = input.map(&:size)
m1 = input.size
checker = ->(e, memo) { raise unless e == memo; e }
[ m1, m2.reduce(&checker), m3.reduce(&checker).reduce(&checker) ]
#⇒ [3, 2, 2]
I've solved this using recursion. If the array represents an n-dimensional matrix, an array of dimensions is returned; else false is returned.
Code
def ndim_matrix(arr)
return false if arr.map(&:size).uniq != [arr.first.size]
arrays, literals = arr.partition { |e| e.is_a? Array }
return [arr.size] if arrays.empty?
return false unless literals.empty?
res = arr.map { |e| ndim_matrix(e) }.uniq
return false if res.size > 1 or res == [false]
[arr.size, *res.first]
end
Examples
arr = [1,2]
ndim_matrix(arr)
#=> [2]
arr = [ [1,2,3],[4,5,6] ]
ndim_matrix(arr)
#=> [2,3]
arr = [ [1,2,3],[4,5,6,7] ]
ndim_matrix(arr)
#=> false
arr = [ [[1,2],[3,4]], [[5,6],[7,8]], [[9,10],[11,12]] ]
ndim_matrix(arr)
#=> [3,2,2]
arr = [ [[1,2],[3,4]], [[5,6],[7,8]], [[9,10]] ]
ndim_matrix(arr)
#=> false
arr = [ [[1,2],[3,4]], [[5,6,7],[7,8]], [[9,10],[11,12]] ]
ndim_matrix(arr)
#=> false
arr = [ [[[1,2,3],[2,1,3]],[[3,4,5],[4,3,2]]],
[[[5,6,7],[6,5,7]],[[7,8,9],[8,7,6]]],
[[[9,10,11],[10,9,8]],[[11,12,13],[12,11,10]]] ]
ndim_matrix(arr)
#=> [3, 2, 2, 3]
arr = [ [[[1,2,3],[2,1,3]],[[3,4],[4,3]]],
[[[5,6,7],[6,5,7]],[[7,8,9],[8,7,6]]],
[[[9,10,11],[10,9,8]],[[11,12,13],[12,11,10]]] ]
ndim_matrix(arr)
#=> false

Grouping an array by comparing 2 adjacent elements

I have an array of objects and I would like to group them based on the difference between the attributes of 2 adjacent elements. The array is already sorted by that attribute. For instance:
Original array:
array = [a, b, c, d, e]
and
a.attribute = 1
b.attribute = 3
c.attribute = 6
d.attribute = 9
e.attribute = 10
If I want to group the elements such that the difference between the attributes of 2 adjacent elements are less or equal than 2, the result should look like so:
END RESULT
result_array = [[a, b], [c], [d, e]]
WHAT I HAVE
def group_elements_by_difference(array, difference)
result_array = []
subgroup = []
last_element_attribute = array.first.attribute
array.each do |element|
if element.attribute <= (last_element_attribute + difference)
subgroup << element
else
#add the subgroup to the result_array
result_array << subgroup
subgroup = []
subgroup << element
end
#update last_element_attribute
last_element_attribute = element.attribute
end
result_array << subgroup
end
QUESTION
Is there a built in function in Ruby 1.9.3, such as group_by that could replace my group_elements_by_difference?
The following uses numerals directly, but the algorithm should be the same as when you do it with attributes. It assumes that all numerals are greater than 0. If not, then replace it with something that works.
array = [1, 3, 6, 9, 10]
[0, *array].each_cons(2).slice_before{|k, l| l - k > 2}.map{|a| a.map(&:last)}
# => [[1, 3], [6], [9, 10]]
With attributes, do l.attribute, etc., and replace 0 with a dummy element whose attribute is 0.
Following Jan Dvorak's suggestion, this solution uses slice_before and a hash to keep the state:
class GroupByAdjacentDifference < Struct.new(:data)
def group_by(difference)
initial = { prev: data.first }
data.slice_before(initial) do |item, state|
prev, state[:prev] = state[:prev], item
value_for(item) - value_for(prev) > difference
end.to_a
end
def value_for(elem)
elem.attribute
end
end
require 'rspec/autorun'
describe GroupByAdjacentDifference do
let(:a) { double("a", attribute: 1) }
let(:b) { double("b", attribute: 3) }
let(:c) { double("c", attribute: 6) }
let(:d) { double("d", attribute: 9) }
let(:e) { double("e", attribute: 10) }
let(:data) { [a, b, c, d, e] }
let(:service) { described_class.new(data) }
context "#group_by" do
it "groups data by calculating adjacent difference" do
expect(service.group_by(2)).to eq([[a, b], [c], [d, e]])
end
end
end
which gives
$ ruby group_by_adjacent_difference.rb
.
Finished in 0.0048 seconds
1 example, 0 failures
In alternative, local variables could also be used to keep state, although I find it a bit harder to read:
class GroupByAdjacentDifference < Struct.new(:data)
def group_by(difference)
tmp = data.first
data.slice_before do |item|
tmp, prev = item, tmp
value_for(item) - value_for(prev) > difference
end.to_a
end
def value_for(elem)
elem.attribute
end
end
array = [1, 3, 6, 9, 10]
prev = array[0]
p array.slice_before{|el| prev,el = el,prev; prev-el > 2}.to_a
# => [[1, 3], [6], [9, 10]]

Algorithm to find a sequence of sub-sequences

Sequence [1,2,3] consider. This sequence has the following 6 different sequence: [1]and [2]and [3] and [1,2] and [2,3] and [1,2,3]
Note! Length the initial sequence may be up to 100 digits.
Please help me. How can I make the following sequences?
I love researching more about this kind of algorithms. Please tell me the name of this type of algorithms.
Here is a c code to print all sub sequences. Algorithm uses nested loops.
#include<stdio.h>
void seq_print(int A[],int n)
{
int k;
for(int i =0;i<=n-1;i++)
{
for(int j=0;j<=i;j++)
{
k=j;
while(k<=i)
{
printf("%d",A[k]);
k++;
}
printf("\n");
}
}
}
void main()
{
int A[]={1,2,3,4,5,6,7,8,9,0};
int n=10;
seq_print(A,n);
}
Your problem can be reduced to the Combination problem. There are already many solutions existed in stackoverflow. You can check this, it may be useful for you.
It is called a power set (in your case the empty set is excluded).
To build a power set, start with a set with an empty set in it; then
for each item in the input set extend the power set with all its subsets accumulated so far
with the current item included (in Python):
def powerset(lst):
S = [[]]
for item in lst:
S += [subset + [item] for subset in S]
return S
Example:
print(powerset([1, 2, 3]))
# -> [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
To avoid producing all subsets at once, a recursive definition could be used:
a power set of an empty set is a set with an empty set in it
a power set of a set with n items contains all subsets from a power set
of a set with n - 1 items plus all these subsets with the n-th item included.
def ipowerset(lst):
if not lst: # empty list
yield []
else:
item, *rest = lst
for subset in ipowerset(rest):
yield subset
yield [item] + subset
Example:
print(list(ipowerset([1, 2, 3])))
# -> [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
Yet another way to generate a power set is to generate r-length subsequences (combinations) for all r from zero to the size of the input set (itertools recipe):
from itertools import chain, combinations
def powerset_comb(iterable):
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
Example:
print(list(powerset_comb([1, 2, 3])))
# -> [(), (1,), (2,), (3,), (1,2), (1,3), (2,3), (1,2,3)]
See also what's a good way to combinate through a set?.

Resources