Lychrel数算法

时间:2015-09-01 15:47:50

标签: python algorithm lychrel-numbers

最近对将Lychrel和回文数字作为休闲数学的搜索着迷。

对于不知道的人,手动执行此检查的过程如下:

  1. 设x为某个数字。
  2. 令R(x)为与反向写入的x对应的数字。
  3. 设n = x + R(x)
  4. 如果n == R(n),则返回True,否则False
  5. 重复n作为新的x,直至获得True

    有没有办法在Python中实现自动化?在哪里我可以输入一个数字,它会告诉我它的反向总和是否是回文。另外,我想知道达到这个数字需要多少步骤。

    示例:

    设x为79. 79 + 97为176,这不是回文,所以我们得到False

    设x现在是176. 176 + 671是847,这不是回文,所以得到False

    我们继续:

    • 847 + 748 == 1595
    • 1595 + 5951 == 7546
    • 7546 + 6457 == 14003
    • 14003 + 30041 = 44044

    这是我们最终遇到回文的地方。它花了6个步骤。

4 个答案:

答案 0 :(得分:3)

首先,定义两个便利功能(你可以自己做!):

def is_palindrome(number):
    """Whether the number is a palindrome."""
    raise NotImplementedError

def reverse(number):
    """The number reversed, e.g. 79 -> 97."""
    raise NotImplementedError

然后我们可以创建一个generator来生成您描述的一系列数字:

def process(number):
    """Create the required series of numbers."""
    while True:
        yield number
        if is_palindrome(number):
            break
        number += reverse(number)

例如:

>>> list(process(79))
[79, 176, 847, 1595, 7546, 14003, 44044]
# 0    1    2     3     4      5      6

确定一个数字是否是一个Lychrel数字是比较棘手的 - 显然,当它不是时,我们的生成器用尽了,这是微不足道的:

def is_lychrel(number):
    """Whether the number is a Lychrel number."""
    for _ in process(number):
        pass
    return False

你可以测试我们是否重复一个数字(如果有一个循环,它不能到达回文):

def is_lychrel(number):
    """Whether the number is a Lychrel number."""
    seen = set()
    for num in process(number):
        if num in seen:
            return True
        seen.update((num, reverse(num)))  # thanks @ReblochonMasque
    return False

但是否则会一直持续到内存不足为止!

答案 1 :(得分:2)

关于Mathematica中链长分布的一个小实验:

f[n_] := NestWhileList[# + FromDigits@k &, n,
                       (# != (k = Reverse@#)) &@IntegerDigits[#] & ] // Length

(* remove known lychrel candidates *)
list = f /@  Complement[Range@1000, {196, 295, 394, 493, 592, 689, 691, 788, 790, 879, 
                                     887, 978, 986}];

Histogram@list

Mathematica graphics

直到3700:

Mathematica graphics

答案 2 :(得分:2)

首先,我们有一个反转数字位数的函数:

def rev(n, r=0):
    if n == 0: return r
    return rev(n // 10, r*10 + n%10)

然后我们可以用它来确定一个数字是否是Lychrel数;在这里,我们返回反驳该数字的Lychrel-ness的链,或单个列表以表明该数字是Lychrel:

def lychrel(n, bound=1000):
    r = rev(n); chain = [n]
    while bound > 0:
        n += r; r = rev(n)
        chain.append(n)
        if n == r: return chain
        bound -= 1
    return [chain[0]]

以下是一些例子:

>>> lychrel(196)
[196]
>>> lychrel(281)
[281, 463, 827, 1555, 7106, 13123, 45254]

您可以在my blog了解有关Lychrel数字的更多信息。

编辑:在被Tony Suffolk 66挑战之后,我做了我向他提出的计时测试。您可以在 http://ideone.com/5gTbSH看到它们。我只使用整数的递归函数比转换为字符串并返回的函数快约30%。更快仍然是仅使用整数的函数的迭代版本。

我通常是一个Scheme程序员,而不是Python程序员,我对迭代和递归版本之间的区别感到惊讶,我将其归因于Python的函数调用开销。当我在Scheme中进行相同的实验时,迭代和递归版本之间基本上没有区别,这是有道理的,因为递归处于尾部位置,因此基本上是迭代的。

答案 3 :(得分:1)

我的代码比jonrsharpe更少Pythonic,但更接近你的例子。首先定义一个名为palindrome.py的文件:

def reverseInt(x):
    """Return the digit-reversed version of integer x (digits in decimal)."""
    #Convert to string, because strings are iterable.  Obtain a reverse
    #  iterator to the characters.
    Rx = reversed(str(x))
    #Obtain reversed string by joining with no intervening characters
    Rx = "".join(Rx)
    #Switch back to integer
    Rx = int(Rx)
    return Rx

def iteration(x):
    """Return a 2-tuple (n, done) where n is the result of one iteration of
    the palindrome search, and done is a boolean flag indicating whether n is
    a palindrome."""
    Rx = reverseInt(x)
    n = x + Rx
    Rn = reverseInt(n)
    return n, n==Rn

def depth(x):
    """Return a 2-tuple (y, numIter) where y is a palindrome and numIter
    is the number of palindrome iterations needed to obtain y from x."""
    numIter = 0
    if x == reverseInt(x):
        return x, numIter
    done = False
    while not done:
        x, done = iteration(x)
        numIter += 1
    return x, numIter

现在运行python

python
>>> import palindrome as pD
>>> #pD is only for brevity, I'm being a lazy typist
>>> pD.iteration(79)
(176, False)
>>> pD.depth(79)
(44044, 6)