【问题标题】:How would one calculate a limit as it approaches infinity or 0?当极限接近无穷大或 0 时,如何计算极限?
【发布时间】:2019-01-21 16:06:21
【问题描述】:

在业余时间,我喜欢编写定理。

const PI = 3.141592653589793; // Math.PI
function CalculatePi(total = 0, x = 1, addSubBool = false) {
  if (addSubBool) {
    total -= (4 / x)
    console.log(`${total}\tDifference: ${PI - total}`);
  } else {
    total += (4 / x)
    console.log(`${total}\tDifference: ${total - PI}`);
  }
  if (total !== PI) {
    setTimeout(function() {
      CalculatePi(total, x + 2, !addSubBool);
    }, 100);
  }
}
CalculatePi();

这是一个计算 pi 的递归调用。我基于this link

我的问题是,如何计算编程限制?这个电话将无限期进行。

那么当 x 接近无穷大时,计算器或其他编程语言如何计算极限?我可以为 x 设置一个最大值吗?

【问题讨论】:

  • 您想限制计算次数还是停止在足够接近您需要的结果上
  • 后者。如果有办法优化计算量,我很想听听。

标签: javascript recursion tail-recursion calculus


【解决方案1】:

下面我们使用looprecur 让你的函数无限循环。我没有使用setTimeout,而是尽可能快地重复出现,而是为每个1000间隔输出一个结果x -

const recur = (...values) =>
  ({ recur, values })
  
const loop = f =>
{ let acc = f ()
  while (acc && acc.recur === recur)
    acc = f (...acc.values)
  return acc
}

const calculatePi = (limit = Infinity) =>
  loop // loop our function ...
    ( (total = 0, x = 1, addSubBool = false) =>
      { if (x > limit) // stop condition
          return total
          
        if (x % 1e3 === 1) // display progress as we go
          console.log(x, total, total - Math.PI)

        if (total === Math.PI) // solution found
          return total
        
        if (addSubBool)
          return recur // recur with ...
            ( total - 4 / x  // next total
            , x + 2          // next x
            , false          // next addSubBool
            )

        else
          return recur // recur with ...
            ( total + 4 / x  // next total
            , x + 2          // next x
            , true           // next addSubBool
            )
     }
   )
   
console.log(calculatePi(1e7))

如您所见,此方法需要很长时间才能收敛到答案。即使在一千万 (10M) x 之后,我们仍然只计算了 6 个精度点 -

x       total               diff
...
9997001 3.1415924535297624 -2.0006003076389334e-7
9998001 3.1415924535497695 -2.0004002365681117e-7
9999001 3.141592453569776 -2.0002001699381822e-7

另一种方法是将precision 作为calculatePi 的输入。我们不会被任意的x 限制,而是继续计算直到达到特定的精度。出于演示目的,此函数还返回 x,因此我们可以看到 x 在达到所需精度之前必须达到多大 -

const calculatePi = (precision = 1e5) =>
  loop
    ( (total = 0, x = 1, addSubBool = false) =>
      { if (total * precision >> 0 === Math.PI * precision >> 0)
          return [ total, x ]

        if (addSubBool)
          return recur
            ( total - 4 / x
            , x + 2
            , false
            )

        else
          return recur
            ( total + 4 / x
            , x + 2
            , true
            )
     }
   )

如您所见,x 超过 3700 万,达到小数点后 7 位的精度 -

console .log
  ( calculatePi (1e2)
    // [ 3.14999586659347, 239 ]

  , calculatePi (1e3)
    // [ 3.141000236580159, 3377 ]

  , calculatePi (1e4)
    // [ 3.1415000095284658, 21589 ]

  , calculatePi (1e5)
    // [ 3.141599999994786, 272243 ]

  , calculatePi (1e7)
    // [ 3.1415926000000005, 37320609 ]
  )

展开下面的 sn-p 以在浏览器中验证结果 -

const recur = (...values) =>
  ({ recur, values })
  
const loop = f =>
{ let acc = f ()
  while (acc && acc.recur === recur)
    acc = f (...acc.values)
  return acc
}

