等分曲线y=ln(x)算法

效果图:

同样50均分,红色(梯形法则)和绿色(积分表达式)是本文章算法实现的效果(为了对比,有一定偏移),蓝色是简单的均分x得到的效果

产生差异的原因

蓝色的均分x因为在0~1时斜率大,就会让点的间距变大,红色和绿色的是通过均分曲线长度实现的,每个点之间的距离基本相差无几

实现方法

1、梯形法则
  1. totalArcLength(a, b, n): 计算区间[a, b]上的总弧长。
  2. arcLength(x): 计算给定点x处的弧长微分。曲线积分公式y=ln(x)求导得1/x,带入公式得到Math.sqrt(1 + 1 / (x * x))
  3. integrate(f, a, b, n): 使用梯形法则近似计算函数f在区间[a, b]上的定积分,n为区间分割数。
    function integrate(f, a, b, n) {
      const h = (b - a) / n;
      let sum = 0.5 * (f(a) + f(b));
      for (let i = 1; i < n; i++) {
          sum += f(a + i * h);
      }
      return sum * h;
    }
    
    function arcLength(x) {
      return Math.sqrt(1 + 1 / (x * x));
    }
    
    function totalArcLength(a, b, n) {
      return integrate(arcLength, a, b, n);
    }

  4. findXForArcLength(targetLength, a, b, n): 使用二分查找法找到使得弧长等于targetLengthx值。
  5. divideCurve(a, b, n, segments): 将曲线y = ln(x)在区间[a, b]上按曲线总长度均匀分割成segments段,并返回每个分割点的坐标。
    function findXForArcLength(targetLength, a, b, n) {
      const tolerance = 1e-6;
      let low = a;
      let high = b;
      while (high - low > tolerance) {
          const mid = (low + high) / 2;
          const length = totalArcLength(a, mid, n);
          if (length < targetLength) {
              low = mid;
          } else {
              high = mid;
          }
      }
      return (low + high) / 2;
    }
    
    export function divideCurve(a, b, n, segments) {
      const totalLength = totalArcLength(a, b, n);
      const segmentLength = totalLength / segments;
      const points = [];
      for (let i = 1; i <= segments; i++) {
          const targetLength = i * segmentLength;
          const x = findXForArcLength(targetLength, a, b, n);
          const y = Math.log(x);
          points.push({ x, y });
      }
      return points;
    }
    2、积分表达式
  • arcLength(x): 计算给定点x处的弧长积分值,先得到弧长积分∫√(1+1/x∧2)dx,然后对其积分,具体推导:
  • totalArcLength(a, b): 计算从a到b的总弧长。
  • findXForArcLength(targetLength, a, b): 使用二分查找法找到使得弧长等于目标长度的x值。
  • divideCurve2(a, b, n): 将从a到b的曲线分成n段,每段的弧长相等,并返回每个分割点的坐标。
    function arcLength(x) {
      return x * Math.sqrt(1 + 1 / (x * x)) - Math.log(Math.sqrt(1 + 1 / (x * x)) + 1 / x);
    }
    
    function totalArcLength(a, b) {
      return arcLength(b) - arcLength(a);
    }
    
    function findXForArcLength(targetLength, a, b) {
      const tolerance = 1e-6;
      let low = a;
      let high = b;
      while (high - low > tolerance) {
        const mid = (low + high) / 2;
        const length = arcLength(mid) - arcLength(a);
        if (length < targetLength) {
          low = mid;
        } else {
          high = mid;
        }
      }
      return (low + high) / 2;
    }
    
    export function divideCurve2(a, b, n) {
      const totalLength = totalArcLength(a, b);
      const segmentLength = totalLength / n;
      const points = [];
      for (let i = 1; i <= n; i++) {
        const targetLength = i * segmentLength;
        const x = findXForArcLength(targetLength, a, b);
        const y = Math.log(x);
        points.push({ x, y });
      }
      return points;
    }
    3、对比
  • 均分长度视觉效果优于均分x
  • 梯形法则原理简单,不需要积分运算,任何曲线都适用,但是运算量大,而且只是近似值,积分表达式相反,不过需要具体曲线具体计算,不是所有曲线都能积分
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值