Возможно, это не самый эффективный способ формирования непересекающихся массивов, но он дает желаемый результат. Доказательство того, что это работает, легко доказывается противоречием.
arr = [[0,2], [1,3], [4,6], [7,9], [6,8], [5,7], [2,4], [3,7], [10,11]]
require 'set'
sets = arr.map(&:to_set)
#=> [#<Set: {0, 2}>, #<Set: {1, 3}>, #<Set: {4, 6}>, #<Set: {7, 9}>, #<Set: {6, 8}>,
# #<Set: {5, 7}>, #<Set: {2, 4}>, #<Set: {3, 7}>, #<Set: {10, 11}>]
loop do
break if sets.size == 1
set1, set2 = sets.combination(2).find { |set1,set2| (set1 & set2).any? }
break if set1.nil?
set1.replace(set1 | set2)
sets.delete(set2)
end
sets.map(&:to_a)
#=> [[0, 2, 4, 6, 8], [1, 3, 7, 9, 5], [10, 11]]
Я использовал наборы вместо массивов для ускорения вычислений объединения и пересечения.
Этапы могут быть проиллюстрированы включением некоторых puts
операторов.
sets = arr.map(&:to_set)
loop do
puts "(#{sets.size} sets at beginning of loop"
puts " #{sets}"
puts " break as sets.size == 1" if sets.size == 1
break if sets.size == 1
set1, set2 = sets.combination(2).find { |set1,set2| (set1 & set2).any? }
if set1.nil?
puts " After find, set1 = nil, so break" if set1.nil?
else
puts " After find, set1 = #{set1}"
puts " set2 = #{set2}"
end
break if set1.nil?
set1.replace(set1 | set2)
sets.delete(set2)
puts " sets after set1 |= set2 and sets.delete(set2)"
puts " #{sets}"
end
sets.map(&:to_a)
печатает следующее.
(9) sets at beginning of loop
[#<Set: {0, 2}>, #<Set: {1, 3}>, #<Set: {4, 6}>, #<Set: {7, 9}>, #<Set: {6, 8}>,
#<Set: {5, 7}>, #<Set: {2, 4}>, #<Set: {3, 7}>, #<Set: {10, 11}>]
After find, set1 = #<Set: {0, 2}>
set2 = #<Set: {2, 4}>
sets after set1 |= set2 and sets.delete(set2)
[#<Set: {0, 2, 4}>, #<Set: {1, 3}>, #<Set: {4, 6}>, #<Set: {7, 9}>,
#<Set: {6, 8}>, #<Set: {5, 7}>, #<Set: {3, 7}>, #<Set: {10, 11}>]
(8) sets at beginning of loop
[#<Set: {0, 2, 4}>, #<Set: {1, 3}>, #<Set: {4, 6}>, #<Set: {7, 9}>,
#<Set: {6, 8}>, #<Set: {5, 7}>, #<Set: {3, 7}>, #<Set: {10, 11}>]
After find, set1 = #<Set: {0, 2, 4}>
set2 = #<Set: {4, 6}>
sets after set1 |= set2 and sets.delete(set2)
[#<Set: {0, 2, 4, 6}>, #<Set: {1, 3}>, #<Set: {7, 9}>, #<Set: {6, 8}>,
#<Set: {5, 7}>, #<Set: {3, 7}>, #<Set: {10, 11}>]
(7) sets at beginning of loop
[#<Set: {0, 2, 4, 6}>, #<Set: {1, 3}>, #<Set: {7, 9}>, #<Set: {6, 8}>,
#<Set: {5, 7}>, #<Set: {3, 7}>, #<Set: {10, 11}>]
After find, set1 = #<Set: {0, 2, 4, 6}>
set2 = #<Set: {6, 8}>
sets after set1 |= set2 and sets.delete(set2)
[#<Set: {0, 2, 4, 6, 8}>, #<Set: {1, 3}>, #<Set: {7, 9}>, #<Set: {5, 7}>,
#<Set: {3, 7}>, #<Set: {10, 11}>]
(6) sets at beginning of loop
[#<Set: {0, 2, 4, 6, 8}>, #<Set: {1, 3}>, #<Set: {7, 9}>, #<Set: {5, 7}>,
#<Set: {3, 7}>, #<Set: {10, 11}>]
After find, set1 = #<Set: {1, 3}>
set2 = #<Set: {3, 7}>
sets after set1 |= set2 and sets.delete(set2)
[#<Set: {0, 2, 4, 6, 8}>, #<Set: {1, 3, 7}>, #<Set: {7, 9}>, #<Set: {5, 7}>,
#<Set: {10, 11}>]
(5) sets at beginning of loop
[#<Set: {0, 2, 4, 6, 8}>, #<Set: {1, 3, 7}>, #<Set: {7, 9}>, #<Set: {5, 7}>,
#<Set: {10, 11}>]
After find, set1 = #<Set: {1, 3, 7}>
set2 = #<Set: {7, 9}>
sets after set1 |= set2 and sets.delete(set2)
[#<Set: {0, 2, 4, 6, 8}>, #<Set: {1, 3, 7, 9}>, #<Set: {5, 7}>, #<Set: {10, 11}>]
(4) sets at beginning of loop
[#<Set: {0, 2, 4, 6, 8}>, #<Set: {1, 3, 7, 9}>, #<Set: {5, 7}>, #<Set: {10, 11}>]
After find, set1 = #<Set: {1, 3, 7, 9}>
set2 = #<Set: {5, 7}>
sets after set1 |= set2 and sets.delete(set2)
[#<Set: {0, 2, 4, 6, 8}>, #<Set: {1, 3, 7, 9, 5}>, #<Set: {10, 11}>]
(3) sets at beginning of loop
[#<Set: {0, 2, 4, 6, 8}>, #<Set: {1, 3, 7, 9, 5}>, #<Set: {10, 11}>]
After find, set1 = nil, so break
sets.map(&:to_a)
#=> [[0, 2, 4, 6, 8], [1, 3, 7, 9, 5], [10, 11]]