HarmonyOS鸿蒙Next数学类上架项目解析5-二分法求函数零点

HarmonyOS鸿蒙Next数学类上架项目解析5-二分法求函数零点 在开发鸿蒙数学计算应用时,需要实现求解方程f(x)=0的功能。要求:

  1. 使用二分法(Bisection Method)迭代求根
  2. 支持自定义精度和最大迭代次数
  3. 能够判断给定区间内是否存在零点

如何用ArkTS实现一个函数零点求解器?

3 回复

二分法是一种简单可靠的求根方法,基于介值定理:如果连续函数f(x)在区间[a,b]上满足f(a)·f(b)<0,则区间内必存在零点。

算法步骤:

1. 取中点 c = (a+b)/2

2. 计算 f©

3. 若 f(a)·f© < 0,零点在[a,c];否则在[c,b]

4. 重复直到区间足够小

1. 定义数据模型

// 求根结果接口

interface RootResult {

  root: number             // 零点

  iterations: number       // 迭代次数

  precision: number        // 实际精度

  functionValue: number    // f(root)的值

  isValid: boolean         // 是否找到有效根

  errorMsg?: string        // 错误信息

}
// 迭代过程记录

interface IterationStep {

  step: number

  a: number

  b: number

  mid: number

  fMid: number

}
// 数学函数类型

type MathFunction = (x: number) => number

2. 核心二分法函数

/

使用二分法求函数零点

@param f 目标函数

@param a 区间左端点

@param b 区间右端点

@param tolerance 精度要求(默认1e-10)

@param maxIterations 最大迭代次数(默认100)

@returns 求根结果

/

function bisectionMethod(

  f: MathFunction,

  a: number,

  b: number,

  tolerance: number = 1e-10,

  maxIterations: number = 100

): RootResult {

  // 验证输入

  if (Number.isNaN(a) || Number.isNaN(b)) {

    return {

      root: 0,

      iterations: 0,

      precision: 0,

      functionValue: 0,

      isValid: false,

      errorMsg: '请输入有效的区间端点'

    }

  }



  // 确保 a < b

  if (a > b) {

    const temp = a

    a = b

    b = temp

  }



  const fa: number = f(a)

  const fb: number = f(b)



  // 检查端点是否为零点

  if (Math.abs(fa) < tolerance) {

    return {

      root: a,

      iterations: 0,

      precision: Math.abs(fa),

      functionValue: fa,

      isValid: true

    }

  }



  if (Math.abs(fb) < tolerance) {

    return {

      root: b,

      iterations: 0,

      precision: Math.abs(fb),

      functionValue: fb,

      isValid: true

    }

  }



  // 检查是否满足二分法条件

  if (fa  fb > 0) {

    return {

      root: 0,

      iterations: 0,

      precision: 0,

      functionValue: 0,

      isValid: false,

      errorMsg: '区间端点函数值同号,无法确定零点存在'

    }

  }



  // 二分迭代

  let left: number = a

  let right: number = b

  let mid: number = 0

  let fMid: number = 0

  let iterations: number = 0



  while (iterations < maxIterations) {

    mid = (left + right) / 2

    fMid = f(mid)

    iterations++



    // 检查是否达到精度要求

    if (Math.abs(fMid) < tolerance || (right - left) / 2 < tolerance) {

      return {

        root: mid,

        iterations: iterations,

        precision: Math.abs(fMid),

        functionValue: fMid,

        isValid: true

      }

    }



    // 更新区间

    if (f(left)  fMid < 0) {

      right = mid

    } else {

      left = mid

    }

  }



  // 达到最大迭代次数

  return {

    root: mid,

    iterations: iterations,

    precision: Math.abs(fMid),

    functionValue: fMid,

    isValid: true,

    errorMsg: '达到最大迭代次数,结果可能不够精确'

  }

}

3. 带迭代过程记录的版本

/

二分法求根(带迭代过程记录)

/