const calculatePi = (precision = 1e5) =>
  loop
    ( (total = 0, x = 1, addSubBool = false) =>
      { if (total * precision >> 0 === Math.PI * precision >> 0)
          return [ total, x ]
        
        if (addSubBool)
          return recur
            ( total - 4 / x
            , x + 2
            , false
            )

        else
          return recur
            ( total + 4 / x
            , x + 2
            , true
            )
     }
   )
   
console .log
  ( calculatePi (1e2)
    // [ 3.14999586659347, 239 ]
  
  , calculatePi (1e3)
    // [ 3.141000236580159, 3377 ]
  
  , calculatePi (1e4)
    // [ 3.1415000095284658, 21589 ]
  
  , calculatePi (1e5)
    // [ 3.141599999994786, 272243 ]
  
  , calculatePi (1e7)
    // [ 3.1415926000000005, 37320609 ]
  )

最后,在计算 pi 时检查 Math.PI 没有多大意义;我想整个目标是计算一个我们假装不知道的数字。为此,我们从一些guess 开始,然后测量它与total 之间的差异。如果猜测在指定的容差范围内,则返回猜测 -

const calculatePi = (precision = 1e5) =>
  loop
    // guess starts at 1
    ( (guess = 1, total = 0, x = 1, addSubBool = false) =>
      { if (Math .abs (guess - total) * precision < 1)
          return [ guess, x ]

        if (addSubBool)
          return recur // recur with ...
            ( total          // next guess
            , total - 4 / x  // next total
            , x + 2          // next x
            , false          // next addSubBool
            )

        else
          return recur // recur with ...
            ( total         // next guess
            , total + 4 / x // next total
            , x + 2         // next x
            , true          // next addSubBool
            )
     }
   )

我们可以看到它按预期工作。诚然,我对输入精度与计算所需的 x 之间的相关性感到惊讶 -

console .log
  ( calculatePi (1e2)
    // [ 3.136592684838816, 403 ]

  , calculatePi (1e3)
    // [ 3.1410926536210413, 4003 ]

  , calculatePi (1e4)
    // [ 3.1415426535898248, 40003 ]

  , calculatePi (1e5)
    // [ 3.1415876535897618, 400003 ]

  , calculatePi (1e7)
    // [ 3.141592603589817, 40000003 ]
  )

展开下面的 sn-p 以在浏览器中验证结果 -

const recur = (...values) =>
  ({ recur, values })
  
const loop = f =>
{ let acc = f ()
  while (acc && acc.recur === recur)
    acc = f (...acc.values)
  return acc
}

const calculatePi = (precision = 1e5) =>
  loop
    // guess starts at 1
    ( (guess = 1, total = 0, x = 1, addSubBool = false) =>
      { if (Math .abs (guess - total) * precision < 1)
          return [ guess, x ]
        
        if (addSubBool)
          return recur // recur with ...
            ( total          // next guess
            , total - 4 / x  // next total
            , x + 2          // next x
            , false          // next addSubBool
            )

        else
          return recur // recur with ...
            ( total         // next guess
            , total + 4 / x // next total
            , x + 2         // next x
            , true          // next addSubBool
            )
     }
   )
   
console .log
  ( calculatePi (1e2)
    // [ 3.136592684838816, 403 ]
  
  , calculatePi (1e3)
    // [ 3.1410926536210413, 4003 ]
  
  , calculatePi (1e4)
    // [ 3.1415426535898248, 40003 ]
  
  , calculatePi (1e5)
    // [ 3.1415876535897618, 400003 ]
  
  , calculatePi (1e7)
    // [ 3.141592603589817, 40000003 ]
  ) 

【讨论】:

  • 这是一个写得很好的解释。我不能要求一个更好的。我很高兴您最终能够对输入精度和必要的 x 进行计算感到惊讶。非常感谢
  • 我很乐意提供帮助。我有more answers 使用looprecur 如果你想看到它们在其他情况下使用:D
【解决方案2】:

就个人而言,我会定义一个合适的容差,然后比较当前值和上一个值之间的差异。如果差值低于公差,则停止计算,并且您知道结果准确到正负公差。

您也可以继续计算,直到获得两个相同的值,因为这可能意味着您已达到存储结果的数据类型的精度限制,并且任何进一步的计算都没有意义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-04
    • 2017-12-26
    • 2023-03-30
    • 1970-01-01
    • 2021-08-29
    • 1970-01-01
    相关资源
    最近更新 更多