得到一个等式 - JavaScript

Gre*_*Mtn 24 javascript math

假设我有这个公式,例如:

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)}`);
}
Run Code Online (Sandbox Code Playgroud)

要获得50级所需的经验,你可以:getExperience(50).

但是,你会如何扭转这种局面并获得经验所需的水平?所以,getLevel(20010272)会输出50.

Eri*_*nil 42

简短的回答

您可以使用4.328085 * Math.log(0.00519842 * xp + 1.259921045)相应级别的非常好的近似值.

如果您需要精确值,则可以迭代所有级别,直到找到所需的范围,如此答案中所示.

答案很长

略有修改的功能

我认为不可能为此函数的反函数找到精确的闭合形式表达式.但是,如果你修改getExperience(level)一下,应该可以.

  • 首先,你可以注意到它的x增长速度慢得多2 ** (x / 3).
  • 那么,Math.floor对大数字的影响不大.

所以让我们删除它们!这是稍微修改过的功能:

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

这种方法的优点是它现在是一个几何级数,所以可以直接计算总和,而不需要任何循环:

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

getExperienceEstimate(50)返回20011971.993575357,仅比0.0015%小getExperience(50).

反函数

根据Wolfram Alpha的说法,这是反函数getExperienceEstimate:

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

由于精度损失较小,您可以进一步简化:

function getLevelEstimate(xp){
  return 4.328085 * Math.log(0.00519842 * xp + 1.259921045)
};
Run Code Online (Sandbox Code Playgroud)

这只是一个估计,但它运作良好,不需要任何循环!

测试

对于20012272 XP,近似反函数返回50.00006263463371,如果要查找确切结果,这应该是一个很好的起点.

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))}`);
}
Run Code Online (Sandbox Code Playgroud)

  • 这是我想要的解决方案.在我的游戏中,我确保通过对log和pow使用相同的指数,我可以直接反转我的关卡公式.如果可能的话,我会建议OP尽量减少对近似的担忧,而不是让它完全匹配. (4认同)

Arm*_*mel 12

您可以使用二进制搜索算法来避免遍历所有可能性.

是我适应你案例的一个例子.

你首先需要创建一个数组来映射你的所有level => experience,这个动作应该只在ONCE完成,然后你再也不用了.

正如您在我的示例中所看到的,即使有1000个级别,您也不必迭代超过您想要查找的级别的9倍.

// 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);
Run Code Online (Sandbox Code Playgroud)

  • 你**不要**建立所有经验的清单. (4认同)

Cer*_*nce 11

一个蛮力(但不优雅)的解决方案就是调用getExperience级别,直到达到需要比传递的更多经验的级别exp:

function getLevel(exp) {
  if (exp === 0) return 0;
  let level = 0;
  let calcExp = 0;
  while (exp > calcExp) {
    calcExp = getExperience(level);
    if (calcExp > exp) break;
    level++;
  }
  return level - 1;
}

console.log(getLevel(20012272)); // experience required for 50 on the dot
console.log(getLevel(20012270));
console.log(getLevel(20012274));
console.log(getLevel(0));

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);
}
Run Code Online (Sandbox Code Playgroud)

  • 当然,您可以预先计算所有值并将它们放在查找表中.我怀疑XP要求或级别会一直在变化,因此每次动态计算它们似乎都是一种过度杀伤力. (6认同)
  • 如果你也知道最低/最高等级,你可以进行伪二分搜索(即如果你知道有100个等级,你看你的经验是否小于或高于50级,那么25/75取决于那个,然后12/38/62/88等).或者在这些步骤之间的某个地方(一分为二,直到你有3个选项,然后检查所有这些). (4认同)