How is the reciprocal sum in Ruby?

I have no idea how to call this in correct mathematical terms. Consider a method that takes two digits:

 def num_of_sum(total, group_count)

 end

      

where total

is an integer and group_count

is an integer.

How do I get a "nicely" grouped array of group_count

-length integers that sum up to total

.

My spec would look like this:

describe "number to sum of" do
  it "grabs all numbers" do
    expect(num_of_sum(10, 2)).to eq([5,5])
    expect(num_of_sum(10, 3)).to eq([3,3,4])
    expect(num_of_sum(20, 3)).to eq([6,7,7])
    expect(num_of_sum(100, 3)).to eq([33,33,34])
    expect(num_of_sum(100, 2)).to eq([50,50])
  end
end

      

I tried this, which works:

def num_of_sum(total, in_groups_of)
  result = []
  section_count ||= (total.to_f / in_groups_of.to_f).round

  while(total > 0)
    total -= section_count

    if (total - section_count) < 0 && (total + section_count).even?
      section_count += total
      total -= total
    end

    result << section_count
  end

  result
end

      

But, for example, this specification doesn't work:

 expect(num_of_sum(67,5)).to eq([13,13,13,14,14]) 

      

I need an array to contain numbers that are as close to each other as possible. But the array is limited in length group_count

.


Does anyone know what the mathematical name is for this so that I can search more precisely?

+3


source to share


5 answers


def n_parts(num, groupcount)
  div, mod = num.divmod(groupcount)
  Array.new(groupcount-mod, div) + Array.new(mod, div+1)
end

n_parts(100,3) => [33, 33, 34]

      



Documents Array.new and Fixnum.divmod

+5


source


The mathematical term for this is an integer partition

A more direct approach to this is to notice that if you do an integer division (round down) of the total by the number of groups, then your sum will be short over the total mod_of_groups, so you just need to distribute that sum across an array:



def even_partition(total, number_of_groups)
  quotient, remainder = total.divmod(number_of_groups)
  (number_of_groups-remainder).times.collect {quotient} +
  remainder.times.collect { quotient + 1}    
end

      

+6


source


The naive implementation is this:

Let's take an example (20, 3)

. As a result, you want to get three numbers.

20 / 3 # => 6

      

This is your "base" value. Create an array of three gears [6, 6, 6]

. This will give you 18. Now you need to distribute the remaining 2 as evenly as possible. For example, list the elements of an array and increment each one by one until you have a value to propagate. Result [7, 7, 6]

. Good enough I think.

Possible (working) implementation:

def breakdown(total, group_count)
  avg_value, extra = total.divmod(group_count)

  result = Array.new(group_count, avg_value)
  extra.times do |i|
    result[i] += 1
  end

  result
end

breakdown(10, 2) == [5, 5] # => true
breakdown(10, 3) == [4, 3, 3] # => true
breakdown(20, 3) # => [7, 7, 6]

      

+5


source


I don't know what it's called, but here's the solution:

def num_of_sum sum, count
  result = [i = sum / count] * count # prepare an array e.g. [3,3,3] for 10,3
  result[sum - i * count..-1] + # these should be left intact
    result[0...sum - i * count].map { |i| i + 1 } # these are ++’ed
end

      

Hope it helps.

0


source


Another way:

def floors_then_ceils(n, groups)
  floor, ceils = n.divmod(groups)
  groups.times.map { |i| (i < groups-ceils) ? floor : floor + 1 }
end

floors_then_ceils(10, 3)
  #=> [3, 3, 4] 
floors_then_ceils(9, 3)
  #=> [3, 3, 3] 

      

Alternatively groups.times.map...

can be replaced with:

Array.new(groups-ceils, floor).concat(Array.new(ceils, floor+1))

      

0


source







All Articles