红宝石将数字分成一半或两半



我喜欢将分数拆分为n个位置的数组。

假设我的分数是 11,数组的大小是 12。 然后我喜欢有一些数组填充,例如 11 个 1 或 10 个 1 和 2 个半 (0.5)。最后它的总和应该是 11。

那么可能的分数是:

size = 12
possible_scores = (0..size).step(0.5).to_a

我可以创建一个包含 12 个位置的数组:

scores = Array.new(size) {0}

我可以从以下可能的值中选择一个随机值:

[0, 0.5, 1].sample

如果可能的话,我正在寻找一种有效的方法来检索随机数组,而无需大量状态变量。我已经尝试在一段时间循环中执行此操作:

while score < 0

并使用随机值减少分数值,并跟踪设置的数组位置。但它变成了一段相当混乱的代码。

有什么想法可以解决这个问题吗?谢谢!

编辑:

对于此示例,我想要一个总和为 11 的数组。所以任何一种

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0] 
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.5, 0.5] 
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1] 

或任何组合,总和为 11。

参数和变量

鉴于:

  • tot,期望的总数,0.5的整数或奇数倍;和
  • size00.51的总数tot,要求size >= tot

我们定义三个变量:

  • n0等于零的数量;
  • n0pt5_pairs等于0.5对的数量;和
  • n1等于 1 的数量。

情况 1:tot是一个整数

我们要求:

0 <= n0pt5_pairs <= [tot, size-tot].min

请注意,因为n1 = tot - n0pt5_pairs2 * n0pt5_pairs + n1 = n0pt5_pairs + tot > size如果n0pt5_pairs > size-tot.也就是说,如果0.5对的数量超过size-tot,则0.5和 1 的总数超过size个。

给定满足上述要求的n0pt5_pairs值,确定n0n1

n1 = tot - n0pt5_pairs
n0 = size - 2*n0pt5_pairs - n1
= size - tot - n0pt5_pairs

因此,我们可以随机选择一个随机的三重[n0, 2*n0pt5_pairs, n1]如下:

def random_combo(size, tot)
n0pt5_pairs = rand(1+[tot, size-tot].min)
[size-tot-n0pt5_pairs, 2*n0pt5_pairs, tot-n0pt5_pairs]
end

例如:

arr = random_combo(17, 11)
#=> [3, 6, 8]

这用于生成数组

arr1 = [*[0]*arr[0], *[0.5]*arr[1], *[1]*arr[2]]
#=> [0, 0, 0, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1, 1, 1, 1, 1] 

我们洗牌:

arr1.shuffle
#=> [1, 0, 0.5, 1, 0.5, 0, 1, 1, 0, 1, 1, 1, 0.5, 0.5, 1, 0.5, 0.5]

注意arr1.size #=> 17arr.sum #=> 11

情况 2:tot是 0.5 的倍

数如果

tot = n + 0.5

其中n是一个整数,00.51的每个组合都至少有一个0.5。因此,我们可以计算01的数量,以及超过10.5的数量。为此,我们只需将tot减少0.5(使其等于整数)并size1,使用generate_for_integer来解决这个问题,然后对于该方法返回的每个三元素数组,将0.5的数量增加 1。

def generate(size, tot)
return nil if size.zero?
is_int = (tot == tot.floor)
tot = tot.floor
size -= 1 unless is_int 
n0pt5_pairs = rand(1+[tot, size-tot].min)
[*[0]*(size-tot-n0pt5_pairs), *[0.5]*(2*n0pt5_pairs + (is_int ? 0 : 1)),
*[1]*(tot-n0pt5_pairs)].
shuffle
end

ge = generate(17, 10)
#=> [0, 1, 0, 1, 0.5, 0.5, 0, 0.5, 0.5, 1, 1, 1, 1, 0.5, 0.5, 0.5, 0.5] 
ge.size #=> 17 
ge.sum  #=> 10.0 
go = generate(17, 10.5)
#=> [0.5, 0.5, 0.5, 1, 0, 0.5, 0.5, 1, 1, 0.5, 1, 1, 0.5, 1, 0.5, 0.5, 0] 
go.size #=> 17 
go.sum  #=> 10.5  

Ruby 在这里提供了你需要的一切,不需要编写任何算法代码。Array#repeated_combination是你的朋友:

