Как объединить N отсортированных массивов (или других подобных спискам структур данных) в Ruby? Например, в Python вы будете использовать heapq.merge. Должно быть что-то подобное встроено в Ruby, верно?
Слияние N отсортированных массивов в рубине лениво
Ответ 1
Я закончил тем, что писал сам, используя структуры данных из "алгоритма". Это было не так плохо, как я ожидал.
require 'algorithms'
class LazyHeapMerger
def initialize(sorted_arrays)
@heap = Containers::Heap.new { |x, y| (x.first <=> y.first) == -1 }
sorted_arrays.each do |a|
q = Containers::Queue.new(a)
@heap.push([q.pop, q])
end
end
def each
while @heap.length > 0
value, q = @heap.pop
@heap.push([q.pop, q]) if q.size > 0
yield value
end
end
end
m = LazyHeapMerger.new([[1, 2], [3, 5], [4]])
m.each do |o|
puts o
end
Ответ 2
Здесь (слегка в гольф) решение, которое должно работать на массивах любых "списков", которые поддерживают #first
, #shift
и #empty?
. Обратите внимание, что это разрушительно - каждый вызов lazymerge
удаляет один элемент из одной коллекции.
def minheap a,i
r=(l=2*(m=i)+1)+1 #get l,r index
m = l if l< a.size and a[l].first < a[m].first
m = r if r< a.size and a[r].first < a[m].first
(a[i],a[m]=a[m],a[i];minheap(a,m)) if (m!=i)
end
def lazymerge a
(a.size/2).downto(1){|i|minheap(a,i)}
r = a[0].shift
a[0]=a.pop if a[0].empty?
return r
end
p arrs = [ [1,2,3], [2,4,5], [4,5,6],[3,4,5]]
v=true
puts "Extracted #{v=lazymerge (arrs)}. Arr= #{arrs.inspect}" while v
Вывод:
[[1, 2, 3], [2, 4, 5], [4, 5, 6], [3, 4, 5]]
Extracted 1. Arr= [[2, 3], [2, 4, 5], [4, 5, 6], [3, 4, 5]]
Extracted 2. Arr= [[3], [2, 4, 5], [4, 5, 6], [3, 4, 5]]
Extracted 2. Arr= [[4, 5], [3], [4, 5, 6], [3, 4, 5]]
Extracted 3. Arr= [[4, 5], [3, 4, 5], [4, 5, 6]]
Extracted 3. Arr= [[4, 5], [4, 5], [4, 5, 6]]
Extracted 4. Arr= [[5], [4, 5], [4, 5, 6]]
Extracted 4. Arr= [[5], [5], [4, 5, 6]]
Extracted 4. Arr= [[5, 6], [5], [5]]
Extracted 5. Arr= [[6], [5], [5]]
Extracted 5. Arr= [[5], [6]]
Extracted 5. Arr= [[6]]
Extracted 6. Arr= [[]]
Extracted . Arr= [[]]
Отметим также, что этот алгоритм также ленив о сохранении свойства кучи - он не поддерживается между вызовами. Это, вероятно, заставляет его выполнять больше работы, чем необходимо, поскольку при каждом последующем вызове происходит полная переуступка. Это можно было бы зафиксировать, выполнив полную команду heapify после начала, затем набрав minheap(a,0)
до строки return r
.
Ответ 3
Вот реализация, которая должна работать над любыми Перечислимыми, даже бесконечными. Он возвращает Enumerator.
def lazy_merge *list
list.map!(&:enum_for) # get an enumerator for each collection
Enumerator.new do |yielder|
hash = list.each_with_object({}){ |enum, hash|
begin
hash[enum] = enum.next
rescue StopIteration
# skip empty enumerators
end
}
loop do
raise StopIteration if hash.empty?
enum, value = hash.min_by{|k,v| v}
yielder.yield value
begin
hash[enum] = enum.next
rescue StopIteration
hash.delete(enum) # remove enumerator that we already processed
end
end
end
end
Infinity = 1.0/0 # easy way to get infinite range
p lazy_merge([1, 3, 5, 8], (2..4), (6..Infinity), []).take(12)
#=> [1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10]
Ответ 4
Нет, ничего не создано для этого. По крайней мере, ничего, что мгновенно приносит в голову. Однако существует проект GSoC для реализации соответствующих типов данных пару лет назад, которые вы могли бы использовать.