You could obtain a solution in a reasonably-efficient way recursively. Below I present a solution in Ruby, which I've written in "pseudo-code" fashion. That hopefully will allow readers unfamiliar with Ruby to understand the gist of what is being done.
def doit(arr, nbr, target)
return nil if arr.size < nbr
find_em(arr.sort_by { |n| -n }, nbr, target)
end
def find_em(arr, nbr, target)
tot = arr[0,nbr].sum
return [] if target > tot
(return tot == target ? [arr] : []) if arr.size == nbr
# arr.size > nbr
first, *rest = arr
a = find_em(rest, nbr, target)
if nbr == 1
if first == target
b = [[first]]
a += b
end
else
find_em(rest, nbr-1, target-first).each { |e| a += ([[first] + e]) }
end
a
end
doit([1, 2, 3, 4, 5], 2, 5)
#=> [[3, 2], [4, 1]]
doit([1, 2, 3, 5, 6, 4], 3, 10)
#=> [[5, 3, 2], [5, 4, 1], [6, 3, 1]]
Notice that doit
sorts arr
from largest to smallest and then passes that array to find_it
.
I have assumed, without loss of generality, that all element of arr
are non-negative. If one or more are initially negative the array can be transformed to an equivalent one in which the array contains all non-negative values. Suppose, for example, one wants arrays of size 3 that total 20 and the smallest value in the array equals -4. In this case we can add 4 to each element of the array (making all elements non-negative) and change the required total from 20 to 32 (20 + 3*4).
To better explain how the recursion works I will execute doit
for the first example above after adding puts
statements to find_all
and including the additional code shown immediately below.
INDENT = 4
def indent; $col += INDENT; end
def undent; $col -= INDENT; end
def pu(s); puts "#{" "*$col}#{s}"; end
def puhline; pu('-'*([65-$col,1].max)); end
def find_em(arr, nbr, target)
indent
puhline
pu "arr=#{arr}, nbr=#{nbr}, target=#{target}"
tot = arr[0,nbr].sum
pu "tot=#{tot}"
if target > tot
pu "returning [] because target > tot"
a = []
elsif arr.size == nbr
a = tot == target ? [arr] : []
pu "returning #{a} because arr.size == nbr"
else
# arr.size > nbr
first, *rest = arr
pu "arr.size > nbr, first = #{first}, rest = #{rest}"
pu "when not using first call find_em(#{rest}, #{nbr}, #{target})"
a = find_em(rest, nbr, target)
pu "find_em returned a = #{a}"
if nbr == 1
pu "nbr = 1"
if first == target
pu "first == target"
b = [[first]]
pu "b = #{b}"
a += b
pu "Now a = a+b = #{a}"
else
pu "first != target"
end
else
pu "nbr > 1"
pu "when using first call find_em(#{rest}, #{nbr-1}, #{target-first})"
b = find_em(rest, nbr-1, target-first)
pu "find_em returned b = #{b}"
b.each { |e| a += ([[first] + e]) }
pu "Now a = #{a}"
end
end
pu "returning a=#{a}"
puhline
undent
a
end
$col = -INDENT
doit([1, 2, 3, 4, 5], 2, 5)
#=> [[3, 2], [4, 1]]
displays the following.
arr=[5, 4, 3, 2, 1], nbr=2, target=5
tot=9
arr.size > nbr, first = 5, rest = [4, 3, 2, 1]
when not using first call find_em([4, 3, 2, 1], 2, 5)
-------------------------------------------------------------
arr=[4, 3, 2, 1], nbr=2, target=5
tot=7
arr.size > nbr, first = 4, rest = [3, 2, 1]
when not using first call find_em([3, 2, 1], 2, 5)
---------------------------------------------------------
arr=[3, 2, 1], nbr=2, target=5
tot=5
arr.size > nbr, first = 3, rest = [2, 1]
when not using first call find_em([2, 1], 2, 5)
-----------------------------------------------------
arr=[2, 1], nbr=2, target=5
tot=3
returning [] because target > tot
returning a=[]
-----------------------------------------------------
find_em returned a = []
nbr > 1
when using first call find_em([2, 1], 1, 2)
-----------------------------------------------------
arr=[2, 1], nbr=1, target=2
tot=2
arr.size > nbr, first = 2, rest = [1]
when not using first call find_em([1], 1, 2)
-------------------------------------------------
arr=[1], nbr=1, target=2
tot=1
returning [] because target > tot
returning a=[]
-------------------------------------------------
find_em returned a = []
nbr = 1
first == target
b = [[2]]
Now a = a+b = [[2]]
returning a=[[2]]
-----------------------------------------------------
find_em returned b = [[2]]
Now a = [[3, 2]]
returning a=[[3, 2]]
---------------------------------------------------------
find_em returned a = [[3, 2]]
nbr > 1
when using first call find_em([3, 2, 1], 1, 1)
---------------------------------------------------------
arr=[3, 2, 1], nbr=1, target=1
tot=3
arr.size > nbr, first = 3, rest = [2, 1]
when not using first call find_em([2, 1], 1, 1)
-----------------------------------------------------
arr=[2, 1], nbr=1, target=1
tot=2
arr.size > nbr, first = 2, rest = [1]
when not using first call find_em([1], 1, 1)
-------------------------------------------------
arr=[1], nbr=1, target=1
tot=1
returning [[1]] because arr.size == nbr
returning a=[[1]]
-------------------------------------------------
find_em returned a = [[1]]
nbr = 1
first != target
returning a=[[1]]
-----------------------------------------------------
find_em returned a = [[1]]
nbr = 1
first != target
returning a=[[1]]
---------------------------------------------------------
find_em returned b = [[1]]
Now a = [[3, 2], [4, 1]]
returning a=[[3, 2], [4, 1]]
-------------------------------------------------------------
find_em returned a = [[3, 2], [4, 1]]
nbr > 1
when using first call find_em([4, 3, 2, 1], 1, 0)
-------------------------------------------------------------
arr=[4, 3, 2, 1], nbr=1, target=0
tot=4
arr.size > nbr, first = 4, rest = [3, 2, 1]
when not using first call find_em([3, 2, 1], 1, 0)
---------------------------------------------------------
arr=[3, 2, 1], nbr=1, target=0
tot=3
arr.size > nbr, first = 3, rest = [2, 1]
when not using first call find_em([2, 1], 1, 0)
-----------------------------------------------------
arr=[2, 1], nbr=1, target=0
tot=2
arr.size > nbr, first = 2, rest = [1]
when not using first call find_em([1], 1, 0)
-------------------------------------------------
arr=[1], nbr=1, target=0
tot=1
returning [] because arr.size == nbr
returning a=[]
-------------------------------------------------
find_em returned a = []
nbr = 1
first != target
returning a=[]
-----------------------------------------------------
find_em returned a = []
nbr = 1
first != target
returning a=[]
---------------------------------------------------------
find_em returned a = []
nbr = 1
first != target
returning a=[]
-------------------------------------------------------------
find_em returned b = []
Now a = [[3, 2], [4, 1]]
returning a=[[3, 2], [4, 1]]
-----------------------------------------------------------------
Note that all values that start in the same column correspond to the same instance of find_all
.