[0, 0.5, 1].
repeated_combination(12).    # 91 unique variant
to_a.                        # unfortunately it cannot be lazy
shuffle.                     # to randomize array outcome
detect { |a| a.sum == 11 }.
shuffle                      # to randomize numbers inside array
#⇒ [0.5, 0.5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

旁注:通过使用Array#repeated_permutation可以避免两次洗牌(生成的数组数组和生成的数组)的必要性,但这会大大增加内存负载和执行时间。

我喜欢Cary Swoveland的回答,但实际上这可以在不生成一系列解决方案的情况下完成。

让我们考虑几个例子。

给定大小 = 6 和分数 = 3,不洗牌,这些是可能的输出(出于显而易见的原因在左侧编号):

i               ones halves zeroes
0│ 1 1 1 0 0 0    3     0      3
1│ 1 1 ½ ½ 0 0    2     2      2
2│ 1 ½ ½ ½ ½ 0    1     4      1
3│ ½ ½ ½ ½ ½ ½    0     6      0

给定大小 = 6 和分数 = 3.5:

i               ones halves zeroes
0│ 1 1 1 ½ 0 0    3     1      2
1│ 1 1 ½ ½ ½ 0    2     3      1
2│ 1 ½ ½ ½ ½ ½    1     5      0

给定大小 = 11 且分数 = 4.5:

i                         ones halves zeroes
0│ 1 1 1 1 ½ 0 0 0 0 0 0    4     1      6
1│ 1 1 1 ½ ½ ½ 0 0 0 0 0    3     3      5
2│ 1 1 ½ ½ ½ ½ ½ 0 0 0 0    2     5      4
3│ 1 ½ ½ ½ ½ ½ ½ ½ 0 0 0    1     7      3
4│ ½ ½ ½ ½ ½ ½ ½ ½ ½ 0 0    0     9      2

给定大小 = 12 且分数 = 11:

i                            ones halves zeroes
0│ 1 1 1 1 1 1 1 1 1 1 1 0    11     0      1
1│ 1 1 1 1 1 1 1 1 1 1 ½ ½    10     2      0

你能看到模式吗?经过一番挠下巴,我们发现以下事实:

  1. 给定大小分数的可能输出数量由下式给出:

    = 最小(⌊分数⌋,大小− ⌈分数⌉) + 1

  2. 随着增加,数量减少。一的数量由下式给出:

    计数(1) = ⌊分数⌋ −

  3. 随着增加,一半的数量(1/2)增加。半数由下式给出:

    计数(1/2) = 2( + mod(分数, 1))

    换句话说,如果分数有小数部分,则为 2 + 1,否则为 2

  4. 随着增加,零的数量减少,由下式给出:

    count(0) =大小− ⌈分数⌉ −

考虑到这四个事实,我们可以通过选择一个随机的 0 ≤ <来随机生成任何可能的输出:>

= 随机( [0.. ) )

这些事实很容易翻译成 Ruby 代码:

n = [score.floor, size - score.ceil].min + 1
i = rand(n)
num_ones = score.floor - i
num_halves = 2 * (i + score % 1)
num_zeroes = (size - score.floor) - i

现在我们只需要稍微清理一下,把它放在一个函数中,该函数将sizescore作为参数,将num_onesnum_halvesnum_zeroes转换为0s、0.5s和1s的数组,并打乱结果:

def generate(size, score)
init_ones = score.floor
init_zeroes = size - score.ceil
i = rand([init_ones, init_zeroes].min + 1)
num_ones = init_ones - i
num_halves = 2 * (i + score % 1)
num_zeroes = init_zeroes - i
[ *[1]*num_ones, *[0.5]*num_halves, *[0]*num_zeroes ].shuffle
end
generate(6, 3.5)
# => [0.5, 1, 0, 0.5, 0.5, 1]

您可以在 repl.it 上看到结果:https://repl.it/@jrunning/UnpleasantDimpledLegacysystem(请注意,当您在 repl.it 上运行它时,输出显示得非常慢。这只是因为 repl.it 在服务器上执行 Ruby 代码并将结果流式传输回浏览器。

如果我明白这一点,一种可能的选择可能是(蛮力)

size = 12
sum = 11
tmp = Array.new(12){1}
loop do
raise 'not possible' if tmp.sum < sum
tmp[tmp.index(1)] = 0.5 if tmp.index(1)
unless tmp.index(1)
tmp[tmp.index(0.5)] = 0
end
break if tmp.sum == sum
end
tmp #=> [0.5, 0.5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
tmp.sum #=> 11.0

最新更新