johnny
johnny

Reputation: 11

Min, Max, Average, and Median of All Possible Sums (Ruby)

I've been using a piece of Ruby code that I found here.

Here's the code:

a = [1, 4, 7, 13]

def add(ary, idx, sum)
    (idx...ary.length).each do |i|
        add(ary, i+1, sum + ary[i])
    end
    puts sum
end
add(a, 0, 0)

Thing is, I don't need it to spit out the results of adding all the sums. I need the min, max, median, and average of the sums.

How do I modify this code in order to get them? I'm a total beginner at Ruby. I've been using this code, and then transferring the results to Excel to get the values I want. But it feels like my methods could be more efficient.

Thank you for your help.

EDIT: Expected results - Currently the code spits this out on my screen:

25
12
18
5
21
8
14
1
24
11
17
4
20
7
13
0

I want it to spit out the min, average, median, and max instead:

0
12.5
12.5
25

Upvotes: 1

Views: 1852

Answers (4)

Cary Swoveland
Cary Swoveland

Reputation: 110725

Min and Max

The min and max are easy.

def min_and_max_of_sums a
  return [nil, nil] if a.empty?      
  negs, nonnegs = a.partition { |n| n < 0 }
  [negs.any? ? negs.sum : nonnegs.min, nonnegs.any? ? nonnegs.sum : negs.max]
end

min_and_max_of_sums [1, 4, -5, 7, -8, 13]
  #=> [-13, 25]
min_and_max_of_sums [1, 2, 3]
  #=> [1, 6]
min_and_max_of_sums [-1, -2, -3]
  #=> [-6, -1]
min_and_max_of_sums []
  #=> [nil, nil]

Mean

Now consider the calculation of the mean.

If n is the size of the array a, there are 2n combinations of elements of a that contain between 0 and n elements.1 Moreover, there is a 1-1 mapping between each of those combinations and an n-vector of zeros and ones, where the ith element of the n-vector equals 1 if and only if the element ai is included in the combination. Note that there are 2n such n-vectors, one-half containing a 1 in the ith position. This means that one-half of the combinations contain the element ai. As i is arbitrary, it follows that each element of a appears in one-half of the combinations.

The mean of the sums of all elements of all combinations equals T/2n, where T is the sum of the sums of the elements of each combination. Each element ai appears in 2n/2 combinations, so its contribution to T equals (in Ruby terms)

a[i] * 2**(n)/2

As this hold for every element of a, the mean equals

a.sum * (2**(n)/2)/2**(n)
  => a.sum/2

Here's an example. For the array

a = [1, 4, 8]

the mean of the sums would be

a.sum/2
  #=> 13/2 => 6.5

If we were to calculate the mean by its definition we would perform the following calculation (and of course get the same return value).

(0 + (1) + (4) + (8) + (1+4) + (1+8) + (4+8) + (1=4+8))/2**3
  #=> (4*1 + 4*4 + 4*8)/8
  #=> (1 + 4 + 8)/2
  #=> 6.5

I will leave the calculating of the median to others.

1 Search for "Sums of the binomial coefficients" here.

Upvotes: 0

A. Bronca
A. Bronca

Reputation: 11

a = [1, 4, 7, 13]

def all_sums(array)
    combination_lengths = (0..array.length)
    all_combinations = combination_lengths.flat_map do |c|
      array.combination(c).to_a
    end
    all_combinations.map(&:sum)
end

def print_min_max_avg_med(array)
  puts array.min
  puts array.max
  puts array.sum.to_f / array.length
  sorted_arr = array.sort
  puts sorted_arr[(array.length - 1) / 2] + sorted_arr[array.length / 2] / 2.0
end

print_min_max_avg_med(all_sums(a))

Upvotes: 1

johnny
johnny

Reputation: 11

Alright, after seeing the examples from Pochmann and Bronca, I put this together after googling for a better way to get the median.

a = [1, 4, 7, 13]

def all_sums(array)
    combination_lengths = (0..array.length)
    all_combinations = combination_lengths.flat_map do |c|
      array.combination(c).to_a
    end
    all_combinations.map(&:sum)
end

def median(array)
  sorted = array.sort
  len = sorted.length
  (sorted[(len - 1) / 2] + sorted[len / 2]) / 2.0
end

def print_min_max_avg_med(array)
  puts array.min
  puts array.empty? ? 0 : array.sum.to_f / array.length
  puts median(array)
  puts array.max
end

print_min_max_avg_med(all_sums(a))

I've run a few tests, and it seems to work for both odd and even arrays. Hope this is useful to the future somebody else stuck in my present position.

Thank you everyone who helped.

Upvotes: 0

SteveTurczyn
SteveTurczyn

Reputation: 36860

Ok, instead of outputting the values we can store them in an arrary and use that array for the values you need. (edited after chewing out by Stefan Pochmann)

 a = [1, 4, 7, 13]

def add(ary, idx, sum, results = nil)
  unless results
    results = []
    first_run = true
  end
  (idx...ary.length).each do |i|
      add(ary, i+1, sum + ary[i], results)
  end
  results << sum

  if first_run
    puts results.min
    puts results.inject(&:+).to_f / results.size
    puts (results.sort[((results.size - 1) / 2)] + results.sort[(results.size / 2)]) / 2.0
    puts results.max
  end
end
add(a, 0, 0)

Upvotes: 0

Related Questions