Nステップあり、1つまたは2つのステップを一度に上がることができる場合、ステップ数とそれらの上昇方法の間にフィボナッチ関係があります。 2 + 1と1 + 2を異なるものとして数えない場合に限ります。
ただし、これはもう当てはまりません。3番目のオプションを追加して、3つのステップを実行する必要があります。どうすればよいですか?
私が持っているもの:
1 step = 1 way
2 steps = 2 ways: 1+1, 2
3 steps = 4 ways: 1+1+1, 2+1, 1+2, 3
私はここからn段の階段の数を見つけるためにどこへ行くべきか分かりません
N = 4の場合は7を、n = 5の場合は14を取得します。その前にすべての組み合わせを合計すると、14 + 7 + 4 + 2 + 1になります。したがって、nステップのウェイ= n-1ウェイ+ n-2ウェイ+..。1ウェイは、すべての値を保持していると想定しています。動的プログラミング。 1、2、3ステップは基本的なケースでしょうか?
数式は次のようになります。
K(1) = 1
K(2) = 2
k(3) = 4
K(n) = K(n-3) + K(n-2) + K(n - 1)
式は、n番目のステップに到達するために、最初に到達する必要があることを示しています。
K(4)= 7、K(5) = 13など。
再帰的な式を使用するか、動的プログラミングを使用できます。
Pythonソリューション:
これは Michaelによる回答 に基づいています。これにはO(n) CPUとO(n)メモリが必要です。
_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)
_
これは、この回答のコメントごとです。この tribonacci -by-doublingソリューションは、Nayukiによる algorithms のfibonacci-by-doubling solution に類似しています。乗算は定数よりも複雑であることに注意してください。これは、キャッシュを必要としないか、キャッシュから利益を得ません。
_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.
_
これは 太極者無極而生による答え によって動機付けられています。 反復フィボナッチ解 の修正トリボナッチ拡張です。これは、c
ではなくa
を返すという点でtribonacciから変更されています。
_def iterative(n):
a, b, c = 0, 0, 1
for _ in range(n):
a, b, c = b, c, a+b+c
return c
_
これは、この回答のコメントごとです。この修正反復トリボナッチバイダブリングソリューションは、対応する再帰的ソリューションから派生しています。背景については、 here および here を参照してください。これは、c
ではなくa
を返すという点でtribonacciから変更されています。乗算は定数よりも複雑であることに注意してください。
n
のビットは左から右に反復されます。つまり、 [〜#〜] msb [〜#〜] から [〜#〜] 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
_
ノート:
list(range(m - 1, -1, -1)) == list(reversed(range(m)))
これは、この回答のコメントごとです。 n
のビットは、右から左に、つまりLSBからMSBに反復されます。このアプローチはおそらく規範的ではありません。
_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
_
もちろん、近似は主に非常に大きなn
に役立ちます。べき乗演算が使用されます。べき乗は定数よりも複雑であることに注意してください。
_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))
_
上記の 近似 は、n = 53まで正しくテストされ、その後は異なりました。より高精度の浮動小数点演算を使用すると、実際により良い近似が得られる可能性があります。
_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)
_
上記の 近似 は、n = 11まで正しくテストされ、その後は異なりました。
_# recursion requirement: it returns the number of way up
# a staircase of n steps, given that the number of steps
# can be 1, 2, 3
def how_many_ways(n)
# this is a bit Zen like, if 0 steps, then there is 1 way
# and we don't even need to specify f(1), because f(1) = summing them up
# and so f(1) = f(0) = 1
# Similarly, f(2) is summing them up = f(1) + f(0) = 1 + 1 = 2
# and so we have all base cases covered
return 1 if n == 0
how_many_ways_total = 0
(1..3).each do |n_steps|
if n >= n_steps
how_many_ways_total += how_many_ways(n - n_steps)
end
end
return how_many_ways_total
end
0.upto(20) {|n| puts "how_many_ways(#{n}) => #{how_many_ways(n)}"}
_
_def how_many_ways(n)
# this is a bit Zen like, if 0 steps, then there is 1 way
# if n is negative, there is no way and therefore returns 0
return 1 if n == 0
return 0 if n < 0
return how_many_ways(n - 1) + how_many_ways(n - 2) + how_many_ways(n - 3)
end
0.upto(20) {|n| puts "how_many_ways(#{n}) => #{how_many_ways(n)}"}
_
_#
# from 0 to 27: recursive: 4.72 second
# iterative: 0.03 second
#
def how_many_ways(n)
arr = [0, 0, 1]
n.times do
new_sum = arr.inject(:+) # sum them up
arr.Push(new_sum).shift()
end
return arr[-1]
end
0.upto(27) {|n| puts "how_many_ways(#{n}) => #{how_many_ways(n)}"}
_
_how_many_ways(0) => 1
how_many_ways(1) => 1
how_many_ways(2) => 2
how_many_ways(3) => 4
how_many_ways(4) => 7
how_many_ways(5) => 13
how_many_ways(6) => 24
how_many_ways(7) => 44
how_many_ways(8) => 81
how_many_ways(9) => 149
how_many_ways(10) => 274
how_many_ways(11) => 504
how_many_ways(12) => 927
how_many_ways(13) => 1705
.
.
how_many_ways(22) => 410744
how_many_ways(23) => 755476
how_many_ways(24) => 1389537
how_many_ways(25) => 2555757
how_many_ways(26) => 4700770
how_many_ways(27) => 8646064
_
@MichałKomorowskiの説明と@riciのコメントが好きです。 K(3) = 4
を知っているかどうかに依存するのであれば、手動でカウントする必要があると思います。
問題の直感を簡単に得る:
あなたが階段を上っていると考えて、あなたが取ることができる可能なステップは1 & 2
合計No.ステップ4に到達する方法の数=いいえ。ステップ3に到達する方法の数+ステップ2に到達する方法の総数
どうやって?
基本的に、ステップ4に到達するためのステップは2つしかありません。
これら2つは、ステップ4に到達できる唯一の可能性です。
同様に、ステップ2に到達する方法は2つしかありません
F(n) = F(n-1) + F(n-2)
F(0) = 0 and F(1) = 1 are the base cases. From here you can start building F(2), F(3) and so on. This is similar to Fibonacci series.
可能なステップの数が増加した場合、たとえば[1,2,3]のように、すべてのステップに1つのオプションがあります。つまり、その前の3つのステップから直接ジャンプできます。
したがって、式は次のようになります
F(n) = F(n-1) + F(n-2) + F(n-3)
理解するには、このビデオを参照してください 階段問題フィボナッチシリーズ
コードの簡単な理解: geeksforgeeks階段問題
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]
Nに到達する方法を数える番目 ステップ1、2、3を使用した階段。
単純な再帰的メソッドを使用してカウントできます。
// Header File
#include<stdio.h>
// Function prototype for recursive Approch
int findStep(int);
int main(){
int n;
int ways=0;
ways = findStep(4);
printf("%d\n", ways);
return 0;
}
// Function Definition
int findStep(int n){
int t1, t2, t3;
if(n==1 || n==0){
return 1;
}else if(n==2){
return 2;
}
else{
t3 = findStep(n-3);
t2 = findStep(n-2);
t1 = findStep(n-1);
return t1+t2+t3;
}
}
私の解決策はJavaです。このボトムアップを解決することにしました。
私は現在のパスの空の配列を持つことから始めます[]各ステップで、可能なすべてのステップサイズを追加します{1,2,3}
最初のステップ[]-> [[1]、[2]、[3]]
第2ステップ[[1]、[2]、[3]]-> [[1,1]、[1,2]、[1,3]、[2,1]、[2 、2]、[2,3]、[3,1] [3,2]、[3,3]]
反復0:[]
反復1:[[1]、[2]、[3]]
反復2:[[1,1]、[1,2]、[1,3]、[2,1]、[2,2]、[2,3]、[3,1] 、[3,2]、[3,3]]
反復3 [[1,1,1]、[1,1,2]、[1,1,3] ....]
シーケンスの長さは次のとおりです1 2 3 5 8 13 21
私のステップ関数は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;
}
}
以下は、1、2、3ステップを使用するいくつかの方法です
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
したがって、上記の組み合わせによれば、solnは次のようになります。
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
再帰的メモ化ベースのC++ソリューション:階段を上っていくといくつ方法がありますか?一番上の階段でない場合は、すべての隣人に尋ねて合計し、結果を返します。一番上の階段の場合、1と表示されます。
vector<int> getAllStairsFromHere(vector<int>& numSteps, int& numStairs, int currentStair)
{
vector<int> res;
for(auto it : numSteps)
if(it + currentStair <= numStairs)
res.Push_back(it + currentStair);
return res;
}
int numWaysToClimbUtil(vector<int>& numSteps, int& numStairs, int currentStair, map<int,int>& memT)
{
auto it = memT.find(currentStair);
if(it != memT.end())
return it->second;
if(currentStair >= numStairs)
return 1;
int numWaysToClimb = 0;
auto choices = getAllStairsFromHere(numSteps, numStairs, currentStair);
for(auto it : choices)
numWaysToClimb += numWaysToClimbUtil(numSteps, numStairs, it, memT);
memT.insert(make_pair(currentStair, numWaysToClimb));
return memT[currentStair];
}
int numWaysToClimb(vector<int>numSteps, int numStairs)
{
map<int,int> memT;
int currentStair = 0;
return numWaysToClimbUtil(numSteps, numStairs, currentStair, memT);
}
1、2、3ステップで距離をカバーする方法の総数を数えます。
再帰解の時間の複雑さは指数関数的、つまりO(3n)です。
同じサブ問題が再度解決されるため、この問題はサブ問題のプロパティが重複しています。したがって、最小二乗和問題には、動的計画問題の両方の特性があります。
public class MaxStepsCount {
/** Dynamic Programming. */
private static int getMaxWaysDP(int distance) {
int[] count = new int[distance+1];
count[0] = 1;
count[1] = 1;
count[2] = 2;
/** Memorize the Sub-problem in bottom up manner*/
for (int i=3; i<=distance; i++) {
count[i] = count[i-1] + count[i-2] + count[i-3];
}
return count[distance];
}
/** Recursion Approach. */
private static int getMaxWaysRecur(int distance) {
if(distance<0) {
return 0;
} else if(distance==0) {
return 1;
}
return getMaxWaysRecur(distance-1)+getMaxWaysRecur(distance-2)
+getMaxWaysRecur(distance-3);
}
public static void main(String[] args) {
// Steps pf 1, 2 and 3.
int distance = 10;
/** Recursion Approach. */
int ways = getMaxWaysRecur(distance);
System.out.println(ways);
/** Dynamic Programming. */
ways = getMaxWaysDP(distance);
System.out.println(ways);
}
}
これに関する私のブログ投稿:
http://javaexplorer03.blogspot.in/2016/10/count-number-of-ways-to-cover-distance.html
Michałの回答 に基づくJavaの再帰的な実装:
public class Tribonacci {
// k(0) = 1
// k(1) = 1
// k(2) = 2
// k(3) = 4
// ...
// k(n) = k(n-3) + k(n-2) + k(n - 1)
static int get(int n) {
if (n == 0) {
return 1;
} if (n == 1) {
return 1;
} else if (n == 2) {
return 2;
//} else if (n == 3) {
// return 4;
} else {
return get(n - 3) + get(n - 2) + get(n - 1);
}
}
public static void main(String[] args) {
System.out.println("Tribonacci sequence");
System.out.println(Tribonacci.get(1));
System.out.println(Tribonacci.get(2));
System.out.println(Tribonacci.get(3));
System.out.println(Tribonacci.get(4));
System.out.println(Tribonacci.get(5));
System.out.println(Tribonacci.get(6));
}
}
ここにあります O(Nk)Java動的プログラミングを使用した実装:
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];
}
}
アイデアは、次の表1の列を左から右に一度に入力することです。
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