0

I'm trying to get a specific converging series function worked out in JavaScript:

function cnvg(sum,marker){
  if((marker--||1000)>=0){
    return cnvg(sum=(sum||0) + 1/Math.pow(-3,marker)/(2*marker+1), marker)
  } else {
    return sum;
  }
}

I'm expecting cnvg() to come back with the equivalent of Math.PI/Math.sqrt(12) (see image attached below) but I keep getting a "Maximum call stack size exceeded" error. I thought it may be the number of iterations, so I dropped the 1000 reference to 100 then 10 and finally to 1, but I still seem to be receiving the error.

enter image description here

Theoretically, once marker has counted down to 0 and executes the last loop it should stop and return the value of sum, but this doesn't seem to be the case... Can anyone tell me what I'm doing wrong?

Thanks in advance.

Eliseo D'Annunzio
  • 592
  • 1
  • 10
  • 26

2 Answers2

5

marker is never assigned in cnvg(), resulting in an infinite recursion. Did you mean:

function cnvg(sum, marker) {
  marker = (typeof(marker) === 'undefined') ? 1000 : marker;
  if (marker-- >= 0) {
    return cnvg(sum=(sum||0) + 1/Math.pow(-3,marker)/(2*marker+1), marker)
  } else {
    return sum;
  }
}

but that's giving me 3 + Math.PI/Math.sqrt(12)... (3.9068996821171087)

marker-- performs subtraction after the check, resulting in an additional term with marker = -1. Use --marker or > 0, or more clearly:

marker = (typeof(marker) === 'undefined') ? 1000 : marker - 1;
if (marker >= 0) {
    // ...
aaron
  • 39,695
  • 6
  • 46
  • 102
1

default parameters

in ye olde times, we wrote default parameters like this

function add (x, y) {
  if (x === undefined) x = 0
  if (y === undefined) y = 0
  return x + y
}

now with ES2015 and later, we can write them like this

function add (x = 0, y = 0) {
  return x + y
}

keep things simple

the recursive procedure can be written simply as

const converge = (k = 0) =>
  k < 0
    ? 0
    : converge (k - 1) + (Math.pow (-3, -k) / (2 * k + 1))

console.log (converge (1000))          // 0.9068996821171091
console.log (Math.PI / Math.sqrt (12)) // 0.9068996821171089

of course, it helps readability if you abstract sigma first

const sigma = f => k =>
  k < 0
    ? 0
    : f (k) + sigma (f) (k - 1)

// hey, this looks just like the formula you posted
const converge =
  sigma (k => Math.pow (-3, -k) / (2 * k + 1))

console.log (converge (1000))          // 0.9068996821171091
console.log (Math.PI / Math.sqrt (12)) // 0.9068996821171089

stack safety

I would like to point out that the stack was never in danger over overflowing – it would take a k-value of around 10,000 to cause an overflow

console.log (converge (1e4)) // RangeError: Maximum call stack size exceeded

In this case, it doesn't matter, because even a tiny k-value of 10 already computes the sixth decimal place; a k-value of 100 computes 14 decimal places

But whatever the case, maybe you have some library that lets you compute decimals with more precision and you want to make converge stack safe...

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

// now stack-safe
const sigma = f => n =>
  loop ((acc = 0, k = n) =>
    k < 0
      ? acc
      : recur (acc + f (k), k - 1))

const converge =
  sigma (k => Math.pow (-3, -k) / (2 * k + 1))

console.log (converge (1e4))           // 0.9068996821171089 << super precise !!
console.log (Math.PI / Math.sqrt (12)) // 0.9068996821171089
Mulan
  • 129,518
  • 31
  • 228
  • 259