假设我有这个公式,例如:
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
。
答案 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));