0

say you have a list [ 1 , 2 ,3 ...... n] if you needed to compare two elements so you would write something like

list = (0..9999).to_a

idx = 0

while idx < list.length
  idx2 = idx
  while idx2 < list.length
    puts list[idx] + list[idx2] if (list[idx] + list[idx2]).odd?
    idx2 += 1
  end
  idx += 1
end

But what if the number of comparisons is not constant and increases? This code hard codes the comparison by having one loop inside another, but if you needed to compare 4 or more elements how does one write a loop or something that achieves this if you don't know the maximum number of comparisons?

user1087058
  • 289
  • 1
  • 2
  • 5

3 Answers3

2

We have a helpful method in ruby to do this, and that is Array#combination:

def find_odd_sums(list, num_per_group)
  list.combination(num_per_group).to_a.map(&:sum).select(&:odd?)
end

You can re-implement combination, if you choose to. There are many versions of this function available at Algorithm to return all combinations of k elements from n

max pleaner
  • 23,403
  • 8
  • 50
  • 99
  • 1
    You can remove the `Enumerator#to_a` after `Array#combination`. I can't remove it because it's less than 6 chars. – 3limin4t0r Oct 14 '17 at 10:50
0

This question is not clear. Firstly, the title, which is vague, asks how a particular approach to an unstated problem can be implemented. What you need, at the beginning, is a statement in words of the problem.

I will make a guess as to what that statement might be and then propose a solution.

Given

  • an array arr;
  • a positive integer n, 1 <= n <= arr.size; and
  • a method m having n arguments that are distinct elements of arr that returns true or false,

what combinations of n elements of arr cause m to return true?

We can use the following method combined with a definition of the method m.

def combos(arr, n, m)
  arr.combination(n).select { |x| public_send(m, *x) }
end

The key, of course, is the method Array#combination. See also the docs for the methods Enumerable#select and Object#public_send.

Here is its use with the example given in the question.

def m(*x)
  x.sum.odd?
end

arr = [1,2,3,4,5,6]

combos(arr, 2, :m)
  #=> [[1, 2], [1, 4], [1, 6], [2, 3], [2, 5], [3, 4], [3, 6], [4, 5], [5, 6]]
combos(arr, 3, :m)
  #=> [[1, 2, 4], [1, 2, 6], [1, 3, 5], [1, 4, 6], [2, 3, 4], [2, 3, 6],
  #    [2, 4, 5], [2, 5, 6], [3, 4, 6], [4, 5, 6]]
combos(arr, 4, :m)
  #=> [[1, 2, 3, 5], [1, 2, 4, 6], [1, 3, 4, 5], [1, 3, 5, 6], [2, 3, 4, 6], [2, 4, 5, 6]]

See the doc for Array#sum (which made it's debut in Ruby v2.4.

Here's a second example: given an array of letters, which combinations of five letters have two vowels?

VOWEL_COUNTER = %w| a e i o u |.product([1]).to_h.tap { |h| h.default=0 }
  #=> {"a"=>1, "e"=>1, "i"=>1, "o"=>1, "u"=>1}
VOWEL_COUNTER['a']
  #=> 1

By setting the hash's default value to zero, VOWEL_COUNTER[k] will return zero if it does not have a key k. For example,

VOWEL_COUNTER['r']
  #=> 0

def m(*x)
  x.sum { |c| VOWEL_COUNTER[c] } == 2
end

arr = %w| a r t u e v s |    
combos(arr, 5, :m)
  #=> [["a", "r", "t", "u", "v"], ["a", "r", "t", "u", "s"],
  #    ["a", "r", "t", "e", "v"], ["a", "r", "t", "e", "s"],
  #    ["a", "r", "u", "v", "s"], ["a", "r", "e", "v", "s"],
  #    ["a", "t", "u", "v", "s"], ["a", "t", "e", "v", "s"],
  #    ["r", "t", "u", "e", "v"], ["r", "t", "u", "e", "s"],
  #    ["r", "u", "e", "v", "s"], ["t", "u", "e", "v", "s"]]

Note that VOWEL_COUNTER is constructed as follows.

a = %w| a e i o u |
  #=> ["a", "e", "i", "o", "u"]
b = a.product([1])
  #=> [["a", 1], ["e", 1], ["i", 1], ["o", 1], ["u", 1]]
c = b.to_h
  #=> {"a"=>1, "e"=>1, "i"=>1, "o"=>1, "u"=>1}

With this hash,

c['r']
  #=> nil

so we need to set the default value to zero.

VOWEL_COUNTER = c.tap { |h| h.default=0 }
  #=> {"a"=>1, "e"=>1, "i"=>1, "o"=>1, "u"=>1}
c['r']
  #=> 0

Alternatively, we could have omitted the last step (setting the hash's default to zero), and written

x.sum { |c| VOWEL_COUNTER[c].to_i } == 2

because NilClass#to_i converts nil to zero.

See also the docs for the methods #select, #public_send

Cary Swoveland
  • 94,081
  • 5
  • 54
  • 87
0

I feel like everyone is making this more complicated than it is. You sure got pointed to the right direction (Array#combination, Array#repeated_combination, Array#permutation, Array#repeated_permutation). To accomplish the exact thing you are doing, you can simply do:

list.repeated_combination(2) { |c| puts c.sum if c.sum.odd? }

Check the links above to see the difference between them.

If you want to create a helper method you can, but in my opinion it's not really needed in this case. Replace 2 with the number you are looking for and you got your answer.

3limin4t0r
  • 13,832
  • 1
  • 17
  • 33