我喜欢将得分分为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的任何组合。
答案 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.5
,1
和tot
的总数,{ 1}}。
我们定义了三个变量:
size >= tot
等于零; n0
等于n0pt5_pairs
的对数;和0.5
等于1。 情况1:n1
是整数
我们要求:
tot
请注意,因为0 <= n0pt5_pairs <= [tot, size-tot].min
,所以n1 = tot - n0pt5_pairs
为2 * n0pt5_pairs + n1 = n0pt5_pairs + tot > size
。也就是说,如果n0pt5_pairs > size-tot
对的数量超过0.5
,则size
和1的总数超过0.5
。
给出满足上述要求的size-tot
值,确定n0pt5_pairs
和n0
:
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
是整数,n
,0
和0.5
的每个组合将至少有一个1
。因此,我们可以计算0.5
和0
的数量,以及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
可以看到图案吗?经过一小会挠后,我们发现了以下事实:
给定的 size 和分数的可能输出数量由下式给出:
= min(⌊分数⌋,大小-⌈分数⌉)+ 1
随着数量的增加,数量减少。的数量由下式给出:
count(1)=⌊分数⌋-
随着增加,一半的数量(1/2)增加。一半的数目由下式给出:
count(1/2)= 2(+ mod(分数,1))
换句话说,如果分数有小数部分,则为2 +1;否则,为2。
随着增加,零的数目减少,由下式给出:
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
现在,我们只需要对其进行一些清理,然后将其放入以size
和score
作为参数,将num_ones
,num_halves
和{将{1}}分成num_zeroes
,0
和0.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