红宝石分割数为一或半

时间:2019-05-01 12:35:49

标签: ruby

我喜欢将得分分为n个位置。

假设我的分数是11,数组的大小是12。 然后我喜欢用11个或10个和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循环中这样做:

while score < 0

并使用随机值减小score的值,并跟踪设置的数组位置。但这变成了很乱的代码。

任何想法如何解决这个问题?谢谢!

编辑:

在此示例中,我想要一个总计为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的任何组合。

4 个答案:

答案 0 :(得分:3)

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两次洗牌(生成的数组和生成的数组)的必要性,但这会大大增加内存负载和执行时间。

答案 1 :(得分:3)

参数和变量

给出:

  • tot,期望的总数,0.5的整数或奇数倍;和
  • size,即0总数中0.51tot的总数,{ 1}}。

我们定义了三个变量:

  • size >= tot等于零;
  • n0等于n0pt5_pairs的对数;和
  • 0.5等于1。

情况1:n1是整数

我们要求:

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和1的总数超过0.5

给出满足上述要求的size-tot值,确定n0pt5_pairsn0

n1

因此,我们可以如下随机选择一个三元组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 #=> 17

情况2:arr.sum #=> 11是0.5的倍数

如果

tot

其中tot = n + 0.5 是整数,n00.5的每个组合将至少有一个1。因此,我们可以计算0.50的数量,以及1数量超过的数量。为此,我们只需将0.5减少tot(使其等于整数),然后将0.5减少1,使用size解决该问题,然后每三个该方法返回的元素数组将generate_for_integer的数量增加一。

0.5

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

答案 2 :(得分:3)

我喜欢卡里·斯沃夫兰(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. 给定的 size 分数的可能输出数量由下式给出:

      

    = min(⌊分数⌋,大小-⌈分数⌉)+ 1

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

      

    count(1)=⌊分数⌋-

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

      

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

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

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

      

    count(0)= 大小-⌈得分⌉-

考虑到这四个事实,我们可以通过选择0≤<:

的随机数来随机生成任何可能的输出
  

= random([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_halves和{将{1}}分成num_zeroes00.5 s的数组,然后将结果混洗:

1

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

答案 3 :(得分:2)

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

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