【问题标题】:Check if a number can be formed by sum of a number and its reverse检查一个数是否可​​以由一个数和它的倒数之和组成
【发布时间】:2023-03-23 15:46:01
【问题描述】:

我想检查给定的数字是否可以由另一个数字组成,例如 breverse(b)。例如12 == 6+622 == 11 + 11121 == 29+92。我想到的一件事是,如果这个数字是 11 的倍数,或者它是小于 20 的偶数,那么它可以形成。我试图在下面实现这个:

num = 121
if num%11==0:
    print('Yes')
else:
    if num%2==0 and num<20:
        print('Yes')
    else:
        for j in range(11,(int(num)//2)+1):
            if j+int(str(j)[::-1])==num:
                print('Yes')
                break
         

但是,如果条件进入for 循环,它会给出TLE。可以给出其他条件吗?

更新:如果反转的数字有尾随零,则应将其删除然后添加。例如:101 == 100+1。我正在寻找我的代码的优化形式。或者我认为我缺少一些可能需要 O(1) 时间的条件,类似于条件if num%11==0: print('Yes')

【问题讨论】:

  • 100 的倒数是多少?是 1 还是没有?
  • @Nick,我的意思是接受所有能被 11 整除的数字。这并不意味着只接受那些能被 11 整除的数字。还有许多其他数字可以接受
  • @SASANTOSHCHIRAG 我误解了你的问题。 TLE 是什么意思?
  • 第二个for 循环在哪里,TLE 是什么意思?
  • 如果您允许前导零,那么从技术上讲,050 + 050 == 100 是允许的。

标签: python algorithm sum integer reverse


【解决方案1】:

之前的所有答案都不是真正的检查。这更像是一种蛮力尝试和错误。 所以让我们做得更聪明一点。

我们从一个数字开始,例如 246808642。我们可以将问题减少到数字末尾和开头的外部 2 位值。让我们把这个值称为前面的 A 和 B,后面的 Y 和 Z。其余的,在中间,是Π。所以我们的数字现在看起来是 ABΠYZ,A = 2,B = 4,Π = 68086,Y = 4 和 Z = 2。(为此总结的一对可能的数字是 123404321)。 A 是否等于 1,这仅适用于总和大于 10(假设,但我想它有效,一些证明会很好!)。

因此,如果它是 1,我们知道倒数第二个数字通过结转大于 1。所以我们暂时忽略 A 并将 B 与 Z 进行比较,因为它们应该是相同的,因为两者都是相同的两个数字相加的结果。如果是这样,我们取剩余部分 Π 并将 Y 减一(外部加法的结转),然后可以从图表顶部重新开始 Π(Y-1)。只有一个结转才能使 B 比 Z 大一,如果是这样,我们可以将 B 替换为 1 并从顶部的 1Π(Y-1) 开始。 B-1!=Z 和 B!=Z,我们可以停下来,对于这样一个数字的和与它的倒数相加的数字,这是不可能的。

如果 A != 1,我们所做的一切都与以前类似,但现在我们使用 A 而不是 B。(我在这里删掉了这个。答案足够长。)

代码:

import time
def timing(f):
    def wrap(*args, **kwargs):
        time1 = time.time()
        ret = f(*args, **kwargs)
        time2 = time.time()
        print('{:s} function took {:.3f} ms'.format(f.__name__, (time2-time1)*1000.0))

        return ret
    return wrap

@timing
def check(num):
    num = str(num)
    if (int(num) < 20 and int(num)%2 == 0) or (len(num) ==2 and int(num)%11 == 0):
        return print('yes')
    if len(num) <= 2 and int(num)%2 != 0:
        return print('no')
    # get the important place values of the number x
    A = num[0]
    B = num[1]
    remaining = num[2:-2]
    Y = num[-2]
    Z = num[-1]
    # check if A = 1
    if A == '1':
        # A = 1
        # check if B == Z
        if B == Z:
            # so the outest addition matches perfectly and no carry over from inner place values is involved
            # reduce the last digit about one and check again.
            check(remaining + (str(int(Y)-1) if Y != '0' else '9'))
        elif int(B)-1 == int(Z):
            # so the outest addition matches needs a carry over from inner place values to match, so we add to
            # to the remaining part of the number a leading one
            # we modify the last digit of the remaining place values, because the outest had a carry over
            check('1' + remaining + (str(int(Y)-1) if Y != '0' else '9'))
        else:
            print("Not able to formed by a sum of a number and its reversed.")
    else:
        # A != 1
        # check if A == Z
        if A == Z:
            # so the outest addition matches perfectly and no carry over from inner place values is involved
            check(B + remaining + Y)
        elif int(A) - 1 == int(Z):
            # so the outest addition matches needs a carry over from inner place values to match, so we add to
            # to the remaining part of the number a leading one
            # we modify the last digit of the remaining place values, because the outest had a carry over
            check('1' + B + remaining + Y)
        else:
            print("Not able to formed by a sum of a number and its reversed.")

@timing
def loop_check(x):
    for i in range(x + 1):
        if i == int(str(x - i)[::-1]) and not str(x - i).endswith("0"):
            print('yes, by brute force')
            break

loop_check(246808642)
check(246808642)

结果:

yes, by brute force
loop_check function took 29209.069 ms
Yes
check function took 0.000 ms

又一次我们看到了数学的力量。希望这对你有用!

【讨论】:

  • 非常感谢。这就是我要找的。但我想,这可能无法在竞争性编码中实现,因为这个想法有点复杂。伟大的工作
  • 这不会为 165 (69 + 96) 和 187 (89 + 98) 打印任何内容。它也不处理前导零,101 (100 + 001) 不起作用。
  • 是的,我也找到了其他一些。我猜对于 101,这来自假设前导 1 作为结转。所以我将 B=0 与 Z=1 进行比较,这是不正确的。这应该是可以修复的。 165 和 187 都可以被 11 整除,所以我猜回报不是很好。感谢您的留言,如果我有任何问题,我将编辑我的答案@Boris
  • 好吧,1000 以下的数字是固定的。对于 9999 以上的数字,它也应该可以工作。只有中间的数字会造成麻烦。我在chat.stackoverflow.com/rooms/230354/… 发布了我的代码,如果你想看看
【解决方案2】:

你能提供问题的约束条件吗?

您可以尝试以下方法:

i = 0
j = num
poss = 0
while(i<=j):
   if(str(i)==str(j)[::-1]):
       poss = 1
       break
   i+=1 
   j-=1
if(poss):
    print("Yes")
else:
    print("No")

【讨论】:

    【解决方案3】:

    你可以这样暴力破解:

    def reverse_digits(n):
        return int(str(n)[::-1])
    
    def sum_of_reversed_numbers(num):
        for i in range(num + 1):
            if i == reverse_digits(num - i):
                return i, num - i
        return None
    
    print("Yes" if sum_of_reversed_numbers(num) else "No")
    

    【讨论】:

      【解决方案4】:

      不用str 切片也可以做到:

      def reverse(n):
          r = 0
          while n != 0:
              r = r*10 + int(n%10)
              n = int(n/10)
          return r
      
      def f(n):
          for i in range(n + 1):
              if i + reverse(i) == n:
                  return True
          return False
      
      print('Yes' if f(101) else 'No')
      #Yes
      

      【讨论】:

        【解决方案5】:

        我的解决方案的基本思想是,您首先生成数字到可以组成它们的数字的映射,因此0 可以由 0+0 或 1+9、2+8 等组成。(但在这种情况下,您必须在下一步记住一个携带的 1 )。然后您从最小的数字开始,并使用该代码检查形成第一位数字的每种可能方式(这为您提供了数字的第一位和最后一位数字的候选者,它们与其相反的总和为您提供输入数字)。然后你移动第二个数字并尝试那些。通过同时检查最后一位和第一位数字可以大大改进此代码,但是由于携带的1,它变得复杂。

        import math
        
        candidates = {}
        for a in range(10):
            for b in range(10):
                # a, b, carry
                candidates.setdefault((a + b) % 10, []).append((a, b, (a + b) // 10))
        
        
        def sum_of_reversed_numbers(num):
            # We reverse the digits because Arabic numerals come from Arabic, which is
            # written right-to-left, whereas English text and arrays are written left-to-right
            digits = [int(d) for d in str(num)[::-1]]
        
            # result, carry, digit_index
            test_cases = [([None] * len(digits), 0, 0)]
        
            if len(digits) > 1 and str(num).startswith("1"):
                test_cases.append(([None] * (len(digits) - 1), 0, 0))
        
            results = []
        
            while test_cases:
                result, carry, digit_index = test_cases.pop(0)
                if None in result:
                    # % 10 because if the current digit is a 0 but we have a carry from
                    # the previous digit, it means that the result and its reverse need
                    # to actually sum to 9 here so that the +1 carry turns it into a 0
                    cur_digit = (digits[digit_index] - carry) % 10
                    for a, b, new_carry in candidates[cur_digit]:
                        new_result = result[::]
                        new_result[digit_index] = a
                        new_result[-(digit_index + 1)] = b
                        test_cases.append((new_result, new_carry, digit_index + 1))
                else:
                    if result[-1] == 0 and num != 0:  # forbid 050 + 050 == 100
                        continue
                    i = "".join(str(x) for x in result)
                    i, j = int(i), int(i[::-1])
                    if i + j == num:
                        results.append((min(i, j), max(i, j)))
        
            return results if results else None
        

        我们可以通过预先计算从 0 到 10ⁿ 的所有数字的总和以及它们的反向并将它们存储在一个名为 correct 的列表的字典中来检查上面的代码(一个列表,因为有很多方法可以形成相同的数字,例如 11+11 == 02 + 20),这意味着我们有 10ⁿ⁻¹ 的正确答案,我们可以用它来检查上述函数。顺便说一句,如果您经常使用少量数字执行此操作,那么这种预先计算的方法会更快,但会消耗内存。

        如果这段代码什么也没打印,说明它可以工作(或者你的终端坏了:))

        correct = {}
        for num in range(1000000):
            backwards = int(str(num)[::-1])
            components = min(num, backwards), max(num, backwards)
            summed = num + backwards
            correct.setdefault(summed, []).append(components)
        
        for i in range(100000):
            try:
                test = sum_of_reversed_numbers(i)
            except Exception as e:
                raise Exception(i) from e
            if test is None:
                if i in correct:
                    print(i, test, correct.get(i))
            elif sorted(test) != sorted(correct[i]):
                print(i, test, correct.get(i))
        

        【讨论】:

          【解决方案6】:

          从@Doluk 那里窃取了这个想法。我今天在测试中被问到这个问题。那时我解决不了。有了 Doluk 的想法并认真思考,下面是一种用于一级递归的决策树。我可能错了,因为我没有运行这个算法。

          设n为数字,我们要检查是否特殊

          案例1:前导数不是1

          案例1a:内部添加没有结转

              abcdefgh
              hgfedcba
              
              x      x  => (a+h) < 10
              
              if both ends are same in n, strip both sides by one digit and recurse
              
          

          案例1b:从内部添加结转

              1    
              abcdefgh
              hgfedcba
              
             (x+1)......(x)  => (a+h+1) < 10
          
             if left end is 1 greater than right end in n, strip both sides by one digit, add digit 1 on the left and recurse
          

          案例2:前导数为1

          案例 2a:内部添加没有结转

             1      1
              abcdefgh
              hgfedcba
              
             1x      x   => (a+h) >= 10
             
           strip -  if second and last digit are same, strip two digits from left and one from right, from the remaining number minus 1 and recurse.
          

          案例 2b:从内部添加结转

          案例 2bi:a+h = 9

                  11    
                   abcdefgh
                   hgfedcba
          
                  10......9
                  
              strip - two from left and one from right and recurse.
                  
          

          案例 2bj:a+h >= 10

                  11     1
                   abcdefgh
                   hgfedcba
          
                  1(x+1)......x
                  
              strip - two from left and one from right and subtract 1 from number and recurse.
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-09-14
            • 2014-09-04
            • 2015-07-28
            • 1970-01-01
            • 2010-09-24
            • 1970-01-01
            相关资源
            最近更新 更多