簡體   English   中英

生成所有k的數字序列,該數字序列的第k位從左到右從右到右加到10

[英]Generate sequence of numbers whose k-th digit from the left and from the right sums to 10 for all k

Python編碼練習要求使函數f使得f(k)是第k個數字,以使所有k的左起和右起的第k個數字求和。 例如5, 19, 28, 37是序列中的前幾個數字。

我使用此函數來顯式檢查數字“ n”是否滿足該屬性:

def check(n):

    #even digit length
    if len(str(n)) % 2 == 0:

        #looping over positions and checking if sum is 10
        for i in range(1,int(len(str(n))/2) + 1):

            if int(str(n)[i-1]) + int(str(n)[-i]) != 10:

                return False

    #odd digit length
    else:

        #checking middle digit first
        if int(str(n)[int(len(str(n))/2)])*2 != 10:

            return False

        else:
            #looping over posotions and checking if sum is 10
            for i in range(1,int(len(str(n))/2) + 1):

                if int(str(n)[i-1]) + int(str(n)[-i]) != 10:

                    return False

    return True

然后遍歷所有數字以生成序列:

for i in range(1, 10**9):

    if check(i):
        print(i)

但是,練習需要一個函數f(i)在不到10秒的時間內返回第i個此類數字。 顯然,我的時間要長得多,因為它會在數字“ i”之前生成整個序列以進行計算。 是否可以使一個函數不必計算所有先前的數字?

測試每個自然數是一個不好的方法。 只有一小部分自然數具有此屬性,並且隨着我們進入更大的數,分數會迅速下降。 在我的機器上,下面的簡單Python程序花了3秒多的時間來找到第1000個數字(2,195,198),並花費了26秒的時間來找到了第2000個數字(15,519,559)。

# Slow algorithm, only shown for illustration purposes

# '1': '9', '2': '8', etc.
compl = {str(i): str(10-i) for i in range(1, 10)}

def is_good(n):
    # Does n have the property
    s = str(n)
    for i in range((len(s)+1)//2):
        if s[i] != compl.get(s[-i-1]):
            return False
    return True

# How many numbers to find before stopping
ct = 2 * 10**3

n = 5
while True:
    if is_good(n):
        ct -= 1
        if not ct:
            print(n)
            break
    n += 1

顯然,需要一種更有效的算法。

我們可以遍歷數字字符串的長度,並在其中生成帶有數字順序的屬性的數字。 偽代碼算法示意圖:

for length in [1 to open-ended]:
    if length is even, middle is '', else '5'
    half-len = floor(length / 2)
    for left in (all 1) to (all 9), half-len, without any 0 digits:
        right = 10's complement of left, reversed
        whole-number = left + middle + right

現在,請注意,可以很容易地計算出每個長度的數字計數:

Length    First    Last     Count
1         5        5        1
2         19       91       9
3         159      951      9
4         1199     9911     81
5         11599    99511    81

通常,如果左半部分有n位數字,則計數為9**n

因此,我們可以簡單地遍歷數字計數,對存在的解決方案進行計數而不必計算它們,直到我們找到包含所需答案的同類為止。 然后,再次計算所需的數字應該相對簡單,而不必遍歷所有可能性。

上面的草圖應該產生一些想法。 我寫完之后要遵循的代碼。

碼:

def find_nth_number(n):
    # First, skip cohorts until we reach the one with the answer
    digits = 1
    while True:
        half_len = digits // 2
        cohort_size = 9 ** half_len
        if cohort_size >= n:
            break
        n -= cohort_size
        digits += 1

    # Next, find correct number within cohort

    # Convert n to base 9, reversed
    base9 = []
    # Adjust n so first number is zero
    n -= 1
    while n:
        n, r = divmod(n, 9)
        base9.append(r)
    # Add zeros to get correct length
    base9.extend([0] * (half_len - len(base9)))
    # Construct number
    left = [i+1 for i in base9[::-1]]
    mid = [5] * (digits % 2)
    right = [9-i for i in base9]
    return ''.join(str(n) for n in left + mid + right)

n = 2 * 10**3

print(find_nth_number(n))

這是一個利用以下模式的函數:相鄰冪10之間的“有效”數字數量為9的冪。這使我們可以跳過很多數字。

def get_starting_point(k):
    i = 0
    while True:
        power = (i + 1) // 2
        start = 10 ** i
        subtract = 9 ** power
        if k >= subtract:
            k -= subtract
        else:
            break
        i += 1
    return k, start

我將其與您定義的方法結合在一起。 假設我們對第45個數字感興趣,這說明搜索從1000開始,我們只需要找到在1000之后出現的第26個“有效”數字。保證小於10000。當然,此界限會變得更糟,並且在規模上更差,您可能想使用本文中其他社區成員建議的技術。

k = 45
new_k, start = get_starting_point(k)
print('new_k: {}'.format(new_k))
print('start at: {}'.format(start))
ctr = 0
for i in range(start, 10**9):
    if check(i):
        ctr += 1
        if ctr == new_k:
            break
print(i)

輸出:

new_k: 26
start at: 1000
3827

看來第45個數字是3827。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM