如何用 useMemo 优化这个斐波那契计算的写法?

作者站长头像
站长
· 阅读数 8

如何用 useMemo 优化这个斐波那契计算的写法?

import React from "react";

function fibonacci(n: number): number {
  return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}

function FibonacciExample() {
  const [inputValue, setInputValue] = React.useState(0);

  function handleClick() {
    let time = new Date().getTime();
    console.log(
      fibonacci(inputValue),
      `耗时:${new Date().getTime() - time}ms`
    );
  }

  return (
    <div>
      <input
        type="number"
        value={inputValue}
        onChange={(event) => setInputValue(Number(event.target.value))}
      />
      <button onClick={handleClick}>计算 fibonacci</button>
    </div>
  );
}

export default FibonacciExample;

在网上搜 React.useMemoReact.memo 的区别,发现有这么个斐波那契的例子,是否可以用 useMemo 来优化性能?

回复
1个回答
avatar
test
2024-07-03

对于这个问题,我想你想问的是假如有高负载计算时要怎么保持界面的交互性,因为就你问的这个具体问题解决起来非常简单,要计算一个斐波那契数可以使用O(N)的算法实现比如

function fib(n: number): number {
  if (n === 0) return 0
  let minusTwo = 0
  let minusOne = 1

  for (let i = 2; i <= n; ++i) {
    const temp = minusOne + minusTwo
    minusTwo = minusOne
    minusOne = temp
  }

  return minusOne
}

考虑到斐波那契数膨胀的非常快如果仅仅是要求double能精准表示的斐波那契数完全可以使用打表实现O(1)的时间复杂度求斐波那契数,但是如果你想要知道有没有通用的方法,那么下面的方法可能是你需要的

1. 优化算法

就你问的这个具体的完全可以通过优化算法而不用采取其他任何其他手段就能达到很好的效果

2. 开线程或进程

如果你能权衡好进程或者线程之间通信和同步的成本或者仅仅是不想阻塞页面那么开线程或进程是解决问题的最简单的方式

3. 将任务分成多个子任务分批的完成

这个做法非常灵活,可以通过不同的方式实现就你举得这个例子可以通过以下代码实现, 可以先把递归的方式转换为迭代的方式(这样比较好控制执行流),并且每次执行任务时设置一个时间片,时间片用尽就主动交出执行权这样就算是在单线程的情况下也能保证页面的交互性在线体验地址

const delay = (ms: number) =>
  new Promise<void>((r) => setTimeout(() => r(), ms));

async function fibonacci(n: number): number {
  if (n < 2) return n;

  enum Op {
    Add,
    Calc
  }
  type Task = {
    op: Op;
    value: any;
  };

  const taskStack: Task[] = [];
  const valueStack: number[] = [];

  taskStack.push({
    op: Op.Calc,
    value: n
  });

  let last = performance.now();
  const MIN_TIME_SLICE = 5;
  let currentTimeSlice = 100;
  let remainingTime = currentTimeSlice;
  while (taskStack.length) {
    const now = performance.now();
    remainingTime -= now - last;
    const shouldYield = remainingTime <= 0;
    if (shouldYield) {
      await delay(0);
      // 长时间不能算出来的降低它的优先级(减少它的时间切片)
      currentTimeSlice /= 2;
      remainingTime = Math.max(currentTimeSlice, MIN_TIME_SLICE);
      last = now;
    }

    const { op, value } = taskStack.pop();
    switch (op) {
      case Op.Calc: {
        if (value < 2) {
          valueStack.push(value);
        } else {
          taskStack.push({
            op: Op.Add,
            value: undefined
          });
          taskStack.push({
            op: Op.Calc,
            value: value - 2
          });
          taskStack.push({
            op: Op.Calc,
            value: value - 1
          });
        }
        break;
      }
      case Op.Add: {
        let a = valueStack.pop();
        let b = valueStack.pop();
        valueStack.push(a + b);
        break;
      }
      default:
        throw new Error("Not reachable");
    }
  }

  return valueStack[0];
}

function FibonacciExample() {
  const [inputValue, setInputValue] = React.useState(0);

  async function handleClick() {
    let time = new Date().getTime();
    const ans = await fibonacci(inputValue);
    console.log(ans, `耗时:${new Date().getTime() - time}ms`);
  }

  return (
    <div>
      <input
        type="number"
        value={inputValue}
        onChange={(event) => setInputValue(Number(event.target.value))}
      />
      <button onClick={handleClick}>计算 fibonacci</button>
    </div>
  );
}
回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容