Skip to content

Commit 4932b29

Browse files
committed
feat: 实现爬楼梯问题的矩阵算法解法
1 parent 161b867 commit 4932b29

File tree

2 files changed

+263
-0
lines changed

2 files changed

+263
-0
lines changed

src/algorithms/dpAlgorithm.ts

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { AnimationTimeline } from '../state/animationSlice';
2+
3+
/**
4+
* 使用动态规划算法生成爬楼梯问题的解和动画时间线
5+
* @param n 楼梯的阶数
6+
* @returns 包含结果和动画时间线的对象
7+
*/
8+
export function generateDPSolution(n: number): {
9+
result: number;
10+
timeline: AnimationTimeline[];
11+
} {
12+
// 处理边界情况
13+
if (n <= 0) {
14+
return {
15+
result: 0,
16+
timeline: [{
17+
timestamp: 0,
18+
description: "输入的阶数小于等于0,没有有效的爬楼梯方法",
19+
visualChanges: {
20+
nodeUpdates: [
21+
{ index: 0, props: { x: 50, y: 300, value: 0 } }
22+
],
23+
matrixUpdates: [],
24+
formulaUpdate: "n <= 0,返回0"
25+
},
26+
interactionPoints: []
27+
}]
28+
};
29+
}
30+
31+
if (n === 1) {
32+
return {
33+
result: 1,
34+
timeline: [{
35+
timestamp: 0,
36+
description: "只有1阶楼梯,只有1种爬法",
37+
visualChanges: {
38+
nodeUpdates: [
39+
{ index: 0, props: { x: 50, y: 300, value: 1 } },
40+
{ index: 1, props: { x: 150, y: 260, value: 1 } }
41+
],
42+
matrixUpdates: [],
43+
formulaUpdate: "n = 1,返回1"
44+
},
45+
interactionPoints: []
46+
}]
47+
};
48+
}
49+
50+
// 初始化DP数组
51+
const dp: number[] = new Array(n + 1).fill(0);
52+
dp[0] = 1;
53+
dp[1] = 1;
54+
55+
// 初始化时间线
56+
const timeline: AnimationTimeline[] = [];
57+
58+
// 初始化阶段的时间线
59+
timeline.push({
60+
timestamp: 0,
61+
description: "初始化阶段,第 0 阶和第 1 阶各有 1 种爬法",
62+
visualChanges: {
63+
nodeUpdates: [
64+
{ index: 0, props: { x: 50, y: 300, value: 1 } },
65+
{ index: 1, props: { x: 150, y: 300, value: 1 } }
66+
],
67+
matrixUpdates: [],
68+
formulaUpdate: "f(0)=1, f(1)=1"
69+
},
70+
interactionPoints: []
71+
});
72+
73+
// 状态转移阶段
74+
for (let i = 2; i <= n; i++) {
75+
// 计算当前阶的值
76+
dp[i] = dp[i - 1] + dp[i - 2];
77+
78+
// 添加到时间线
79+
timeline.push({
80+
timestamp: i * 1000, // 时间戳按步骤递增
81+
description: `计算第 ${i} 阶的爬法数量`,
82+
visualChanges: {
83+
nodeUpdates: [
84+
{
85+
index: i,
86+
props: {
87+
x: 50 + i * 100,
88+
y: 300 - Math.min(i * 50, 200),
89+
value: dp[i]
90+
}
91+
}
92+
],
93+
matrixUpdates: [],
94+
formulaUpdate: `f(${i})=f(${i-1})+f(${i-2})=${dp[i-1]}+${dp[i-2]}=${dp[i]}`
95+
},
96+
interactionPoints: []
97+
});
98+
}
99+
100+
// 滚动数组优化阶段
101+
timeline.push({
102+
timestamp: (n + 1) * 1000,
103+
description: "通过滚动数组,我们将空间复杂度从 O(n) 优化到 O(1)",
104+
visualChanges: {
105+
nodeUpdates: [],
106+
matrixUpdates: [],
107+
formulaUpdate: "空间优化:只需保存前两个状态,p=f(n-2), q=f(n-1), r=p+q"
108+
},
109+
interactionPoints: []
110+
});
111+
112+
return {
113+
result: dp[n],
114+
timeline
115+
};
116+
}
117+
118+
/**
119+
* 使用滚动数组优化的动态规划算法计算爬楼梯问题
120+
* @param n 楼梯的阶数
121+
* @returns 到达第n阶的方法数
122+
*/
123+
export function climbStairsDP(n: number): number {
124+
if (n <= 0) return 0;
125+
if (n === 1) return 1;
126+
127+
let p = 1; // f(0)
128+
let q = 1; // f(1)
129+
let r = 0;
130+
131+
for (let i = 2; i <= n; i++) {
132+
r = p + q;
133+
p = q;
134+
q = r;
135+
}
136+
137+
return r;
138+
}