function bisectionWithSteps(

  f: MathFunction,

  a: number,

  b: number,

  tolerance: number = 1e-6,

  maxIterations: number = 50

): { result: RootResult, steps: IterationStep[] } {

  const steps: IterationStep[] = []



  let left: number = a

  let right: number = b

  let iterations: number = 0



  while (iterations < maxIterations && (right - left) / 2 > tolerance) {

    const mid: number = (left + right) / 2

    const fMid: number = f(mid)

    

    steps.push({

      step: iterations + 1,

      a: left,

      b: right,

      mid: mid,

      fMid: fMid

    })



    if (Math.abs(fMid) < tolerance) {

      break

    }



    if (f(left)  fMid < 0) {

      right = mid

    } else {

      left = mid

    }

    

    iterations++

  }



  const finalMid: number = (left + right) / 2

  const result: RootResult = {

    root: finalMid,

    iterations: iterations,

    precision: Math.abs(f(finalMid)),

    functionValue: f(finalMid),

    isValid: true

  }



  return { result, steps }

}

4. 完整UI组件实现

@Entry

@Component

struct RootFinder {

  @State leftBound: string = '0'

  @State rightBound: string = '2'

  @State tolerance: string = '0.000001'

  @State selectedFunc: string = 'x² - 2'

  @State result: RootResult | null = null



  private funcOptions: string[] = ['x² - 2', 'x³ - x - 1', 'sin(x) - 0.5', 'eˣ - 3', 'cos(x) - x']



  private functions: Map<string, MathFunction> = new Map([

    ['x² - 2', (x: number) => x  x - 2],

    ['x³ - x - 1', (x: number) => x  x  x - x - 1],

    ['sin(x) - 0.5', (x: number) => Math.sin(x) - 0.5],

    ['eˣ - 3', (x: number) => Math.exp(x) - 3],

    ['cos(x) - x', (x: number) => Math.cos(x) - x]

  ])



  build() {

    Column() {

      Text('函数零点求解器')

        .fontSize(24)

        .fontWeight(FontWeight.Bold)

        .margin({ bottom: 10 })



      Text('二分法 (Bisection Method)')

        .fontSize(14)

        .fontColor('#666666')

        .margin({ bottom: 20 })



      // 函数选择

      Row() {

        Text('方程 f(x) = ')

        Select(this.funcOptions.map(item => ({ value: item })))

          .selected(0)

          .value(this.selectedFunc)

          .onSelect((index: number) => {

            this.selectedFunc = this.funcOptions[index]

          })

      }

      .margin({ bottom: 15 })



      // 区间输入

      Row() {

        Text('区间 [')

        TextInput({ text: this.leftBound })

          .width(60)

          .type(InputType.Number)

          .onChange((value: string) => { this.leftBound = value })

        Text(', ')

        TextInput({ text: this.rightBound })

          .width(60)

          .type(InputType.Number)

          .onChange((value: string) => { this.rightBound = value })

        Text(']')

      }

      .margin({ bottom: 10 })



      // 精度输入

      Row() {

        Text('精度:')

        TextInput({ text: this.tolerance })

          .width(120)

          .onChange((value: string) => { this.tolerance = value })

      }

      .margin({ bottom: 20 })



      // 求解按钮

      Button('求解零点')

        .onClick(() => this.handleSolve())

        .width(200)

        .margin({ bottom: 20 })



      // 结果显示

      if (this.result) {

        Column() {

          if (this.result.isValid) {

            Text(f(x) = ${this.selectedFunc} = 0)

              .fontSize(16)

              .margin({ bottom: 12 })

            Text(零点 x = ${this.result.root.toFixed(10)})

              .fontSize(20)

              .fontWeight(FontWeight.Bold)

              .fontColor('#007AFF')

              .margin({ bottom: 8 })

            Text(f(x) = ${this.result.functionValue.toExponential(4)})

              .fontSize(14)

              .margin({ bottom: 4 })

            Text(迭代次数:${this.result.iterations})

              .fontSize(12)

              .fontColor('#666666')

            if (this.result.errorMsg) {

              Text(this.result.errorMsg)

                .fontSize(12)

                .fontColor('#FF9500')

                .margin({ top: 8 })

            }

          } else {

            Text(this.result.errorMsg || '求解失败')

              .fontColor(Color.Red)

          }

        }

        .padding(16)

        .backgroundColor('#f5f5f5')

        .borderRadius(8)

        .alignItems(HorizontalAlign.Start)

      }

    }

    .width('100%')

    .height('100%')

    .padding(20)

  }



  handleSolve(): void {

    const a: number = parseFloat(this.leftBound)

    const b: number = parseFloat(this.rightBound)

    const tol: number = parseFloat(this.tolerance)

    const func = this.functions.get(this.selectedFunc)



    if (func) {

      this.result = bisectionMethod(func, a, b, tol)

    }

  }

}

