逆转方程式-JavaScript

时间:2019-01-30 06:47:57

标签: 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)}`);
}

要获得50级所需的经验,您可以这样做:getExperience(50)

但是,您将如何扭转这种情况并获得经验所需的水平?因此,getLevel(20010272)将输出50

5 个答案:

答案 0 :(得分:42)

简短回答

您可以使用4.328085 * Math.log(0.00519842 * xp + 1.259921045)作为相应级别的很好近似。

如果需要一个确切的值,可以遍历所有级别,直到找到所需的范围为止,如answer所示。

长答案

功能稍作修改

我认为不可能找到与此函数相反的确切closed-form expression。不过,如果稍微修改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;
}

此方法的优点在于它现在是geometric series,因此可以计算总和directly,而无需任何循环:

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

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

反函数

根据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);
};

有一些小的精度损失,您可以进一步简化它:

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

这只是一个估计,但效果很好,不需要任何循环!

测试

对于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))}`);
}

答案 1 :(得分:12)

您可以使用binary search algorithm来避免遍历所有可能性。

Here是我适应您情况的一个例子。

您首先需要创建一个数组来映射所有level => experience,此操作只应执行一次,然后就不必再执行了。

在我的示例中可以看到,即使具有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);

答案 2 :(得分: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);
}

答案 3 :(得分:2)

您可以使用binary search更快地找到水平值-最多7个步骤。

(虽然我怀疑长度100列表的增益是否显着)

答案 4 :(得分:1)

二进制搜索思路可以如下使用。请注意,以下假设最多有100个级别。您可以根据需要将其更改为1000、10000或100000。

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(exp) {
    let min = 1,
        max = 100;
    while (min <= max) {
        let mid = Math.floor((min + max) / 2),
            expm = getExperience(mid),
            expn = getExperience(mid + 1);
        if (expm <= exp && exp < expn) {
            return mid;
        }
        if (expm < exp) {
            min = mid + 1;
        } else {
            max = mid - 1;
        }
    }
    return null;
}

console.log(getLevel(getExperience(17)));
console.log(getLevel(getExperience(17) - 1));
console.log(getLevel(getExperience(100)));
console.log(getLevel(getExperience(100) - 1));