JavaScript:PI(π)计算器

8

有没有一种方法可以在JavaScript中计算π?我知道你可以使用Math.PI像这样找到派:

var pie = Math.PI;
alert(pie); // output "3.141592653589793"

但这并不准确。我想要的是能够计算它,有尽可能多的位数,而不是像pie = 3.141592...那样。但是,我想要的不仅仅是更多的位数,而是尽可能多(比如有一千个数字,但我需要更多)。


10
当然可以!Javascript是一种图灵完备语言。 - Paul
2
你的问题的答案是肯定的。我非常确定如果你在谷歌上搜索如何做到这一点,你会找到足够的资源。 - Felix Kling
2
你已经知道要使用什么数据类型来存储计算结果了吗? - zerkms
1
相关:https://cs.uwaterloo.ca/~alopez-o/math-faq/mathtext/node12.html - Paul
2
可能是重复的问题 javascript - 更精确的圆周率值? - Qantas 94 Heavy
5个回答

14
这是 Jeremy Gibbons 在 2004 年的《无界水龙头算法求解圆周率十进制数字》第 6 章中描述的流式算法的实现代码:

这里是一个Jeremy Gibbons在《无界水龙头算法求解圆周率十进制数字》中描述的流式算法的实现:

function * generateDigitsOfPi() {
    let q = 1n;
    let r = 180n;
    let t = 60n;
    let i = 2n;
    while (true) {
        let digit = ((i * 27n - 12n) * q + r * 5n) / (t * 5n);
        yield Number(digit);
        let u = i * 3n;
        u = (u + 1n) * 3n * (u + 2n);
        r = u * 10n * (q * (i * 5n - 2n) + r - t * digit);
        q *= 10n * i * (i++ * 2n - 1n);
        t *= u;
    }
}

// Demo
let iter = generateDigitsOfPi();

let output = document.querySelector("div");
(function displayTenNextDigits() {
    let digits = "";
    for (let i = 0; i < 10; i++) digits += iter.next().value;
    output.insertAdjacentHTML("beforeend", digits);
    scrollTo(0, document.body.scrollHeight);
    requestAnimationFrame(displayTenNextDigits);
})();
div { word-wrap:break-word; font-family: monospace }
<div></div>


6

您可以通过使用蒙特卡罗模拟来近似计算π的值。生成范围在[-1,1]之间的随机X和Y,那么点(X, Y)在以原点为中心的单位圆内的概率就是π/4。样本数量越多,对其值的估计就越准确。您可以通过比较单位圆内样本数与总样本数的比例,并乘以4来估算π的值。

this.pi = function(count) {
    var inside = 0;

    for (var i = 0; i < count; i++) {
        var x = random()*2-1;
        var y = random()*2-1;
        if ((x*x + y*y) < 1) {
            inside++
        }
    }

    return 4.0 * inside / count;
}

这种计算的精度是多少?我的意思是在什么时候它会变得不准确? - Roko C. Buljan
1
由于它使用蒙特卡罗模拟,因此在进行少量试验时可能无法产生良好的结果。但是,随着试验次数的增加,良好结果的可能性也会增加。因此,精度是非确定性的。 - andand
2
由于浮点数的语义,如果不使用BigDecimal库,就无法获得比Math.PI更精确的π表示。 - Qantas 94 Heavy
count的值应该是多少才能有很大的机会正确地得到14位小数(就像Math.PI一样)?我尝试了1e9的值,但是仍然只有前3到4个小数位是稳定的(3.1415)。 - trincot

6

我在这个网站上找到了这段代码:

mess = "";
Base = Math.pow(10, 11);
cellSize = Math.floor(Math.log(Base) / Math.LN10);
a = Number.MAX_VALUE;
MaxDiv = Math.floor(Math.sqrt(a));

function makeArray(n, aX, Integer) {
  var i = 0;
  for (i = 1; i < n; i++) aX[i] = null;
  aX[0] = Integer
}

function isEmpty(aX) {
  var empty = true
  for (i = 0; i < aX.length; i++)
    if (aX[i]) {
      empty = false;
      break
    }
  return empty
}

function Add(n, aX, aY) {
  carry = 0
  for (i = n - 1; i >= 0; i--) {
    aX[i] += Number(aY[i]) + Number(carry);
    if (aX[i] < Base) carry = 0;
    else {
      carry = 1;
      aX[i] = Number(aX[i]) - Number(Base)
    }
  }
}

function Sub(n, aX, aY) {
  for (i = n - 1; i >= 0; i--) {
    aX[i] -= aY[i];
    if (aX[i] < 0) {
      if (i > 0) {
        aX[i] += Base;
        aX[i - 1]--
      }
    }
  }
}

function Mul(n, aX, iMult) {
  carry = 0;
  for (i = n - 1; i >= 0; i--) {
    prod = (aX[i]) * iMult;
    prod += carry;
    if (prod >= Base) {
      carry = Math.floor(prod / Base);
      prod -= (carry * Base)
    } else carry = 0;
    aX[i] = prod
  }
}

function Div(n, aX, iDiv, aY) {
  carry = 0;
  for (i = 0; i < n; i++) {
    currVal = Number(aX[i]) + Number(carry * Base);
    theDiv = Math.floor(currVal / iDiv);
    carry = currVal - theDiv * iDiv;
    aY[i] = theDiv
  }
}

function arctan(iAng, n, aX) {
  iAng_squared = iAng * iAng;
  k = 3;
  sign = 0;
  makeArray(n, aX, 0);
  makeArray(n, aAngle, 1);
  Div(n, aAngle, iAng, aAngle);
  Add(n, aX, aAngle);
  while (!isEmpty(aAngle)) {
    Div(n, aAngle, iAng_squared, aAngle);
    Div(n, aAngle, k, aDivK);
    if (sign) Add(n, aX, aDivK);
    else Sub(n, aX, aDivK);
    k += 2;
    sign = 1 - sign
  }
  mess += "aArctan=" + aArctan + "<br>"
}

function calcPI(numDec) {
  var ans = "";
  t1 = new Date();
  numDec = Number(numDec) + 5;
  iAng = new Array(10);
  coeff = new Array(10);
  arrayLength = Math.ceil(1 + numDec / cellSize);
  aPI = new Array(arrayLength);
  aArctan = new Array(arrayLength);
  aAngle = new Array(arrayLength);
  aDivK = new Array(arrayLength);
  coeff[0] = 4;
  coeff[1] = -1;
  coeff[2] = 0;
  iAng[0] = 5;
  iAng[1] = 239;
  iAng[2] = 0;
  makeArray(arrayLength, aPI, 0);
  makeArray(arrayLength, aAngle, 0);
  makeArray(arrayLength, aDivK, 0);
  for (var i = 0; coeff[i] != 0; i++) {
    arctan(iAng[i], arrayLength, aArctan);
    Mul(arrayLength, aArctan, Math.abs(coeff[i]));
    if (coeff[i] > 0) Add(arrayLength, aPI, aArctan);
    else Sub(arrayLength, aPI, aArctan)
  }
  Mul(arrayLength, aPI, 4);
  sPI = "";
  tempPI = "";
  for (i = 0; i < aPI.length; i++) {
    aPI[i] = String(aPI[i]);
    if (aPI[i].length < cellSize && i != 0) {
      while (aPI[i].length < cellSize) aPI[i] = "0" + aPI[i]
    }
    tempPI += aPI[i]
  }
  for (i = 0; i <= numDec; i++) {
    if (i == 0) sPI += tempPI.charAt(i) + ".<br>";
    else {
      if (document.getElementById("cbCount").checked) addcount = " (" + (i) + ")";
      else addcount = "";
      if (document.getElementById("cbSpace").checked) thespace = " ";
      else thespace = "";
      if ((i) % 50 == 0 && i != 0) sPI += tempPI.charAt(i) + addcount + "<br>";
      else if (i % 5 == 0) sPI += tempPI.charAt(i) + thespace;
      else sPI += tempPI.charAt(i)
    }
  }
  ans += ("PI (" + numDec + ")=" + sPI + "<br>");
  ans += ("Win PI=<br>3.1415926535897932384626433832795<br>");
  t2 = new Date();
  timeTaken = (t2.getTime() - t1.getTime()) / 1000;
  ans += "It took: " + timeTaken + " seconds";
  var myDiv = document.getElementById("d1");
  myDiv.innerHTML = ans
}
<form name="" onsubmit="calcPI(this.t1.value);return false;">
  Number of Digits:<br>
  <input type="text" name="t1" id="t1" value="100" size="25" maxlength="25">
  <br>Add Count:
  <input type="checkbox" name="cbCount" id="cbCount" value="" checked="checked">
  <br>Add Spaces:
  <input type="checkbox" name="cbSpace" id="cbSpace" value="" checked="checked">
  <br>
  <input type="button" value="Calculate Pi" onclick="calcPI(this.form.t1.value)">
</form>
<div id="d1"></div>


4
@CaiHaoyang 的工作方式是,你需要掌握一些高等数学和编程知识,具备一定的 JS 和浮点数误差的了解,然后逐行阅读脚本,获取一些线索并做笔记 :) - Roko C. Buljan
我认为,即使没有太多的学习,也可以看出它在比需要的十进制数字位数多一个的数组中进行10¹¹进制的多精度算术运算。我想这应该留有相当大的误差余地,但我不确定是否足够。我很好奇它的速度有多快。 - PJTraill
1
就其价值而言:这个程序可以正确计算出63198位数字。从第63191位开始的数字是1950140348...,但是这段代码却输出了19501403465...。这是预料之中的,因为浮点数精度的极限在某一时刻会发挥作用。 - trincot

2

这里是我的实现,使用了无穷级数

function calculatePI(iterations = 10000){
    let pi = 0;
    let iterator = sequence();

    for(let i = 0; i < iterations; i++){
        pi += 4 /  iterator.next().value;
        pi -= 4 / iterator.next().value;
    }

    function* sequence() {
      let i = 1;
      while(true){
        yield i;
        i += 2;
      }
    }

    return pi;
}

3
这将不会给出比 Math.PI 更多的数字。 - Yukulélé

-3

你可以根据自己的需求使用这个

Math.PI.toFixed(n)

其中n是您希望显示的小数位数。

它显示圆周率的四舍五入值。在15个小数位上,可以认为它是相当准确的。


1
返回的数字不是圆周率。 - Roko C. Buljan
1
这实际上并不返回 pi,而是作为一个双精度浮点数返回最接近 pi 的 n 位小数的实际数字。 - Qantas 94 Heavy
1
这实际上并不会返回一个数字,而是一个字符串。 - ashleedawg
3
他们要求超过15位小数的圆周率。Math.PI只有15位小数,所以.toFixed()无法神奇地生成更多的圆周率数字。 - Hermanboxcar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接