将数字拆分为相等的部分或最接近

时间:2016-04-05 00:03:54

标签: ruby-on-rails ruby math

我有一个随机数,我想把它分成几个部分(加数),条件是一个部分不能超过20个,部分必须尽可能彼此接近。

例如,如果我的随机数为41,则加数应为14,14,13。 如果随机数为60,则加数应为20,20,20。 如果随机数为21,则加数应为11和10 等等。

我的代码是在Ruby(Rails)中,所以我非常感谢一个能够在Ruby中实现这一功能的答案,尽管也欢迎使用伪代码或其他编程语言。

这是我在数组中找到的,但我真的需要用数字做这件事:“Splitting an array into equal parts in ruby

3 个答案:

答案 0 :(得分:12)

你可以使用这款酷炫的单线眼镜。它应该可以除以你想要的任何数量:

def split_into n, p
    [n/p + 1] * (n%p) + [n/p] * (p - n%p)
end

print (split_into 32, 3) # => [11, 11, 10]

它基本上将数字分成相等的部分,并将余数除以前面的数字(每个为1)。那个以及你可以将数组相乘并添加在一起的事实,如下所示:

[1] * 3     # => [1, 1, 1]
[1] + [2]   # => [1, 2]

编辑:考虑到每件作品应小于20的规则,您可以按照@Cary Swoveland的建议计算理想的件数:

p = (n / 20.0).ceil

答案 1 :(得分:4)

<强>代码

n为要分割的给定数字,并m每个组件的最大值。

def doit(n,m)
  nbr_components = (n/(m.to_f)).ceil
  smaller, nbr_larger = n.divmod(nbr_components)
  { smaller=>nbr_components-nbr_larger, smaller+1=>nbr_larger }.
    delete_if { |_,v| v.zero? }
end

Fixnum#divmod是一种经常被忽视的方法,有很多用途。

<强>实施例

(1..300).to_a.sample(15).sort.each { |n| puts "doit(#{n},20) = #{doit(n,20)}" }
doit(2,20)   = {2=>1}
doit(7,20)   = {7=>1}
doit(13,20)  = {13=>1}
doit(19,20)  = {19=>1}
doit(32,20)  = {16=>2}
doit(36,20)  = {18=>2}
doit(47,20)  = {15=>1, 16=>2}
doit(61,20)  = {15=>3, 16=>1}
doit(77,20)  = {19=>3, 20=>1}
doit(146,20) = {18=>6, 19=>2}
doit(149,20) = {18=>3, 19=>5}
doit(160,20) = {20=>8}
doit(199,20) = {19=>1, 20=>9}
doit(253,20) = {19=>7, 20=>6}
doit(275,20) = {19=>5, 20=>9}

doit(11_347_155, 20)
  #=> {19=>5, 20=>567353}
5*19 + 567353*20
  #=> 11347155

<强>解释

假设:

n = 77
m = 20

步骤如下:

nbr_components = (n/(m.to_f)).ceil
  #=> (77/20.0).ceil
  #=> 3.85.ceil
  #=> 4 
smaller, nbr_larger = n.divmod(nbr_components)
  #=> 77.divmod(4) 
  #=> [19, 1] 
smaller
  #=> 19
nbr_larger
  #=> 1
h = { smaller=>nbr_components-nbr_larger, smaller+1=>nbr_larger }
  #=> {19=>3, 20=>1}
h.delete_if { |_,v| v.zero? }
  #=> {19=>3, 20=>1}

答案 2 :(得分:3)

没有内置功能来执行此操作。

我可以为您写出解决方案,但一般来说,您应该在StackOverflow问题中包含您自己的尝试。这种类型的问题似乎是练习学习ruby语言的基础知识。如果你不确定从哪里开始,我建议你阅读一本Ruby教科书。

def split_number_into_three(number) # i.e. number=32
  base = Array.new(3, (number / 3)) # i.e. base=[10,10,10]
  remainder = number % 3            # i.e. remainer=2
  return base if remainder.zero?
  while remainder > 0
    base.each_with_index do |num, idx|
      if remainder > 0
        base[idx] += 1
        remainder -= 1
      end
    end
  end
  base
end

运行此代码:

irb(main):035:0> split_number_into_three(32)
=> [11, 11, 10]