如何将一个数字随机分成几个数字?如何在红宝石中随机分配一个数字?
例如:我有一个数字30,我想它随机分成几个数字,每个数字的大小为3-10之间,且每个数字的大小是互不相同
结果可能是这样的:[5,7,9,6,3],[9,10,3,8],...等
我试过了,但是我解决不了,请给我帮助。
如何将一个数字随机分成几个数字?如何在红宝石中随机分配一个数字?
例如:我有一个数字30,我想它随机分成几个数字,每个数字的大小为3-10之间,且每个数字的大小是互不相同
结果可能是这样的:[5,7,9,6,3],[9,10,3,8],...等
我试过了,但是我解决不了,请给我帮助。
非常不错的拼图。我会去:
class Fixnum
def random_split(set = nil, repeats = false)
set ||= 1..self
set = [*set]
return if set.empty? || set.min > self || set.inject(0, :+) < self
tried_numbers = []
while (not_tried = (set - tried_numbers).select {|n| n <= self }).any?
tried_numbers << number = not_tried.sample
return [number] if number == self
new_set = set.dup
new_set.delete_at(new_set.index(number)) unless repeats
randomized_rest = (self-number).random_split(new_set, repeats)
return [number] + randomized_rest if randomized_rest
end
end
end
30.random_split(3..10)
一般来说,上面的代码涵盖了很多情况。你可以在没有任何参数的情况下执行它,它会假定它是从1到给定数字中选择数字,并且结果集不应该包含任何重复。您可以选择传递给定数量的设置。如果您通过[1,2,3,4,4,4],则需要注意4不会重复3次以上。如果第二个参数设置为true,它将允许设置元素在结果中出现两次或更多次。
不是在世界上最好的算法中(它的一些试验和错误),但嘿,CPU周期很便宜如今...这应该对每一个号码的工作:
def split_this_number_into_several_numbers_randomly(a_number, min_number_to_start_from)
random_numbers = [0]
until (random_numbers.inject(&:+) == a_number)
random_numbers << rand(min_number_to_start_from..a_number/3) # replace 30 here, I assumed you wanted up to 1/3 of the original number
if (r = random_numbers.detect { |x| random_numbers.count(x) > 1}) then random_numbers.delete(r) end # so we have all unique numbers
random_numbers.pop if random_numbers.inject(&:+) >= a_number - min_number_to_start_from && random_numbers.inject(&:+) != a_number
end
random_numbers.delete_if{ |x| x == 0 }
end
,当然,有些代码对其进行测试:
all_true = true
1000.times do
arr = split_this_number_into_several_numbers_randomly(30, 3)
all_true == false unless arr.inject(&:+) == 30
all_true == false unless arr.size == arr.uniq.size
end
p all_true #=> true
的OP已确认关于该问题的是随机选择是,以反映下面的过程的注释:“选择3和10之间不同的数目的所有组合([3],[3, 4],[3,5,6,8,9],.. [9,10],[10]),抛出所有不加总和为30的组合,并选择o那些随机离开的人“。以下是实现这一点的直接方式。可以提高效率,但这将是很多工作。
代码
def arrays(sum, range)
largest_sum = (range.first+range.last)*(range.last-range.first+1)/2
(raise ArgumentError,
"largest sum for range = #{largest_sum}") if sum > largest_sum
avail = [*range]
b = -(2*range.last + 1.0)
c = 8.0*sum
min_nbr = ((-b - (b*b - c)**0.5)/2).ceil.to_i
max_nbr = ((-1.0 + (1.0 + c)**0.5)/2).to_i
(min_nbr..max_nbr).each_with_object([]) { |n, a|
a.concat(avail.combination(n).select { |c| c.inject(:+) == sum }) }
end
min_nbr
注和max_nbr
采用二次公式,以确定其可以总和为sum
不同号码的数目的范围内。
例子
sum = 30
range = (3..10)
arr = arrays(sum, range) # all combinations that sum to 30
#=> [[3, 8, 9, 10], [4, 7, 9, 10], [5, 6, 9, 10], [5, 7, 8, 10],
# [6, 7, 8, 9],
# [3, 4, 5, 8, 10], [3, 4, 6, 7, 10], [3, 4, 6, 8, 9],
# [3, 5, 6, 7, 9], [4, 5, 6, 7, 8]]
(Solution time: well under 1 sec.)
10.times { p arr[rand(arr.size)] } # 10 random selections
#=> [3, 4, 6, 8, 9]
# [3, 4, 6, 8, 9]
# [5, 7, 8, 10]
# [4, 5, 6, 7, 8]
# [3, 4, 5, 8, 10]
# [6, 7, 8, 9]
# [3, 4, 5, 8, 10]
# [4, 5, 6, 7, 8]
# [6, 7, 8, 9]
# [3, 4, 6, 7, 10]
sum = 60
range = (3..10)
arr = arrays(sum, range)
#=> in `arrays': largest sum for range = 52 (ArgumentError)
两个更多...
sum = 60
range = (3..20)
arr = arrays(sum, range) # all combinations that sum to 60
arr.size
#=> 1092
(Solution time: about 1 sec.)
10.times { p arr[rand(arr_size)] } # 10 random selections
#=> [12, 14, 15, 19]
# [3, 4, 6, 7, 11, 13, 16]
# [3, 6, 7, 9, 15, 20]
# [3, 8, 14, 17, 18]
# [3, 4, 5, 7, 10, 13, 18]
# [3, 5, 6, 7, 11, 13, 15]
# [5, 6, 7, 8, 14, 20]
# [4, 5, 9, 11, 15, 16]
# [4, 5, 8, 13, 14, 16]
# [3, 4, 5, 12, 16, 20]
sum = 100
range = (3..30)
arr = arrays(sum, range) # all combinations that sum to 100
arr.size
#=> 54380
(Solution time: 3 or 4 minutes)
10.times { p arr[rand(arr_size)] } # 10 random selections
#=> [3, 4, 6, 9, 11, 12, 15, 17, 23]
# [4, 5, 6, 7, 9, 13, 14, 17, 25]
# [4, 5, 6, 7, 11, 17, 21, 29]
# [9, 10, 12, 13, 17, 19, 20]
# [6, 9, 10, 23, 25, 27]
# [3, 4, 5, 6, 7, 8, 9, 14, 15, 29]
# [3, 4, 5, 6, 7, 8, 9, 15, 17, 26]
# [3, 4, 5, 6, 7, 8, 17, 22, 28]
# [3, 5, 6, 7, 9, 12, 13, 15, 30]
# [6, 8, 9, 10, 13, 15, 18, 21]
分割数称为integer partition。下面是基于Marc-André Lafortune's recursive algorithm一个解决方案:
def expand(n, max = n)
return [[]] if n == 0
[max, n].min.downto(1).flat_map do |i|
expand(n-i, i).map{|rest| [i, *rest]}
end
end
expand(30).select { |a| a.size >= 3 && a.size <= 10 }.sample(5)
#=> [[15, 3, 3, 3, 2, 2, 1, 1],
# [9, 5, 4, 3, 2, 2, 2, 1, 1, 1],
# [13, 10, 4, 2, 1],
# [8, 8, 7, 2, 2, 1, 1, 1],
# [8, 6, 4, 3, 3, 2, 1, 1, 1, 1]]
注意可能的分区的数量变得非常大:30 5604个分区,100具有190569292个分区和1000具有2.4 × 1031分区。
其他答案的作者 - 包括我 - 解释问题为随机选择''3'之间30'和'10'那笔一组不同的号码之一。 (你可以用替换'a.size> = 3..''a.min> = 3 && a.max <= 10&a.reduce(:+)== 30',但是这不是非常有效的,因为可能的设置大小的范围可以预先确定,减少用'expand'生成的组合的数量。)也许OP会澄清。 – 2014-09-27 21:24:16
“随机分成几个数字”是模糊的。你必须准确解释你的意思是“随机”,但我希望你会有一些困难。 – 2014-09-26 01:17:03
@Cary Swoveland,随机,它意味着号码的数字是随机的 – user3673267 2014-09-26 01:31:22
我认为歧义是什么意思是“分裂一个数字”。你是否要求一个随机的数字加起来的原始数字的唯一加号? – 2014-09-26 01:31:54