python倒置数字拼图。需要有效率的帮助

时间:2014-12-11 20:41:29

标签: python performance algorithm math python-3.x

颠倒的数字定义为:

颠倒的数字是i的整数 左边的数字加上i 右边的数字是 总是等于10.例如13579是一个颠倒的数字,因为1 + 9 = 10,3 + 7 = 10和(从那以后) 5是左起第三位和右起5 + 5 = 10。 按数字顺序排列的前几个倒数字是5,19,28,37,......,82,91,159 ......

任务

编写程序以确定第n个倒置数字(按数字顺序)。 输入将由单个整数n(1

我的代码

def upsidecheck(tocheck):
    intolist=list(map(int, str(tocheck)))
    x=0
    while x<(len(intolist)/2):
        if (intolist[x]+intolist[len(intolist)-x-1])!= 10 :
            return False
            break
        x+=1
    return True
print("which nth upsidedownnumber do you want?")
nth=int(input())
y=0
answer=0
for x in range (1,(2**31)):
    counter=upsidecheck(x)
    if counter == True:y+=1
    if y==nth:answer=x;break
print("the answeris",answer)

对于小于100的数字,代码是正常的,但是对于与'1234'一样大的数字,它需要在两秒内运行,这应该产生'4995116'的答案。

它确实有效但只需要太长时间(通常约30秒)。它需要在2秒内完成;(

提前感谢您的帮助。

注意:这不是考试/家庭作业等,只是为了帮助我准备考试

4 个答案:

答案 0 :(得分:1)

首先,我们需要一个函数来告诉我们哪些数字是颠倒的:

def is_ud(n):
    digits = [int(ch) for ch in str(n)]
    check = (len(digits) + 1) // 2
    return all(digits[i] + digits[-1 - i] == 10 for i in range(check))

然后让我们生成一些值并寻找模式:

ud = [i for i in range(10000000) if is_ud(i)]

for digits in range(1, 8):
    lo, hi = 10 ** (digits - 1), (10 ** digits) - 1
    answers = sum(lo <= n <= hi for n in ud)
    print("{}: {}".format(digits, answers))

给出了

1: 1
2: 9
3: 9
4: 81
5: 81
6: 729
7: 729

因此有81个4位数解决方案和729个6位数解决方案;这应该是有道理的,因为6位数的解决方案看起来像&#34; 1&#34; +(每个4位数解决方案)+&#34; 9&#34;,&#34; 2&#34; +(每个4位数解决方案)+&#34; 8&#34;,...&#34; 9&#34; +(每个4位数解决方案)+&#34; 1&#34; - 因此,每4位数解决方案有9个6位数解决方案(如果以这种方式生成它们,您将按升序生成它们)。同样,对于每个4位数的解决方案,通过在中间插入5来解决相应的5位数解决方案。

查看此表,您现在应该可以看到,如果您想要(例如)第200个解决方案,它必须有6位数字;事实上,它必须是第19个6位数的解决方案。不止于此,因为19&lt; 81,它必须看起来像&#34; 1&#34; +第19个4位数解决方案+&#34; 9&#34;!

您现在拥有编写递归解决方案以直接生成第N个倒置数字所需的一切。祝你好运!

答案 1 :(得分:0)

由于这里的所有数字强制执行不是一个选项,因此您需要先解决数学问题:

  1. 按升序生成“颠倒”数字
  2. 如果可能的话,确定有多少“倒置”数字有一定长度,因为你的索引上限非常高,甚至蛮力生成。
  3. 对于第一个,“倒置”数字是:

    • 长度 2n a 1 ... a n a' n .. .A'<子> 1
    • 长度 2n + 1 a 1 ... a n 5a' n ...一个“<子> 1
      其中 a i 是除0以外的任何数字而 a' i 是其10 -complement。

    由于相同长度的数字是逐位比较的,因此猜测哪个顺序是升序。

    第二,

    • 长度 2n 2n + 1 的“倒置”数字的数量都是 a1的所有可能组合的数量...一个
    • 由于上一个表达式非常简单,您甚至可以计算总和的公式 - 所有“倒置”数字的数量,最长为N

    其余部分非常简单,我只想补充一点divmod//可能会为最终算法派上用场。

答案 2 :(得分:0)

这是一个有趣的问题。正如其他人所说,第一部分是你想要任意数量的数字的第n个数字,所以如果你能找到数字较少的数字的总数,你可以从数值中减去它们并忽略它们。

然后你有一个更简单的问题:找到恰好有k位数的第n个值。如果k是奇数,则中心数字是&#39; 5&#39;,否则前半部分只是第n个基数9,但是数字表示在1..9的范围内。尾部只是相同的基数9值,数字反转,使用范围9..1表示值0..8。

此函数会将值转换为基数9,但具有用于表示每个数字的特定字符:

def base9(n, size, digits):
    result = []
    for i in range(size):
        result.append(n%9)
        n //= 9
    return ''.join(digits[i] for i in reversed(result))

例如:

>>> print(base9(20, 3, "abcdefghi"))
acc

现在要打印第n个倒置号码,我们将k个数字准确转换为基数9并插入&#39; 5&#39;如果需要的话。

def ud_fixed(n, k):
    """Upside down number of exactly k digits
    0 => 1..159..9
    1 => 1..258..9
    and so on
    """
    ln = k // 2
    left = base9(n, ln, "123456789")
    right = base9(n, ln, "987654321")[::-1]
    if k%2:
        return left + "5" + right
    else:
        return left + right

现在我们需要做的就是计算有多少更短的结果并忽略它们:

def upside_down(n):
    number = [1, 9]
    total = [1, 10]
    if n==1: return "5"
    while total[-1] < n:
        number.append(9*number[-2])
        total.append(total[-1]+number[-1])
    length = len(total)
    if length >= 2:
        n -= total[length-2]  # Ignore all the shorter results.
    return ud_fixed(n-1, length)

打印一些值以进行检查:

if __name__=='__main__':
    for i in range(1, 21):
        print(i, upside_down(i))
    print(1234, upside_down(1234))

输出如下:

C:\Temp>u2d.py
1 5
2 19
3 28
4 37
5 46
6 55
7 64
8 73
9 82
10 91
11 159
12 258
13 357
14 456
15 555
16 654
17 753
18 852
19 951
20 1199
1234 4995116

答案 3 :(得分:0)

颠倒的数字

代码位于一个模块中,OP可以从中导入ud函数。

% cat upside.py
# module variables

# a table with the values of n for which we have the greatest
# UD number of i digits 
_t = [0, 1]
for i in range(1,11): last = _t[-1] ; d=9**i ; _t+=[last+d,last+d+d]

# a string with valid digits in our base 9 conversion
_d = '123456789'

def _b9(n,l):
    # base 9 conversion using the numbers from 1 to 9
    # note that we need a zero padding 
    s9 = []
    while n : s9.append(_d[n%9]) ; n = n//9
    while len(s9)<l: s9.append(_d[0]) # ZERO PADDING!
    s9.reverse()
    return ''.join(s9)

def _r9(s):
    # reverse the first half of our UD number
    return ''.join(str(10-int(c)) for c in s[::-1])

def ud(n):
    # Error conditions
    if n<1:raise ValueError, 'U-D numbers count starts from 1, current index is %d.'%(n,)
    if n>_t[-1]:raise ValueError, 'n=%d is too large, current max is %d.'%(n,_t[-1])

    # find length of the UD number searching in table _t
    for i, j in enumerate(_t):
        if n<=j: break

    # the sequence number of n in all the OD numbers of length i
    # note that to apply base9 conversion we have to count from 0,
    # hence the final -1
    dn = n-_t[i-1]-1

    # now we compute the "shifted base 9" representation of the ordinal dn,
    # taking into account the possible need for padding to a length of i//2
    a = _b9(dn,i//2)

    # we compute the string representing the OD number by using _r9 for
    # the second part of the  number and adding a '5' iff i is odd,
    # we convert to int, done
    return int(a+('5' if i%2 else '')+_r9(a))
% 

使用示例

这是一个使用示例

% python
Python 2.7.8 (default, Oct 18 2014, 12:50:18) 
[GCC 4.9.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from upside import ud
>>> ud(1234)
4995116
>>> ud(7845264901)
999999999951111111111L
>>> ud(7845264902)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "upside.py", line 15, in ud
    if n>_t[-1]:raise ValueError, 'n=%d is too large, current max is %d.'%(n,_t[-1])
ValueError: n=7845264902 is too large, current max is 7845264901.
>>> exit()
% 

一句小话

除了评论和错误处理之外,ud(n)非常短......

def ud(n):
    for i, j in enumerate(_t):
        if n<=j: break
    dn = n-_t[i-1]-1
    a = _b9(dn,i//2)
    return int(a+('5' if i%2 else '')+_r9(a))
相关问题