2

Нужно, чтобы программа проверяла является ли число точным кубом. Я написал так:

import math

n=int(input())
if (pow(n,1/3)).is_integer():
    print("yes")
else:
    print("no")

Но когда ввожу число 8 - все верно пишет "yes". Но если ввести 125, 216 и другие точные кубы, то почему-то выдает -"no". В чем ошибка?

Пробовал вместо pow(n,1/3) использовать n**(1/3) - та же проблема.

Kromster
  • 13,809
starter
  • 23

2 Answers2

3

python, как и большинство других языков, не отличит число 1/3 от числа достаточно близкого к нему

>>> 1/3 == 0.33333333333333331234512345
True

Точность вычислений с плавающей точкой ограничена, поэтому все сравнения дробных чисел нужно производить с учетом погрешности, например так.

if round(pow(n, 1/3), 6).is_integer(): # сравнение с точностью до 0.000001

Правда, стоит сказать, что от всех проблем это не убережет: если числа достаточно большие, потеря точности проявится значительно сильнее.

>>> round((123123123**3) ** (1/3), 6)
123123123.0
>>> round((1231231231**3) ** (1/3), 6)
1231231230.999999
>>> 100000000000000000.0 == 99999999999999999.0
True

Upd:

Целочисленный алгоритм нахождения кубического корня (с отбрасыванием дробной части)

def intcuberoot(n):
    if n < 0:
        return -intcuberoot(-n)
    elif n < 2:
        return n
    else:
        x = intcuberoot(n >> 3) << 1
        return x if (x + 1) ** 3 > n else x + 1

def iscube(n): return intcuberoot(n) ** 3 == n

У него тоже есть свои ограничения - он не особо оптимальный да и упрется в потолок рекурсии на огромных числах, но явная ошибка лучше чем неточный результат.

>>> iscube(555**300)
True

Upd2

Итеративный алгоритм (без ограничений на размер числа)

def intcuberoot2(n):
    if n < 0:
        return -intcuberoot2(-n)
x = 0

for bit in range((n.bit_length() + 2) // 3, -1, -1):
    if (x | 1 &lt;&lt; bit) ** 3 &lt;= n:
        x |= 1 &lt;&lt; bit

return x

extrn
  • 10,941
  • добавлю, что условие round(n**1./3),2)**3 == n будет давать правильный результат для всего диапазона... ЗЫ: связаный вопрос – Fat-Zer Sep 10 '19 at 00:24
  • @Fat-Zer смотря что называть диапазоном :) 2**150 входит в диапазон чисел двойной точности (даже записывается без потерь). – extrn Sep 10 '19 at 01:52
  • да... что-то я очитался — мне казалось, что ограничение для n до 10⁹ – Fat-Zer Sep 10 '19 at 10:49
  • Значит лучший вариант - это просто перебирать числа меньшие заданного числа? Если куб совпадет - ответ да.. – starter Sep 10 '19 at 11:21
  • @starter Мой ответ был больше не о том, как правильно, а о том, почему не сработал ваш вариант. Перебирать полностью конечно не нужно, но чтобы гарантированно получить верный результат, лучше не переходить на вычисления с плавающей точкой (обновил ответ). Fat-Zer и Harry предложили хороший компромисс между точностью и простотой вычислений. – extrn Sep 10 '19 at 17:04
3

Я бы действовал так - округлял кубический корень до ближайшего целого, и проверял, совпадает ли его куб с исходным числом.

Если не ошибаюсь, на Python'е это выглядит так:

n = int(input())
c = round(pow(n,1/3))

if c*c*c == n:
    print("yes")
else:
    print("no")

Понятно, что при больших значениях начнет играть роль погрешность вычисления кубического корня...

Harry
  • 221,325