1. 程式人生 > 實用技巧 >2020-09-13:判斷一個正整數是a的b次方,a和b是整數,並且大於等於2,如何求解?

2020-09-13:判斷一個正整數是a的b次方,a和b是整數,並且大於等於2,如何求解?

福哥答案2020-09-13:

首先確定b的範圍,b的範圍一定在[2,logN]裡。然後遍歷b,求a的範圍,如果範圍長度等於0,說明這個正整數是a的b次方。
1.遍歷b範圍。二分法求a,a初始範圍是[2,logN]。2的400次方耗時5秒。【有程式碼】
2.遍歷b範圍。優化二分法求a,a初始範圍是[2,上一次a的結果]。2的10000次方耗時5秒。【有程式碼】
3.應該有更優化的方案,暫時沒想到。【無程式碼】

因為用到了大整數,所以用python語言編寫。程式碼如下:

#!/usr/bin/python3
import time
from functools import wraps
def _get_sqrt_range(num, right, exp=2):
    """
        求num的exp開方,exp是指數,num是結果。求底數。
        Args:
            num: 大於等於0並且是整數。
            right: 大於等於0並且是整數。右邊界。
            exp: 大於等於0並且是整數。
        Returns:
            返回元組,表示一個開方範圍。
        Raises:
            IOError: 無錯誤。
    """
    left = 1
    if num == 0:
        return 0, 0
    if num == 1:
        return 1, 1
    if num == 2 or num == 3:
        return 1, 2
    while True:
        mid = (left + right) // 2
        if mid ** exp > num:
            right = mid
            if left ** exp == num:
                return left, left
            if left + 1 == right:
                return left, right
        elif mid ** exp < num:
            left = mid
            if right ** exp == num:
                return right, right
            if left + 1 == right:
                return left, right
            if mid == 1:
                return 1, 2
        else:
            return mid, mid


def get_log_range(num, basenum):
    """
        求對數範圍。
        Args:
            num: 數,大於等於1並且是整數。
            basenum: 底數,大於等於2並且是整數。
        Returns:
            返回結果。對數範圍。
        Raises:
            IOError: 無錯誤。
    """
    if num == 1:
        return 0, 0
    else:
        n = 0
        ism = 0
        while num >= basenum:
            if ism == 0 and num % basenum != 0:
                ism = 1
            n += 1
            num //= basenum
        return n, n + ism

def timefn(fn):
    """計算效能的修飾器"""
    @wraps(fn)
    def measure_time(*args, **kwargs):
        t1 = time.time()
        result = fn(*args, **kwargs)
        t2 = time.time()
        print(f"@timefn: {fn.__name__} took {t2 - t1: .5f} s")
        return result
    return measure_time

@timefn
def is_power1(num):
    """
        判斷n是否是一個數的冪次方形式。
        Args:
            num: 大於等於0並且是整數。
        Returns:
            返回結果。true是冪數
        Raises:
            IOError: 無錯誤。
    """
    if num <= 3:
        return False
    else:
        log_range = get_log_range(num, 2)
        if log_range[0] == log_range[1]:
            return True
        expmax = log_range[0]
        expmin = 2
        exp = expmin
        sqrt = 0
        right = 2 ** (1 + log_range[0] // 2)
        while exp <= expmax:
            sqrt = _get_sqrt_range(num, right, exp)
            # right = sqrt[0]#縮小右邊界範圍
            if sqrt[0] == sqrt[1]:
                return True
            if sqrt == (1, 2):
                return False
            exp += 1
        return False

@timefn
def is_power2(num):
    """
        判斷n是否是一個數的冪次方形式。
        Args:
            num: 大於等於0並且是整數。
        Returns:
            返回結果。true是冪數
        Raises:
            IOError: 無錯誤。
    """
    if num <= 3:
        return False
    else:
        log_range = get_log_range(num, 2)
        if log_range[0] == log_range[1]:
            return True
        expmax = log_range[0]
        expmin = 2
        exp = expmin
        sqrt = 0
        right = 2 ** (1 + log_range[0] // 2)
        while exp <= expmax:
            sqrt = _get_sqrt_range(num, right, exp)
            right = sqrt[0]  # 縮小右邊界範圍
            if sqrt[0] == sqrt[1]:
                return True
            if sqrt == (1, 2):
                return False
            exp += 1
        return False


if __name__ == "__main__":
    print("----2的400次方")
    num = 2 ** 400 + 1
    print(is_power1(num))
    print(is_power2(num))
    print("\r\n----2的10000次方")
    num = 2 ** 10000 + 1
    print(is_power2(num))

  

執行程式碼結果如下:


***
[評論](https://user.qzone.qq.com/3182319461/blog/1599951713)