LoginSignup
0
0

More than 1 year has passed since last update.

小道具:正整数を累乗表現(底,指数)にする

Last updated at Posted at 2022-08-30

入力値(正整数)が累乗数ならば底と指数を求めます。累乗数でなければ指数は $1$ とします。

入力値が大きいと、pow で累乗根を求めるときに float 型変換で失敗するため、int 型で求める処理を追加しています。(もっと良い法方があるかと思います)

# 平方根(Python 3.8 以上は組み込みを使用)
try:
    from math import isqrt

    def isqrtex(x: int) -> tuple:
        q = isqrt(x)
        return (q, x - q * q)

except:

    def isqrtex(x: int) -> tuple:
        l, r, s = x.bit_length() >> 1, 0, 0
        for b in range(l, -1, -1):
            r <<= 1
            t = 1 << b
            u = s + t
            v = u << b
            if x < v:
                continue
            x -= v
            r |= 1
            s = u + t
        return (r, x)

    def isqrt(x: int) -> int:
        return isqrtex(x)[0]


# float 型指数部の範囲
perfect_power_float_bits = 0
try:
    for b in range(8, 24):
        s = 1 << b
        float(1 << (s - 1))
        perfect_power_float_bits = s
except:
    pass

# 以下をコメントアウトすると float 型の処理をスキップします
# perfect_power_float_bits = -1   # float型を使わない


# 累乗数表現を求める
def perfect_power(x: int) -> tuple:
    x = abs(x)
    if x < 4:
        return (x, 1)

    # float 型が使用可能か?
    b = x.bit_length()
    if b <= perfect_power_float_bits:
        for e in range(b, 1, -1):
            m = int(x ** (1 / e))
            if x == (m ** e):
                return (m, e)
            m += 1
            if x == (m ** e):
                return (m, e)
        return (x, 1)

    # 指数 e に対する底を探す関数
    def find(x, e):
        s = 1 << (((x.bit_length() + e - 1) // e) - 1)
        n = 1
        while s > 1:
            m = n + s
            t = x - (m ** e)
            if t == 0:
                return (m, e)
            if t > 0:
                n = m
            s >>= 1
        return (x, 1)

    # float 型が使用できない場合
    w = 0
    if (x & 1) == 0:      # 偶数の冪乗か?
        # x = (2m)^e = 2^e m^e
        e = (x & -x).bit_length() - 1
        if e == 1:        # 奇数の 2 倍か?
            return (x, 1)
        m = x >> e
        if m == 1:        # 2 の冪乗か?
            return (2, e)
        x = m
        w = e

    # x = (m^e)^(s=2^k)
    s = 1
    while True:
        q, r = isqrtex(x)
        if r != 0:
            break
        s <<= 1
        x = q

    b = x.bit_length()
    z = b * 1024 // 1623  # Log2(3) ≒ 1.5849625
    for k in range((z | 1), 2, -2):
        m, e = find(x, k)
        if e != 1:
            x = m
            s *= e
            break

    if w == 0:
        return (x, s)
    if w == s:
        return (x << 1, s)

    u, v = w, s
    while v:
        u, v = v, u % v
    return ((1 << (w // u)) * (x ** (s // u)), u)
実行結果
>>> perfect_power(1)
(1, 1)
>>> perfect_power(2)
(2, 1)
>>> perfect_power(3)
(3, 1)
>>> perfect_power(4)
(2, 2)
>>> perfect_power(5)
(5, 1)
>>> perfect_power(6)
(6, 1)
>>> perfect_power(7)
(7, 1)
>>> perfect_power(8)
(2, 3)
>>> perfect_power(9)
(3, 2)
>>> perfect_power(26)
(26, 1)
>>> perfect_power(27)
(3, 3)
>>> perfect_power(28)
(28, 1)
>>> x = 123**321
>>> x
72367033806371673149109894141163778628811792657571658906010558390395870363798401744095280686155507736404921657070284961721828960592977909542637098897697223102622628566787654091327825453991595140205701412961364188732408936197890553699715836951569999800431957769217006743321026257517932764164662319487914962533302741368207211189494615326552790667720411285474162636765168907211924134973374304496019635376665858559941735703924836467756917247995469583487467791524582153744522107597865277798136080074161485280424274076931083994487111719562249702540362855712911132265966235754355353516703339043001506118520760359577737869472018617942120590873170710805078696371738906375721785723
>>> perfect_power(x)
(123, 321)
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0