Как найти целые n-ые корни?
Я хочу найти наибольшее целое число, меньшее или равное корню kth из n. Я попробовал
int(n**(1/k))
Но для n = 125, k = 3 это дает неправильный ответ! Я знаю, что 5 кубов 125.
>>> int(125**(1/3))
4
Какой лучший алгоритм?
Справочная информация. В 2011 году этот проскальзык стоил мне избиения Google Code Jam. https://code.google.com/codejam/contest/dashboard?c=1150486#s=p2
Ответы
Ответ 1
Одно решение сначала скопирует ответ между lo и hi, повторно умножая hi на 2, пока n не будет между lo и hi, а затем использует двоичный поиск для вычисления точного ответа:
def iroot(k, n):
hi = 1
while pow(hi, k) < n:
hi *= 2
lo = hi / 2
while hi - lo > 1:
mid = (lo + hi) // 2
midToK = pow(mid, k)
if midToK < n:
lo = mid
elif n < midToK:
hi = mid
else:
return mid
if pow(hi, k) == n:
return hi
else:
return lo
В другом решении используется метод Ньютона, который отлично работает на целых числах:
def iroot(k, n):
u, s = n, n+1
while u < s:
s = u
t = (k-1) * s + n // pow(s, k-1)
u = t // k
return s
Ответ 2
Как насчет:
def nth_root(val, n):
ret = int(val**(1./n))
return ret + 1 if (ret + 1) ** n == val else ret
print nth_root(124, 3)
print nth_root(125, 3)
print nth_root(126, 3)
print nth_root(1, 100)
Здесь ожидается, что как val
, так и n
будут целыми и положительными. Это делает выражение return
основано исключительно на целочисленной арифметике, исключая любую вероятность ошибок округления.
Обратите внимание, что точность гарантируется только тогда, когда val**(1./n)
достаточно мала. Как только результат этого выражения отклонится от истинного ответа более чем на 1
, метод больше не даст правильный ответ (он даст тот же приблизительный ответ, что и исходная версия).
Тем не менее мне интересно, почему int(125**(1/3))
есть 4
In [1]: '%.20f' % 125**(1./3)
Out[1]: '4.99999999999999911182'
int()
обрезает это значение до 4
.
Ответ 3
Мое осторожное решение после столь плохого горения:
def nth_root(N,k):
"""Return greatest integer x such that x**k <= N"""
x = int(N**(1/k))
while (x+1)**k <= N:
x += 1
while x**k > N:
x -= 1
return x
Ответ 4
Почему бы не попробовать это:
125 ** (1 / float(3))
или
pow(125, 1 / float(3))
Он возвращает 5.0, поэтому вы можете использовать int(), чтобы преобразовать в int.
Ответ 5
Здесь он находится в Lua, используя метод Ньютона-Рафсона
> function nthroot (x, n) local r = 1; for i = 1, 16 do r = (((n - 1) * r) + x / (r ^ (n - 1))) / n end return r end
> return nthroot(125,3)
5
>
Версия Python
>>> def nthroot (x, n):
... r = 1
... for i in range(16):
... r = (((n - 1) * r) + x / (r ** (n - 1))) / n
... return r
...
>>> nthroot(125,3)
5
>>>
Ответ 6
Интересно, если начать с метода, основанного на логарифмах, может помочь вывести источники ошибок округления. Например:
import math
def power_floor(n, k):
return int(math.exp(1.0 / k * math.log(n)))
def nth_root(val, n):
ret = int(val**(1./n))
return ret + 1 if (ret + 1) ** n == val else ret
cases = [
(124, 3),
(125, 3),
(126, 3),
(1, 100),
]
for n, k in cases:
print "{0:d} vs {1:d}".format(nth_root(n, k), power_floor(n, k))
выводит
4 vs 4
5 vs 5
5 vs 5
1 vs 1
Ответ 7
def nth_root(n, k):
x = n**(1./k)
y = int(x)
return y + 1 if y != x else y
Ответ 8
int(125**(1/3))
должно быть четным, т.е. правильным ответом, поэтому это должна быть стандартная ошибка округления окружения, то есть внутренне результат равен 4.9999999999, который округляется до 4. Эта проблема будет существовать с любым алгоритмом, который вы используете. Одно простое решение - добавить небольшое число, например. int((125**(1/3)) + 0.00000001)
Ответ 9
Вы можете округлить до ближайшего целого числа вместо округления/до нуля (я не знаю, что указывает Python):
def rtn (x):
return int (x + 0.5)
>>> rtn (125 ** (1/3))
5
Ответ 10
Сделайте это перед всем:
from __future__ import division
а затем запустите любой из указанных выше методов, чтобы получить ваши результаты.