Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the most Ruby-like way of generating every unique combination of 3 positive integers that add up to 100

Conditions:

a + b + c = 100
a,b,c positive integers or 0

Desired output:

[
  [0,0,100],
  [0,1,99 ],
  ... # all other permutations
  [99,1,0 ],
  [100,0,0]
]
like image 565
OhNoez Woez Avatar asked Dec 06 '12 22:12

OhNoez Woez


3 Answers

I'd write:

(0..100).flat_map { |x| (0..100-x).map { |y| [x, y, 100-x-y] } }
#=> [[0, 0, 100], [0, 1, 99]], ..., [99, 1, 0], [100, 0, 0]]

Site note 1: this is a classical example where list-comprehensions shine (and even more if there were a condition somewhere). Since Ruby has no LC we have to do the typical conversion to OOP: N-1 flat_map's + 1 map. It would be awesome to have LCs in Ruby (check this feature request), Scala has proven that even a pure OOP language greatly benefits from this syntactic sugar (though I can understand prevention from the devs because of the implicit iterable protocol/method). On an imaginary Ruby that supported them you'd write:

[[x, y, 100-x-y] for x in 0..100 for y in 0..100-x] # imaginary Ruby

Side note 2: Imagine that you prefer a less memory-consuming solution (you probably don't need the whole array). A lazy solution with Ruby 2.0 requires just to add a couple of [lazy][2] proxies:

(0..100).lazy.flat_map { |x| (0..100-x).lazy.map { |y| [x, y, 100-x-y] } }

Side note 3: Just for completeness, in the line of @akuhn answer, another lazy solution using enumerators:

Enumerator.new do |e| 
  (0..100).each { |x| (0..100-x).each { |y| e.yield([x, y, 100-x-y]) } }
end
like image 60
12 revs Avatar answered Nov 18 '22 02:11

12 revs


Here's my solution

for a in 0..100; for b in 0..100-a; p [a,b,100-a-b]; end; end

which I find reads almost like @tokland’s list comprehension. If you like to use the values further downstream rather than printing them, put the code in a generator method

def generate
  return enum_for(:generate) unless block_given?
  for a in 0..100
    for b in 0..100-a
      yield [a,b,100-a-b]
    end
  end
end

which is then used as in

generate { |a,b,c| puts [a,b,c] } 

or as in

generate { |each| p each }

or as in

p generate.to_a

which all print all generated tuples.

like image 4
akuhn Avatar answered Nov 18 '22 03:11

akuhn


I'd say the most Ruby-ish way (which is not by any means the most efficient way) is this:

(0..100).to_a.repeated_permutation(3).find_all{|triplet|triplet.inject(:+)==100}

The only non-transparent part of this chain is the to_a, which converts the range object given by (0..100) to an array (ranges are lazy, which doesn't work with repeated_permutation).

like image 3
histocrat Avatar answered Nov 18 '22 04:11

histocrat