假设我有这个公式,例如:
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。
发布于 2019-01-30 10:55:38
简短回答
您可以使用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;
}这种方法的优点是它现在是一个几何级数,因此可以不用任何循环计算和直接:
function getExperienceEstimate(level) {
let a = 50;
let r = 2 ** (1 / 3);
return a * (r**level - r) / (r - 1);
};getExperienceEstimate(50)返回比getExperience(50)小0.0015%的20011971.993575357。
逆函数
根据沃尔夫拉姆阿尔法,这是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))}`);
}
发布于 2019-01-30 07:20:04
您可以使用二进制搜索算法来避免循环所有可能性。
这里是一个我已经适应你的情况的例子。
首先需要创建一个数组来映射所有的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);
发布于 2019-01-30 06:55:13
一个蛮力(但不优雅)的解决方案是,只需调用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);
}
https://stackoverflow.com/questions/54434757
复制相似问题