仅使用2或3从0到达N的方法的数量?

时间:2015-06-05 14:18:32

标签: algorithm fibonacci

我正在解决这个问题,我们需要从X = 0到X = N.我们一次只能采取2或3步。

对于2的每一步,我们的概率为0.2,对于每步3,我们的概率为0.8。我们如何找到达到N的总概率。

  

e.g。达到5,

2+3 with probability =0.2 * 0.8=0.16

3+2 with probability =0.8 * 0.2=0.16 total = 0.32.

我最初的想法:

通过简单的Fibonacci序列可以找到多种方法。 F(N)= F(N-3)+ F(N-2); 但是我们如何记住数字,以便我们可以将它们相乘以找出概率?

5 个答案:

答案 0 :(得分:6)

这可以使用Dynamic programming来解决。

让我们在F(N)

时仅使用2和3来调用函数0 =达到the starting number is N的概率
F(N) = 0.2*F(N-2) + 0.3*F(N-3)

基本情况:

F(0) = 1 and F(k)= 0 where k< 0

所以DP代码就像那样:

F[0] = 1;
for(int i = 1;i<=N;i++){
     if(i>=3)
         F[i] = 0.2*F[i-2] + 0.8*F[i-3];
     else if(i>=2)
         F[i] = 0.2*F[i-2];
     else
         F[i] = 0;
}
return F[N];

此算法将在O(N)

中运行

答案 1 :(得分:2)

关于这个解决方案的一些澄清:我假设从2s和3s生成数字的唯一允许操作是加法(你的定义也允许减法)并且输入数总是有效的(2 <=输入)。定义:唯一的数字行意味着:在另一个顺序中没有其他具有相同数量的3和2的行在范围内。

我们可以将问题简化为多个小问题:

问题A:查找可以总计给定数字的所有数字序列。 (仅限唯一的数字行)
首先找到构建给定数字所需的最小3个数,这只是input % 2。可以用这种方式计算可用于构建输入的最大3个数:

int max_3 = (int) (input / 3);
if(input - max_3 == 1)
    --max_3;

现在,总计input的所有数字序列必须保持在input % 2max_3 3之间。可以从给定数量的3s轻松计算出2s。

问题B:计算给定列表的概率及其排列结果 对于每个唯一的数字行,我们可以轻松地推导出所有排列。由于它们由相同的数字组成,因此它们具有相同的出现可能性并产生相同的总和。可以从行0.8 ^ number_of_3s * 0.2 ^ number_of_2s轻松计算出可能性。下一步是计算不同的permuatation的数量。计算具有特定数量的2和3的所有不同集合可以这样做:计算集合中所有可能的2s分布:(number_of_2s + number_of_3s)! / (number_of_3s! * numer_of_2s!)。基本上只是可​​能的不同排列的数量。

现在从理论到实践
由于数学已经给出,其余部分非常简单:

define prob:
    input: int num
    output: double

    double result = 0.0

    int min_3s = (num % 2)
    int max_3s = (int) (num / 3)
    if(num - max_3 == 1)
        --max_3

    for int c3s in [min_3s , max_3s]
        int c2s = (num - (c3s * 3)) / 2

        double p = 0.8 ^ c3s * 0.2 * c2s
        p *= (c3s + c2s)! / (c3s! * c2s!)

        result += p

    return result

答案 2 :(得分:2)

不是跳入编程,而是使用数学。

设p(n)是到达n步之外的位置的概率。

基本情况:

p(0)=1
p(1)=0
p(2)=0.2

Linear recurrence relation

p(n+3)=0.2 p(n+1) + 0.8 p(n)

您可以通过查找线性递归关系的指数解来以封闭形式解决此问题。

c ^ 3 = 0.2 c + 0.8

c = 1,( - 5 + - sqrt(55)i)/ 10

虽然这是立方体,但c = 1将始终是此类问题的解决方案,因为存在恒定的非零解。

因为根是不同的,所有解都是a1(1)^ n + a2(( - 5 + sqrt(55)i)/ 10)^ n + a3(( - 5-sqrt(55)) ⅰ)/ 10)^ N。您可以使用初始条件求解a1,a2和a3:

a1=5/14 
a2=(99-sqrt(55)i)/308
a3=(99+sqrt(55)i)/308

这为p(n)提供了一个非递归公式:

p(n)=5/14+(99-sqrt(55)i)/308((-5+sqrt(55)i)/10)^n+(99+sqrt(55)i)/308((-5-sqrt(55)i)/10)^n

非递归公式的一个不错的属性是你可以读取5/14的渐近值,但这也很清楚,因为跳跃的平均值是2(1/5)+ 3(4/5) )= 14/5,你几乎肯定会击中一组密度为1 /(14/5)的整数。你可以使用其他根的大小,2 / sqrt(5)~0.894,看看概率接近渐近的速度有多快。

5/14 - (|a2|+|a3|) 0.894^n < p(n) < 5/14 + (|a2|+|a3|) 0.894^n
|5/14 - p(n)| < (|a2|+|a3|) 0.894^n

答案 3 :(得分:1)

f(n,p)= f(n-3,p * .8)+ f(n -2,p * .2)

从1开始。

如果n = 0则返回p,如果n <0则返回0.

答案 4 :(得分:1)

不是使用(非常低效)递归算法,而是从头开始计算您可以通过多少种方式达到后续步骤,即使用'dynamic programming'。通过这种方式,您可以轻松地计算概率,并且还具有仅 O(n)的复杂度,以计算直到步骤 n 的所有内容。

对于每个步骤,记住达到该步骤的可能方式,如果有的话(无论如何),以及达到该步骤的可能性。对于第0步(开始),这是(1, 1.0)

steps = [(1, 1.0)]

现在,对于每个连续步骤n,获取先前计算的可能方式poss和概率prob以达到步骤 n-2 n-3 (或(0, 0.0)n < 2分别为n < 3),将这些添加到组合的可能性和概率以达到新步骤,并将它们添加到列表。

for n in range(1, 10):
    poss2, prob2 = steps[n-2] if n >= 2 else (0, 0.0)
    poss3, prob3 = steps[n-3] if n >= 3 else (0, 0.0)
    steps.append( (poss2 + poss3, prob2 * 0.2 + prob3 * 0.8) )

现在您可以从该列表中获取数字:

>>> for n, (poss, prob) in enumerate(steps):
...     print "%s\t%s\t%s" % (n, poss, prob)
0   1   1.0
1   0   0.0
2   1   0.2
3   1   0.8
4   1   0.04
5   2   0.32        <-- 2 ways to get to 5 with combined prob. of 0.32
6   2   0.648
7   3   0.096
8   4   0.3856
9   5   0.5376

(代码在Python中)

请注意,这将使两者达到某个步骤的可能方式的数量(例如,“前2,然后3”或“前3,然后2”为5),一次性达到该步骤的概率。当然,如果你只需要 概率,你可以使用单个数字而不是元组。