HARMONYOS应用实例263:零点存在性定理
·
- 零点存在性定理
- 功能:展示连续曲线穿过x轴的过程,直观理解函数值变号必有零点。
展示连续曲线穿过x轴的过程
直观理解函数值变号必有零点
验证零点存在性定理
支持多种函数类型(多项式函数、正弦函数)
自动检测和计算零点
自动检测函数值变号位置
提供动画演示函数绘制过程
实时显示零点和变号分析
- 功能:展示连续曲线穿过x轴的过程,直观理解函数值变号必有零点。
// 零点存在性定理
// 功能:展示连续曲线穿过x轴的过程,直观理解函数值变号必有零点
// 函数配置接口
interface ZeroPointFunctionConfig {
type: 'polynomial' | 'sine' | 'custom';
a: number;
b: number;
c: number;
d: number;
}
// 变号数据接口
interface SignChangeData {
x: number;
from: number;
to: number;
}
@Entry
@Component
struct ZeroPointTheorem {
@State canvasWidth: number = 350;
@State canvasHeight: number = 350;
@State showGrid: boolean = true;
@State showAxes: boolean = true;
@State showZeroPoints: boolean = true;
@State showSignChange: boolean = true;
@State showAnimation: boolean = false;
@State animationProgress: number = 0;
@State functionConfig: ZeroPointFunctionConfig = {
type: 'polynomial',
a: 1,
b: 0,
c: -4,
d: 0
};
@State zeroPoints: number[] = [];
@State signChanges: SignChangeData[] = [];
build() {
Column({ space: 15 }) {
Text('零点存在性定理')
.fontSize(26)
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center)
Column() {
Text('功能介绍')
.fontSize(18)
.fontWeight(FontWeight.Medium)
Text('展示连续曲线穿过x轴的过程,直观理解函数值变号必有零点,验证零点存在性定理')
.fontSize(14)
.fontColor('#666666')
.textAlign(TextAlign.Center)
}
.width('100%')
.backgroundColor('#E3F2FD')
.borderRadius(10)
.padding(15)
Column({ space: 10 }) {
Text('函数图像')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
Canvas(this.canvasContext)
.width(this.canvasWidth)
.height(this.canvasHeight)
.backgroundColor('#FFFFFF')
.border({ width: 2, color: '#333' })
.borderRadius(10)
.onReady(() => {
this.drawGraph()
})
}
.width('100%')
.padding(10)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 10 }) {
Text('函数选择')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
Row({ space: 10 }) {
Button('多项式函数')
.width('48%')
.height(40)
.fontSize(14)
.backgroundColor(this.functionConfig.type === 'polynomial' ? '#2196F3' : '#E0E0E0')
.onClick(() => {
this.functionConfig = {
type: 'polynomial',
a: 1,
b: 0,
c: -4,
d: 0
}
this.calculateZeroPoints()
this.drawGraph()
})
Button('正弦函数')
.width('48%')
.height(40)
.fontSize(14)
.backgroundColor(this.functionConfig.type === 'sine' ? '#2196F3' : '#E0E0E0')
.onClick(() => {
this.functionConfig = {
type: 'sine',
a: 1,
b: 1,
c: 0,
d: 0
}
this.calculateZeroPoints()
this.drawGraph()
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.width('90%')
.padding(10)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 8 }) {
Text('函数系数调节')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
Column({ space: 8 }) {
Text(`a值: ${this.functionConfig.a.toFixed(2)}`)
.fontSize(14)
.fontColor('#666666')
Slider({
value: this.functionConfig.a,
min: -2,
max: 2,
step: 0.1
})
.width('100%')
.blockColor('#2196F3')
.trackColor('#E0E0E0')
.selectedColor('#2196F3')
.onChange((value: number) => {
this.functionConfig.a = value
this.calculateZeroPoints()
this.drawGraph()
})
}
.width('100%')
.padding(8)
.backgroundColor('#F5F5F5')
.borderRadius(6)
Column({ space: 8 }) {
Text(`b值: ${this.functionConfig.b.toFixed(2)}`)
.fontSize(14)
.fontColor('#666666')
Slider({
value: this.functionConfig.b,
min: -5,
max: 5,
step: 0.1
})
.width('100%')
.blockColor('#2196F3')
.trackColor('#E0E0E0')
.selectedColor('#2196F3')
.onChange((value: number) => {
this.functionConfig.b = value
this.calculateZeroPoints()
this.drawGraph()
})
}
.width('100%')
.padding(8)
.backgroundColor('#F5F5F5')
.borderRadius(6)
Column({ space: 8 }) {
Text(`c值: ${this.functionConfig.c.toFixed(2)}`)
.fontSize(14)
.fontColor('#666666')
Slider({
value: this.functionConfig.c,
min: -10,
max: 10,
step: 0.1
})
.width('100%')
.blockColor('#2196F3')
.trackColor('#E0E0E0')
.selectedColor('#2196F3')
.onChange((value: number) => {
this.functionConfig.c = value
this.calculateZeroPoints()
this.drawGraph()
})
}
.width('100%')
.padding(8)
.backgroundColor('#F5F5F5')
.borderRadius(6)
}
.width('90%')
.padding(10)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 10 }) {
Text('零点分析')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
if (this.zeroPoints.length > 0) {
Column({ space: 8 }) {
Text(`找到 ${this.zeroPoints.length} 个零点:`)
.fontSize(16)
.fontColor('#4CAF50')
.fontWeight(FontWeight.Bold)
ForEach(this.zeroPoints, (zeroPoint: number, index: number) => {
Text(`零点 ${index + 1}: x = ${zeroPoint.toFixed(2)}`)
.fontSize(14)
.fontColor('#2196F3')
})
Text('定理验证: 函数在零点处连续且函数值为0')
.fontSize(14)
.fontColor('#666666')
}
.width('100%')
.padding(15)
.backgroundColor('#E8F5E9')
.borderRadius(10)
} else {
Column() {
Text('未找到零点')
.fontSize(16)
.fontColor('#F44336')
Text('尝试调整函数参数以获得零点')
.fontSize(14)
.fontColor('#666666')
}
.width('100%')
.padding(15)
.backgroundColor('#FFEBEE')
.borderRadius(10)
}
}
.width('100%')
.padding(10)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 10 }) {
Text('变号分析')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
if (this.signChanges.length > 0) {
Column({ space: 8 }) {
Text(`发现 ${this.signChanges.length} 处函数值变号:`)
.fontSize(16)
.fontColor('#FF9800')
.fontWeight(FontWeight.Bold)
ForEach(this.signChanges, (change: SignChangeData, index: number) => {
Text(`在 x = ${change.x.toFixed(2)} 处,从 ${change.from > 0 ? '+' : '-'} 变为 ${change.to > 0 ? '+' : '-'}`)
.fontSize(14)
.fontColor('#FF9800')
})
Text('定理验证: 函数值变号处必有零点')
.fontSize(14)
.fontColor('#666666')
}
.width('100%')
.padding(15)
.backgroundColor('#FFF3E0')
.borderRadius(10)
} else {
Column() {
Text('未发现函数值变号')
.fontSize(16)
.fontColor('#999999')
Text('函数值保持同号')
.fontSize(14)
.fontColor('#666666')
}
.width('100%')
.padding(15)
.backgroundColor('#F5F5F5')
.borderRadius(10)
}
}
.width('100%')
.padding(10)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 8 }) {
Text('显示选项')
.fontSize(16)
.fontWeight(FontWeight.Medium)
Row({ space: 10 }) {
Row({ space: 5 }) {
Text('显示零点')
.fontSize(14)
Text(this.showZeroPoints ? '✓' : '✗')
.fontSize(14)
.fontColor(this.showZeroPoints ? '#4CAF50' : '#F44336')
}
.onClick(() => {
this.showZeroPoints = !this.showZeroPoints
this.drawGraph()
})
Row({ space: 5 }) {
Text('显示变号')
.fontSize(14)
Text(this.showSignChange ? '✓' : '✗')
.fontSize(14)
.fontColor(this.showSignChange ? '#4CAF50' : '#F44336')
}
.onClick(() => {
this.showSignChange = !this.showSignChange
this.drawGraph()
})
Row({ space: 5 }) {
Text('显示网格')
.fontSize(14)
Text(this.showGrid ? '✓' : '✗')
.fontSize(14)
.fontColor(this.showGrid ? '#4CAF50' : '#F44336')
}
.onClick(() => {
this.showGrid = !this.showGrid
this.drawGraph()
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
}
.width('90%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
Row({ space: 10 }) {
Button('开始动画')
.width('48%')
.height(50)
.fontSize(16)
.backgroundColor('#4CAF50')
.onClick(() => {
this.startAnimation()
})
Button('重置')
.width('48%')
.height(50)
.fontSize(16)
.backgroundColor('#FF9800')
.onClick(() => {
this.reset()
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Column({ space: 8 }) {
Text('使用说明')
.fontSize(16)
.fontWeight(FontWeight.Bold)
Text('• 选择函数类型(多项式、正弦)')
.fontSize(14)
.fontColor('#666666')
Text('• 调节函数系数,观察图像变化')
.fontSize(14)
.fontColor('#666666')
Text('• 观察函数值变号和零点')
.fontSize(14)
.fontColor('#666666')
Text('• 理解零点存在性定理')
.fontSize(14)
.fontColor('#666666')
Text('• 点击"开始动画"演示过程')
.fontSize(14)
.fontColor('#666666')
}
.width('95%')
.padding(12)
.backgroundColor('#FFF3E0')
.borderRadius(10)
}
.width('100%')
.height('100%')
.padding(10)
.justifyContent(FlexAlign.Start)
}
private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D();
private calculateZeroPoints() {
this.zeroPoints = []
this.signChanges = []
const step = 0.1
let prevY: number | null = null
let prevX: number | null = null
for (let x = -5; x <= 5; x += step) {
const y = this.calculateFunctionValue(x)
if (prevY !== null && prevX !== null) {
if (prevY * y < 0) {
const zeroPoint = this.findZeroPoint(prevX, x)
if (zeroPoint !== null) {
this.zeroPoints.push(zeroPoint)
}
this.signChanges.push({
x: (prevX + x) / 2,
from: prevY,
to: y
})
}
}
prevY = y
prevX = x
}
}
private findZeroPoint(x1: number, x2: number): number | null {
const tolerance = 0.01
let low = x1
let high = x2
for (let i = 0; i < 50; i++) {
const mid = (low + high) / 2
const midValue = this.calculateFunctionValue(mid)
if (Math.abs(midValue) < tolerance) {
return mid
}
const lowValue = this.calculateFunctionValue(low)
if (lowValue * midValue < 0) {
high = mid
} else {
low = mid
}
}
return null
}
private calculateFunctionValue(x: number): number {
switch (this.functionConfig.type) {
case 'polynomial':
return this.functionConfig.a * x * x * x + this.functionConfig.b * x + this.functionConfig.c
case 'sine':
return this.functionConfig.a * Math.sin(this.functionConfig.b * x)
default:
return 0
}
}
private startAnimation() {
this.showAnimation = true
this.animationProgress = 0
const totalSteps = 100
let step = 0
const animate = () => {
if (step <= totalSteps) {
this.animationProgress = step / totalSteps
this.drawGraph()
step++
setTimeout(animate, 30)
} else {
this.showAnimation = false
}
}
animate()
}
private reset() {
this.functionConfig = {
type: 'polynomial',
a: 1,
b: 0,
c: -4,
d: 0
}
this.calculateZeroPoints()
this.showAnimation = false
this.animationProgress = 0
this.drawGraph()
}
private drawGraph() {
const ctx = this.canvasContext
ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
if (this.showGrid) {
this.drawGridLines(ctx)
}
if (this.showAxes) {
this.drawAxes(ctx)
}
this.drawFunction(ctx)
if (this.showZeroPoints) {
this.drawZeroPoints(ctx)
}
if (this.showSignChange) {
this.drawSignChanges(ctx)
}
}
private drawGridLines(ctx: CanvasRenderingContext2D) {
ctx.strokeStyle = '#E0E0E0'
ctx.lineWidth = 1
const centerX = this.canvasWidth / 2
const centerY = this.canvasHeight / 2
const scale = 30
for (let i = -5; i <= 5; i++) {
ctx.beginPath()
ctx.moveTo(centerX + i * scale, 0)
ctx.lineTo(centerX + i * scale, this.canvasHeight)
ctx.stroke()
}
for (let i = -5; i <= 5; i++) {
ctx.beginPath()
ctx.moveTo(0, centerY + i * scale)
ctx.lineTo(this.canvasWidth, centerY + i * scale)
ctx.stroke()
}
}
private drawAxes(ctx: CanvasRenderingContext2D) {
const centerX = this.canvasWidth / 2
const centerY = this.canvasHeight / 2
ctx.strokeStyle = '#333'
ctx.lineWidth = 2
ctx.beginPath()
ctx.moveTo(centerX, 0)
ctx.lineTo(centerX, this.canvasHeight)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(0, centerY)
ctx.lineTo(this.canvasWidth, centerY)
ctx.stroke()
ctx.fillStyle = '#333'
ctx.font = '12px sans-serif'
ctx.fillText('x', this.canvasWidth - 15, centerY + 15)
ctx.fillText('y', centerX + 5, 15)
ctx.fillText('O', centerX + 5, centerY + 15)
const scale = 30
for (let i = -5; i <= 5; i++) {
if (i !== 0) {
ctx.fillText(i.toString(), centerX + i * scale - 3, centerY + 20)
ctx.fillText((-i).toString(), centerX - 15, centerY + i * scale + 3)
}
}
}
private drawFunction(ctx: CanvasRenderingContext2D) {
const centerX = this.canvasWidth / 2
const centerY = this.canvasHeight / 2
const scale = 30
ctx.strokeStyle = '#2196F3'
ctx.lineWidth = 3
ctx.beginPath()
const endX = this.showAnimation ? -5 + 10 * this.animationProgress : 5
for (let x = -5; x <= endX; x += 0.05) {
const y = this.calculateFunctionValue(x)
const pixelX = centerX + x * scale
const pixelY = centerY - y * scale
if (x === -5) {
ctx.moveTo(pixelX, pixelY)
} else {
ctx.lineTo(pixelX, pixelY)
}
}
ctx.stroke()
ctx.fillStyle = '#2196F3'
ctx.font = '14px sans-serif'
ctx.fillText('f(x)', 10, 30)
}
private drawZeroPoints(ctx: CanvasRenderingContext2D) {
const centerX = this.canvasWidth / 2
const centerY = this.canvasHeight / 2
const scale = 30
this.zeroPoints.forEach((zeroPoint: number) => {
const pixelX = centerX + zeroPoint * scale
const pixelY = centerY
ctx.fillStyle = '#4CAF50'
ctx.beginPath()
ctx.arc(pixelX, pixelY, 8, 0, 2 * Math.PI)
ctx.fill()
ctx.strokeStyle = '#4CAF50'
ctx.lineWidth = 2
ctx.stroke()
ctx.fillStyle = '#4CAF50'
ctx.font = '12px sans-serif'
ctx.fillText(`x=${zeroPoint.toFixed(2)}`, pixelX + 10, pixelY - 10)
})
}
private drawSignChanges(ctx: CanvasRenderingContext2D) {
const centerX = this.canvasWidth / 2
const centerY = this.canvasHeight / 2
const scale = 30
this.signChanges.forEach((change: SignChangeData) => {
const pixelX = centerX + change.x * scale
const pixelY = centerY - change.from * scale
ctx.strokeStyle = '#FF9800'
ctx.lineWidth = 2
ctx.setLineDash([5, 5])
ctx.beginPath()
ctx.moveTo(pixelX, pixelY)
ctx.lineTo(pixelX, centerY - change.to * scale)
ctx.stroke()
ctx.setLineDash([])
ctx.fillStyle = '#FF9800'
ctx.font = '10px sans-serif'
ctx.fillText('变号', pixelX + 5, (pixelY + centerY - change.to * scale) / 2)
})
}
}
更多推荐



所有评论(0)