Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get reverse of an equation - JavaScript

Tags:

Let's say I have this formula, for example:

function getExperience(level) {    let a = 0;    for (let x = 1; x < level; x += 1) {      a += Math.floor(x + (200 * (2 ** (x / 3))));    }      return Math.floor(a / 4);  }      for (var i = 1; i < 100; i++) {    console.log(`Level ${i}: ${getExperience(i)}`);  }

To get the experience needed for level 50, you'd do: getExperience(50).

But, how would you reverse that and get the LEVEL needed for experience? So, getLevel(20010272) would output 50.

like image 309
Green Mtn Avatar asked Jan 30 '19 06:01

Green Mtn


2 Answers

Short answer

You can use 4.328085 * Math.log(0.00519842 * xp + 1.259921045) as a very good approximation of the corresponding level.

If you need an exact value, you could iterate over all levels until you find the desired range, as in this answer.

Long answer

Slightly modified function

I don't think it's possible to find an exact, closed-form expression for the inverse of this function. It should be possible if you modify getExperience(level) a bit, though.

  • First, you can notice that x grows much slower than 2 ** (x / 3).
  • Then, Math.floor doesn't have much influence over large numbers.

So let's remove them! Here's the slightly modified function:

function getExperienceEstimate(level) {   let a = 0;   for (let x = 1; x < level; x += 1) {     a += 200 * (2 ** (x / 3));   }   return a / 4; } 

The advantage of this method is that it's now a geometric series, so it's possible to calculate the sum directly, without any loop:

function getExperienceEstimate(level) {   let a = 50;   let r = 2 ** (1 / 3);   return a * (r**level - r) / (r - 1); }; 

getExperienceEstimate(50) returns 20011971.993575357, which is only 0.0015% smaller than getExperience(50).

Inverse function

According to Wolfram Alpha, here's the inverse function of getExperienceEstimate:

function getLevelEstimate(xp){   let a = 50;   let r = 2 ** (1 / 3);   return Math.log(xp * (r - 1) / a + r) / Math.log(r); }; 

With some minor precision loss, you can simplify it further:

function getLevelEstimate(xp){   return 4.328085 * Math.log(0.00519842 * xp + 1.259921045) }; 

It's only an estimate, but it works pretty well and doesn't require any loop!

Test

For 20012272 XP, the approximate inverse function returns 50.00006263463371, which should be a good starting point if you want to find the exact result.

function getExperience(level) {    let a = 0;    for (let x = 1; x < level; x += 1) {      a += Math.floor(x + (200 * (2 ** (x / 3))));    }    return Math.floor(a / 4);  }    function getLevelEstimate(xp){    return 4.328085 * Math.log(0.00519842 * xp + 1.259921045)  };    for (var i = 1; i < 100; i++) {    console.log(`Level ${i} (XP = ${getExperience(i)}). Estimated level : ${getLevelEstimate(getExperience(i))}`);  }
like image 119
Eric Duminil Avatar answered Sep 19 '22 05:09

Eric Duminil


You can use a binary search algorithm to avoid to loop over all possibilities.

Here is an example that I have adapted to your case.

You first need to create an array to map all your level => experience, this action should be done only ONCE, then you never have to do it again.

As you can see in my example, even with 1000 levels, you never have to iterate more than 9 times whatever level you are trying to find.

// You first have to create an array with all your levels.  // This has to be done only ONCE because it's an expensive one!  const list = [];  for (let i = 1; i <= 1000; i++) {    list[i] = getExperience(i);  }    function getExperience(level) {    let a = 0;    for (let x = 1; x < level; x += 1) {      a += Math.floor(x + (200 * (2 ** (x / 3))));    }      return Math.floor(a / 4);  }    function getLevel(value) {    // initial values for start, middle and end    let start = 0    let stop = list.length - 1    let middle = Math.floor((start + stop) / 2)    let iterations = 0;        // While the middle is not what we're looking for and the list does not have a single item.    while (list[middle] !== value && start < stop) {      iterations++;      if (value < list[middle]) {        stop = middle - 1      } else {        start = middle + 1      }        // Recalculate middle on every iteration.      middle = Math.floor((start + stop) / 2)    }        console.log(`${value} is Level ${middle} (Result found after ${iterations} iterations)`);    return middle;  }    // Then you can search your level according to the experience  getLevel(0);  getLevel(72);  getLevel(20010272);  getLevel(getExperience(50));  getLevel(33578608987644589722);
like image 34
Armel Avatar answered Sep 20 '22 05:09

Armel