5. 使用示例

// 示例1:求 √2(即 x² - 2 = 0 的正根)

const f1: MathFunction = (x: number) => x x - 2

const result1 = bisectionMethod(f1, 1, 2)

// { root: 1.4142135624, iterations: 34, … }

// 示例2:求 x³ - x - 1 = 0 的根

const f2: MathFunction = (x: number) => x x x - x - 1

const result2 = bisectionMethod(f2, 1, 2)

// { root: 1.3247179572, iterations: 34, … }

// 示例3:求 sin(x) = 0.5 的解

const f3: MathFunction = (x: number) => Math.sin(x) - 0.5

const result3 = bisectionMethod(f3, 0, 1)

// { root: 0.5235987756 (即 π/6), iterations: 34, … }

总结

使用ArkTS实现二分法的关键点:

  1. 二分法要求区间端点函数值异号(f(a)·f(b) < 0)

  2. 每次迭代区间长度减半,收敛速度为线性

  3. 迭代次数约为 log₂((b-a)/tolerance)

  4. 二分法简单可靠,但只能找到单个根

  5. 需要处理端点恰好是零点的特殊情况

  6. 项目链接:https://gitee.com/solgull/math-fbox

更多关于HarmonyOS鸿蒙Next数学类上架项目解析5-二分法求函数零点的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


鸿蒙Next中二分法求函数零点实现步骤

  1. 定义目标函数,如f(x)=x^2-4
  2. 设置初始区间[a,b],确保f(a)*f(b)<0
  3. 循环计算中点c=(a+b)/2,根据f(c)符号更新区间。
  4. 当区间长度小于精度阈值时终止,返回近似零点。

使用鸿蒙ArkTS实现核心算法,需注意数值精度与收敛条件。

在HarmonyOS Next中,使用ArkTS实现基于二分法的函数零点求解器,关键在于结合ArkTS的强类型特性和函数式编程能力。以下是一个核心实现方案:

1. 定义函数接口与求解器类

// 定义一元函数类型
type UnaryFunction = (x: number) => number;

// 求解器配置
interface SolverConfig {
  tolerance: number;      // 精度要求
  maxIterations: number;  // 最大迭代次数
}

class BisectionSolver {
  private config: SolverConfig;

  constructor(config: SolverConfig) {
    this.config = config;
  }

2. 实现核心二分法算法

  solve(f: UnaryFunction, a: number, b: number): number | null {
    // 验证区间是否存在零点
    if (!this.hasRootInInterval(f, a, b)) {
      return null;
    }

    let left = a;
    let right = b;
    
    for (let i = 0; i < this.config.maxIterations; i++) {
      const mid = (left + right) / 2;
      const fMid = f(mid);
      
      // 达到精度要求
      if (Math.abs(fMid) < this.config.tolerance) {
        return mid;
      }
      
      // 更新区间
      if (f(left) * fMid < 0) {
        right = mid;
      } else {
        left = mid;
      }
      
      // 区间足够小
      if (Math.abs(right - left) < this.config.tolerance) {
        return (left + right) / 2;
      }
    }
    
    return (left + right) / 2; // 返回最后一次近似值
  }

3. 实现零点存在性判断

  private hasRootInInterval(f: UnaryFunction, a: number, b: number): boolean {
    // 检查端点函数值是否异号
    return f(a) * f(b) <= 0;
  }

4. 使用示例

// 创建求解器实例
const solver = new BisectionSolver({
  tolerance: 1e-6,
  maxIterations: 1000
});

// 定义目标函数
const targetFunction: UnaryFunction = (x: number) => {
  return x * x - 2; // 求解√2
};

// 执行求解
const root = solver.solve(targetFunction, 1, 2);
if (root !== null) {
  console.log(`Found root: ${root}`);
} else {
  console.log('No root found in the given interval');
}

关键点说明:

  • 类型安全:通过UnaryFunction类型确保函数签名正确
  • 配置化:支持自定义精度和迭代次数
  • 健壮性:包含区间验证和迭代保护
  • 性能:时间复杂度为O(log n),适合鸿蒙应用性能要求

此实现可直接集成到鸿蒙数学计算应用中,通过调整配置参数平衡精度与计算效率。

回到顶部