简体   繁体   中英

Show only the combinations of two permutated arrays that have a sum less than or equal to target number

I have two arrays: teams = [1,2,3] and drivers = [4,5,6] . Using permutations I have managed to show all combinations of the two arrays, but have managed to define what number of values I'd like to use from each array. So from 'Teams' I have used 1 value and 'Drivers' I have used two. I would like to only show the combinations where the sum is less than or equal to 10 and remove any duplicates.

    teams = [1,2,3]
    drivers = [4,5,6]
    team = teams.permutation(1).to_a
    driver = drivers.permutation(2).to_a
    array = team.product(driver)
    target = 11

This is successfully outputting all combinations of the two arrays using 1 number from teams and 2 from drivers as follows:

[[1], [4, 5]], [[1], [4, 6]], [[1], [5, 4]], [[1], [5, 6]], [[1], [6, 4]], [[1], [6, 5]], [[2], [4, 5]], etc...

To only show values less than or equal to 10 my expected outcome would be: [[1], [4, 5]], [[1], [5, 4]],

and then no duplicates would leave me with just: [[1], [4, 5]]

I have tried adding the below line of code but am getting an undefined method `<=' error:

@array = array[0].product(*array[1..-1]).select { |a| a.reduce(:+) <= target }

I have also tried this with no luck:

result = array.combination(1).select{|combi| combi.sum <= target}

@array = result

I'm guessing it's something to do with the permutation?

teams = [1,2,3]
drivers = [2,5,4,5,6,4,5,7]
max_driver_sum = 10

I have assumed that drivers can contain duplicate elements (as in my example), but I will explain at the end how the calculations would simplify if there are no duplicates.


As a first step let's partition drivers between values that are repeated and those that are not.

counts = drivers.tally
  #=> {2=>1, 5=>3, 4=>2, 6=>1, 7=>1}

dup_drivers, uniq_drivers = counts.partition { |_d,n| n > 1 }
                                  .map { |arr| arr.map(&:first) }​
  #=> [[5, 4], [2, 6, 7]]

Therefore,

dup_drivers
  #=> [5, 4]

uniq_drivers    
  #=> [2, 6, 7]

See Enumerable#tally and Enumerable#partition .

Here,

counts.partition { |_d,n| n > 1 } 
 #=> [[[5, 3], [4, 2]], [[2, 1], [6, 1], [7, 1]]]

First compute the unique combinations in which the two drivers are equal:

dup_combos = teams.each_with_object([]) do |t,arr|
  max_driver = (max_driver_sum - t)/2
  dup_drivers.each do |d|
    arr << [[t],[d,d]] if d <= max_driver
  end
end
  #=> [[[1], [4, 4]], [[2], [4, 4]]]

Next, compute the unique combinations in which the two drivers are not equal:

all_uniq = uniq_drivers + dup_drivers
  #=> [2, 6, 7, 5, 4]
all_uniq_combos = all_uniq.combination(2).to_a
  #=> [[2, 6], [2, 7], [2, 5], [2, 4], [6, 7], [6, 5],
  #    [6, 4], [7, 5], [7, 4], [5, 4]]
uniq_combos = teams.each_with_object([]) do |t,arr|
  adj_driver_sum = max_driver_sum - t
  all_uniq_combos.each do |combo|
    arr << [[t],combo] if combo.sum <= adj_driver_sum
  end
end
  #=> [[[1], [2, 6]], [[1], [2, 7]], [[1], [2, 5]], [[1], [2, 4]],
  #   [[1], [5, 4]], [[2], [2, 6]], [[2], [2, 5]], [[2], [2, 4]],
  #   [[3], [2, 5]], [[3], [2, 4]]]

SeeArray#combination .


The final step is to combine the two groups of combinations:

a1 = dup_combos + uniq_combos
  #=> [[[1], [4, 4]], [[2], [4, 4]], [[1], [2, 6]], [[1], [2, 7]],
  #    [[1], [2, 5]], [[1], [2, 4]], [[1], [5, 4]], [[2], [2, 6]],
  #    [[2], [2, 5]], [[2], [2, 4]], [[3], [2, 5]], [[3], [2, 4]]]

Sorted, this result is as follows.

a1.sort
  #=> [[[1], [2, 4]], [[1], [2, 5]], [[1], [2, 6]], [[1], [2, 7]],
  #    [[1], [4, 4]], [[1], [5, 4]],
  #    [[2], [2, 4]], [[2], [2, 5]], [[2], [2, 6]], [[2], [4, 4]],
  #    [[3], [2, 4]], [[3], [2, 5]]]

Notice that Array#uniq was not used in the foregoing. If desired, one could of course substitute out some of the variables above.


If drivers contains no duplicates the desired array is given by uniq_combos where all_uniq is replaced by drivers in the calculation of all_uniq_combos . If, for example,

teams = [1,2,3]
drivers = [2,5,4,6,7]
max_driver_sum = 10

then

all_uniq_combos = drivers.combination(2).to_a
  #=> [[2, 5], [2, 4], [2, 6], [2, 7], [5, 4], [5, 6],
  #    [5, 7], [4, 6], [4, 7], [6, 7]]

combos = teams.each_with_object([]) do |t,arr|
  adj_driver_sum = max_driver_sum - t
  all_uniq_combos.each do |combo|
    arr << [[t],combo] if combo.sum <= adj_driver_sum
  end
end ​
  #=> [[[1], [2, 5]], [[1], [2, 4]], [[1], [2, 6]], [[1], [2, 7]],
  #    [[1], [5, 4]], [[2], [2, 5]], [[2], [2, 4]], [[2], [2, 6]],
  #    [[3], [2, 5]], [[3], [2, 4]]]

combos.sort
  #=> [[[1], [2, 4]], [[1], [2, 5]], [[1], [2, 6]], [[1], [2, 7]],
  #    [[1], [5, 4]],
  #    [[2], [2, 4]], [[2], [2, 5]], [[2], [2, 6]],
  #    [[3], [2, 4]], [[3], [2, 5]]]

Here's an approach

teams = [1, 2, 3]
drivers = [2, 6, 5, 4]
team = teams.permutation(1).to_a
driver = drivers.permutation(2).to_a
array = team.product(driver)

target = 10

res = array.select {|i| i.map(&:sum).sum <= target}.compact
==> [[[1], [2, 6]], [[1], [2, 5]], [[1], [2, 4]], [[1], [6, 2]],
     [[1], [5, 2]], [[1], [5, 4]], [[1], [4, 2]], [[1], [4, 5]],
     [[2], [2, 6]], [[2], [2, 5]], [[2], [2, 4]], [[2], [6, 2]],
     [[2], [5, 2]], [[2], [4, 2]], [[3], [2, 5]], [[3], [2, 4]],
     [[3], [5, 2]], [[3], [4, 2]]]

Getting the unique items (modified to also work for values of teams > drivers)

t1 = res.map {|i| i[0]}
d2 = res.map {|i| i[1].flatten.sort}

t1.zip(d2).uniq
==> [[[1], [2, 6]], [[1], [2, 5]], [[1], [2, 4]], [[1], [4, 5]],
     [[2], [2, 6]], [[2], [2, 5]], [[2], [2, 4]], [[3], [2, 5]],
     [[3], [2, 4]]]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM