Это кажется легким делом, но у меня возникают проблемы, чтобы решить эту проблему:
Я хотел бы рассчитать уровень на основе данных очков опыта (опыта). Поэтому я использую формулу кубического корня и округляю до следующего целого числа. Следующий уровень достигается, когда точно достигает опыта level^3
, Количество уровней не ограничено, поэтому я бы не использовал предварительно рассчитанную таблицу поиска.
Когда я использую стандартную PHP математику
floor( pow( 10648, 1/3))
Он возвращает 21 вместо 22. Это неверно, поскольку 21 ^ 3 дает 92161. Причина в том, что из-за ограниченной точности с плавающей точкой pow (10648, 1/3) возвращает не совсем 22, вместо этого он возвращает 21.9993112732.
Вы можете проверить это с помощью следующего фрагмента:
$lvl = pow( 10647, (float) 1 / 3);
print number_format( $lvl, 10);
Это мой обходной путь. Но я не уверен, что это пуленепробиваемый
public static function getLevel($exp) {
$lvl = floor(pow($exp, (float) 1 / 3)); // calculate the level
if (pow($lvl + 1, 3) == $exp) { // make check
$lvl++; // correct
}
return $lvl;
}
Кроме того, это выглядит немного хрупким, когда дело доходит до проверки. Таким образом, остается вопрос:
Существует ли надежный, эффективный и пуленепробиваемый способ вычисления кубического корня (из положительных чисел).
Благодарю.
Я думаю, что это единственная модификация, в которой нуждается ваш код:
public static function getLevel($exp) {
$lvl = floor(pow($exp, (float) 1 / 3));
if (pow($lvl + 1, 3) <= $exp) { // compare with <= instead of ==
$lvl++;
}
return $lvl;
}
Если вам нужны 100% надежные результаты, вы, вероятно, должны использовать Библиотека GMP для вычислений произвольной точности.
gmp_root
Функция должна делать то, что вам нужно. Вам понадобится PHP версии 5.6 или новее с включенным расширением GMP.
$num = gmp_init(10648);
$third_root = gmp_root($num, 3);
var_dump(gmp_strval($third_root)); // string(2) "22"
Если библиотека GMP неудобна для вас, и вы уверены, что у вашего номера есть целочисленный корень, вы можете попробовать следующее:
function getLevel($base, $root = 3.0) {
$exact = pow($base, 1.0 / $root);
$ceil = ceil($exact);
$floor = floor($exact);
if (pow($exact, $root) == $base) { return $exact; }
if (pow($ceil, $root) == $base) { return $ceil; }
if (pow($floor, $root) == $base) { return $floor; }
// Default: no integer root
return FALSE;
}
Он проверяет точное, floor
, а также ceil
значения результата, чтобы найти, который является правильным ответом. Если это не один из трех, то число не имеет целочисленного корня и по умолчанию FALSE
,
var_dump(getLevel(10648, 3)); // 22^3 => float(22)
var_dump(getLevel(16807, 5)); // 7^5 => float(7)
var_dump(getLevel(1, 3)); // Should always return 1 => float(1)
var_dump(getLevel(1, 99)); // Should always return 1 => float(1)
var_dump(getLevel(7)); // Has no integer 3rd root => bool(false)
Конечно, вы можете сделать функцию return $floor;
или же return $ceil;
как случай по умолчанию, но это зависит от вас.