1. 零点存在性定理
    • 功能:展示连续曲线穿过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)
    })
  }
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