src/algorithms/formulaAlgorithm.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { AnimationTimeline } from '../state/animationSlice';
2+
3+
/**
4+
* 使用通项公式算法生成爬楼梯问题的解和动画时间线
5+
* @param n 楼梯的阶数
6+
* @returns 包含结果和动画时间线的对象
7+
*/
8+
export function generateFormulaSolution(n: number): {
9+
result: number;
10+
timeline: AnimationTimeline[];
11+
} {
12+
// 处理边界情况
13+
if (n <= 0) return { result: 0, timeline: [] };
14+
if (n === 1) return { result: 1, timeline: [] };
15+
16+
// 初始化时间线
17+
const timeline: AnimationTimeline[] = [];
18+
19+
// 特征方程推导阶段
20+
timeline.push({
21+
timestamp: 0,
22+
description: "特征方程推导",
23+
visualChanges: {
24+
nodeUpdates: [],
25+
matrixUpdates: [],
26+
formulaUpdate: "f(n) = f(n-1) + f(n-2) 对应的特征方程:x² - x - 1 = 0"
27+
},
28+
interactionPoints: []
29+
});
30+
31+
// 求解特征方程阶段
32+
const phi = (1 + Math.sqrt(5)) / 2;
33+
const psi = (1 - Math.sqrt(5)) / 2;
34+
35+
timeline.push({
36+
timestamp: 1000,
37+
description: "求解特征方程",
38+
visualChanges: {
39+
nodeUpdates: [],
40+
matrixUpdates: [],
41+
formulaUpdate: `解得特征根:λ₁ = (1+√5)/2 ≈ ${phi.toFixed(3)},λ₂ = (1-√5)/2 ≈ ${psi.toFixed(3)}`
42+
},
43+
interactionPoints: []
44+
});
45+
46+
// 通项公式推导阶段
47+
timeline.push({
48+
timestamp: 2000,
49+
description: "通项公式推导",
50+
visualChanges: {
51+
nodeUpdates: [],
52+
matrixUpdates: [],
53+
formulaUpdate: "通项公式:f(n) = c₁λ₁ⁿ + c₂λ₂ⁿ = c₁((1+√5)/2)ⁿ + c₂((1-√5)/2)ⁿ"
54+
},
55+
interactionPoints: []
56+
});
57+
58+
// 确定系数阶段
59+
timeline.push({
60+
timestamp: 3000,
61+
description: "确定系数",
62+
visualChanges: {
63+
nodeUpdates: [],
64+
matrixUpdates: [],
65+
formulaUpdate: "根据f(0)=1, f(1)=1,解得c₁=1/√5, c₂=-1/√5"
66+
},
67+
interactionPoints: []
68+
});
69+
70+
// 最终公式阶段
71+
timeline.push({
72+
timestamp: 4000,
73+
description: "最终公式",
74+
visualChanges: {
75+
nodeUpdates: [],
76+
matrixUpdates: [],
77+
formulaUpdate: "f(n) = (1/√5)·((1+√5)/2)ⁿ - (1/√5)·((1-√5)/2)ⁿ"
78+
},
79+
interactionPoints: []
80+
});
81+
82+
// 最终结果计算阶段
83+
const result = calculateBinetFormula(n);
84+
timeline.push({
85+
timestamp: 5000,
86+
description: "计算结果",
87+
visualChanges: {
88+
nodeUpdates: [],
89+
matrixUpdates: [],
90+
formulaUpdate: `代入n=${n},得到结果:f(${n}) = ${result}`
91+
},
92+
interactionPoints: []
93+
});
94+
95+
return {
96+
result,
97+
timeline
98+
};
99+
}
100+
101+
/**
102+
* 使用比内公式(Binet's Formula)计算爬楼梯问题
103+
* @param n 楼梯的阶数
104+
* @returns 到达第n阶的方法数
105+
*/
106+
export function climbStairsFormula(n: number): number {
107+
if (n <= 0) return 0;
108+
if (n === 1) return 1;
109+
110+
return calculateBinetFormula(n);
111+
}
112+
113+
/**
114+
* 计算比内公式 (Binet's Formula)
115+
* @param n 阶数
116+
* @returns 计算结果
117+
*/
118+
function calculateBinetFormula(n: number): number {
119+
const sqrt5 = Math.sqrt(5);
120+
const phi = (1 + sqrt5) / 2;
121+
const psi = (1 - sqrt5) / 2;
122+
123+
// 使用精确计算,避免浮点数误差
124+
return Math.round((Math.pow(phi, n) - Math.pow(psi, n)) / sqrt5);
125+
}

0 commit comments

Comments
 (0)