n steps with 1, 2 or 3 steps taken. How many ways to get to the top?

前端 未结 13 762
时光说笑
时光说笑 2020-12-08 08:12

If we have n steps and we can go up 1 or 2 steps at a time, there is a Fibonacci relation between the number of steps and the ways to climb them. IF and ONLY if we do not co

相关标签:
13条回答
  • 2020-12-08 08:50

    My solution is in java. I decided to solve this bottom up.

    I start off with having an empty array of current paths [] Each step i will add a all possible step sizes {1,2,3}

    First step [] --> [[1],[2],[3]]
    Second step [[1],[2],[3]] --> [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1][3,2],[3,3]]



    Iteration 0: []
    Iteration 1: [ [1], [2] , [3]]
    Iteration 2: [ [1,1], [1,2], [1,3], [2,1], [2,2], [2,3], [3,1], [3,2], [3,3]]
    Iteration 3 [ [1,1,1], [1,1,2], [1,1,3] ....]

    The sequence lengths are as follows 1 2 3 5 8 13 21

    My step function is called build

    public class App {
    
    
    
    public static boolean isClimedTooHigh(List<Integer> path, int maxSteps){
        int sum = 0;
        for (Integer i : path){
            sum+=i;
        }
        return sum>=maxSteps;
    }
    
    public static void modify(Integer x){
        x++;
        return;
    }
    
    ///  1    2   3
    
    /// 11 12 13
    /// 21 22 23
    /// 31 32 33
    
    ///111 121
    public static boolean build(List<List<Integer>> paths, List<Integer> steps, int maxSteps){
        List<List<Integer>> next = new ArrayList<List<Integer>>();
        for (List<Integer> path : paths){
            if (isClimedTooHigh(path, maxSteps)){
                next.add(path);
            }
            for (Integer step : steps){
                List<Integer> p = new ArrayList<Integer>(path);
                p.add(step);
                next.add(p);
            }
        }
        paths.clear();
        boolean completed = true;
        for (List<Integer> n : next){
            if (completed && !isClimedTooHigh(n, maxSteps))
                completed = false;
            paths.add(n);
        }
    
        return completed;
    }
    
    public static boolean isPathEqualToMax(List<Integer> path, int maxSteps){
        int sum = 0;
        for (Integer i : path){
            sum+=i;
        }
        return sum==maxSteps;
    }
    
    public static void calculate( int stepSize, int maxSteps ){
        List<List<Integer>> paths = new ArrayList<List<Integer>>();
        List<Integer> steps = new ArrayList<Integer>();
        for (int i =1; i < stepSize; i++){
            List<Integer> s = new ArrayList<Integer>(1);
            s.add(i);
            steps.add(i);
            paths.add(s);
        }
        while (!build(paths,steps,maxSteps));
        List<List<Integer>> finalPaths = new ArrayList<List<Integer>>();
        for (List<Integer> p : paths){
            if (isPathEqualToMax(p, maxSteps)){
                finalPaths.add(p);
            }
        }
    
        System.out.println(finalPaths.size());
    }
    public static void main(String[] args){
        calculate(3,1);
        calculate(3,2);
        calculate(3,3);
        calculate(3,4);
        calculate(3,5);
        calculate(3,6);
        calculate(3,7);
    
        return;
    }
    

    }

    0 讨论(0)
  • 2020-12-08 08:50

    Here is an O(Nk) Java implementation using dynamic programming:

    public class Sample {
        public static void main(String[] args) {
            System.out.println(combos(new int[]{4,3,2,1}, 100));
        }
    
        public static int combos(int[] steps, int stairs) {
            int[][] table = new int[stairs+1][steps.length];
            for (int i = 0; i < steps.length; i++) {
    
                for (int n = 1; n <= stairs; n++ ) {
                    int count = 0;
                    if (n % steps[i] == 0){
                        if (i == 0)
                            count++;
                        else {
                            if (n <= steps[i])
                                count++;
                        }
                    }
    
                    if (i > 0 && n > steps[i]) {
                        count += table[n - steps[i]][i];
                    }
    
                    if (i > 0)
                        count += table[n][i-1];
    
                    table[n][i] = count;
                }
            }
    
            for (int n = 1; n < stairs; n++) {
                System.out.print(n + "\t");
                for (int i = 0; i < steps.length; i++) {
                    System.out.print(table[n][i] + "\t");
                }
                System.out.println();
            }
            return table[stairs][steps.length-1];
        }
    }
    

    The idea is to fill the following table 1 column at a time from left to right:

    N    (4)    (4,3)  (4,3,2) (4,3,2,1)
    1   0   0   0   1   
    2   0   0   1   2   
    3   0   1   1   3   
    4   1   1   2   5   
    5   0   0   1   6   
    6   0   1   3   9   
    7   0   1   2   11  
    8   1   1   4   15  
    9   0   1   3   18  
    10  0   1   5   23  
    11  0   1   4   27  
    12  1   2   7   34  
    13  0   1   5   39  
    ..
    ..
    99  0   9   217 7803
    100             8037
    
    0 讨论(0)
  • 2020-12-08 08:51

    I would say that the formula will look in the following way:

    K(1) = 1
    K(2) = 2
    k(3) = 4
    K(n) = K(n-3) + K(n-2) + K(n - 1)
    

    The formula says that in order to reach the n'th step we have to firstly reach:

    • n-3'th step and then take 3 steps at once i.e. K(n-3)
    • or n-2'th step and then take 2 steps at once i.e. K(n-2)
    • or n-1'th step and then take 1 steps at once i.e. K(n-1)

    K(4) = 7, K(5) = 13 etc.

    You can either utilize the recursive formula or use dynamic programming.

    0 讨论(0)
  • 2020-12-08 08:53

    Below is the several ways to use 1 , 2 and 3 steps

    1: 1
    2: 11   2
    3: 111 12 21 3
    4: 1111 121 211 112 22 13 31
    5: 11111 1112 1121 1211 2111 122 212 221 113 131 311 23 32
    6: 111111 11112 11121 11211 12111 21111 1113 1131 1311 3111 123 132 312 321 213 231 33 222 1122 1221 2211 1212 2121 2112
    

    So according to above combination the soln should be:

    K(n) = K(n-3) + K(n-2) + K(n - 1)
    k(6) = 24 which is k(5)+k(4)+k(3) = 13+7+4
    
    0 讨论(0)
  • 2020-12-08 08:55
    def count(steps):
      sol = []
      sol.append(1)
      sol.append(1 + sol[0])
      sol.append(1 + sol[1] + sol[0])
    
    
      if(steps > 3):
        for x in range(4, steps+1):
            sol[(x-1)%3] = sum(sol)
    
    
    return sol[(steps-1)%3]
    
    0 讨论(0)
  • 2020-12-08 08:57

    Python solutions:

    Recursive O(n)

    This is based on the answer by Michael. This requires O(n) CPU and O(n) memory.

    import functools
    
    @functools.lru_cache(maxsize=None)
    def recursive(n):
        if n < 4:
            initial = [1, 2, 4]
            return initial[n-1]
        else:
            return recursive(n-1) + recursive(n-2) + recursive(n-3)
    

    Recursive O(log(n))

    This is per a comment for this answer. This tribonacci-by-doubling solution is analogous to the fibonacci-by-doubling solution in the algorithms by Nayuki. Note that multiplication has a higher complexity than constant. This doesn't require or benefit from a cache.

    def recursive_doubling(n):
        def recursive_tribonacci_tuple(n):
            """Return the n, n+1, and n+2 tribonacci numbers for n>=0.
    
            Tribonacci forward doubling identities:
            T(2n)   = T(n+1)^2 + T(n)*(2*T(n+2) - 2*T(n+1) - T(n))
            T(2n+1) = T(n)^2 + T(n+1)*(2*T(n+2) - T(n+1))
            T(2n+2) = T(n+2)^2 + T(n+1)*(2*T(n) + T(n+1))
            """
            assert n >= 0
            if n == 0:
                return 0, 0, 1  # T(0), T(1), T(2)
    
            a, b, c = recursive_tribonacci_tuple(n // 2)
            x = b*b + a*(2*(c - b) - a)
            y = a*a + b*(2*c - b)
            z = c*c + b*(2*a + b)
    
            return (x, y, z) if n % 2 == 0 else (y, z, x+y+z)
        return recursive_tribonacci_tuple(n)[2]  # Is offset by 2 for the steps problem.
    

    Iterative O(n)

    This is motivated by the answer by 太極者無極而生. It is a modified tribonacci extension of the iterative fibonacci solution. It is modified from tribonacci in that it returns c, not a.

    def iterative(n):
        a, b, c = 0, 0, 1
        for _ in range(n):
            a, b, c = b, c, a+b+c
        return c
    

    Iterative O(log(n)) (left to right)

    This is per a comment for this answer. This modified iterative tribonacci-by-doubling solution is derived from the corresponding recursive solution. For some background, see here and here. It is modified from tribonacci in that it returns c, not a. Note that multiplication has a higher complexity than constant.

    The bits of n are iterated from left to right, i.e. MSB to LSB.

    def iterative_doubling_l2r(n):
        """Return the n+2 tribonacci number for n>=0.
    
        Tribonacci forward doubling identities:
        T(2n)   = T(n+1)^2 + T(n)*(2*T(n+2) - 2*T(n+1) - T(n))
        T(2n+1) = T(n)^2 + T(n+1)*(2*T(n+2) - T(n+1))
        T(2n+2) = T(n+2)^2 + T(n+1)*(2*T(n) + T(n+1))
        """
        assert n >= 0
        a, b, c = 0, 0, 1  # T(0), T(1), T(2)
        for i in range(n.bit_length() - 1, -1, -1):  # Left (MSB) to right (LSB).
            x = b*b + a*(2*(c - b) - a)
            y = a*a + b*(2*c - b)
            z = c*c + b*(2*a + b)
            bit = (n >> i) & 1
            a, b, c = (y, z, x+y+z) if bit else (x, y, z)
        return c
    

    Notes:

    • list(range(m - 1, -1, -1)) == list(reversed(range(m)))
    • If the bit is odd (1), the sequence is advanced by one iteration. This intuitively makes sense after understanding the same for the efficient integer exponentiation problem.

    Iterative O(log(n)) (right to left)

    This is per a comment for this answer. The bits of n are iterated from right to left, i.e. LSB to MSB. This approach is probably not prescriptive.

    def iterative_doubling_r2l(n):
        """Return the n+2 tribonacci number for n>=0.
    
        Tribonacci forward doubling identities:
        T(2n)   = T(n+1)^2 + T(n)*(2*T(n+2) - 2*T(n+1) - T(n))
        T(2n+1) = T(n)^2 + T(n+1)*(2*T(n+2) - T(n+1))
        T(2n+2) = T(n+2)^2 + T(n+1)*(2*T(n) + T(n+1))
    
        Given Tribonacci tuples (T(n), T(n+1), T(n+2)) and (T(k), T(k+1), T(k+2)),
        we can "add" them together to get (T(n+k), T(n+k+1), T(n+k+2)).
    
        Tribonacci addition formulas:
        T(n+k)   = T(n)*(T(k+2) - T(k+1) - T(k)) + T(n+1)*(T(k+1) - T(k)) + T(n+2)*T(k)
        T(n+k+1) = T(n)*T(k) + T(n+1)*(T(k+2) - T(k+1)) + T(n+2)*T(k+1)
        T(n+k+2) = T(n)*T(k+1) + T(n+1)*(T(k) + T(k+1)) + T(n+2)*T(k+2)
        When n == k, these are equivalent to the doubling formulas.
        """
        assert n >= 0
        a, b, c = 0, 0, 1  # T(0), T(1), T(2)
        d, e, f = 0, 1, 1  # T(1), T(2), T(3)
        for i in range(n.bit_length()):  # Right (LSB) to left (MSB).
            bit = (n >> i) & 1
            if bit:
                # a, b, c += d, e, f
                x = a*(f - e - d) + b*(e - d) + c*d
                y = a*d + b*(f - e) + c*e
                z = a*e + b*(d + e) + c*f
                a, b, c = x, y, z
            # d, e, f += d, e, f
            x = e*e + d*(2*(f - e) - d)
            y = d*d + e*(2*f - e)
            z = f*f + e*(2*d + e)
            d, e, f = x, y, z
        return c
    

    Approximations

    Approximations are of course useful mainly for very large n. The exponentiation operation is used. Note that exponentiation has a higher complexity than constant.

    def approx1(n):
        a_pos = (19 + 3*(33**.5))**(1./3)
        a_neg = (19 - 3*(33**.5))**(1./3)
        b = (586 + 102*(33**.5))**(1./3)
        return round(3*b * ((1./3) * (a_pos+a_neg+1))**(n+1) / (b**2 - 2*b + 4))
    

    The approximation above was tested to be correct till n = 53, after which it differed. It's certainly possible that using higher precision floating point arithmetic will lead to a better approximation in practice.

    def approx2(n):
        return round((0.618363 * 1.8392**n + \
                      (0.029252 + 0.014515j) * (-0.41964 - 0.60629j)**n + \
                      (0.029252 - 0.014515j) * (-0.41964 - 0.60629j)**n).real)
    

    The approximation above was tested to be correct till n = 11, after which it differed.

    0 讨论(0)
提交回复
热议问题