【问题标题】:How do a split integers within a list into single digits only?如何将列表中的整数拆分为单个数字?
【发布时间】:2017-08-22 10:49:00
【问题描述】:

假设我有这样的事情:

    list(range(9:12))

这给了我一个清单:

    [9,10,11]

但我希望它是这样的:

    [9,1,0,1,1]

将每个整数分成个位数,有没有在不牺牲太多性能的情况下实现这一点?或者有没有办法首先生成这样的列表?

【问题讨论】:

    标签: python python-3.x list


    【解决方案1】:

    您可以高效地构建最终结果,而无需使用itertools.chain.from_iterable 构建一个大的和/或小的中间字符串。

    In [18]: list(map(int, chain.from_iterable(map(str, range(9, 12)))))
    Out[18]: [9, 1, 0, 1, 1]
    

    In [12]: %%timeit
        ...: list(map(int, chain.from_iterable(map(str, range(9, 20)))))
        ...: 
    100000 loops, best of 3: 8.19 µs per loop
    
    In [13]: %%timeit
        ...: [int(i) for i in ''.join(map(str, range(9, 20)))]
        ...: 
    100000 loops, best of 3: 9.15 µs per loop
    
    In [14]: %%timeit
        ...: [int(x) for i in range(9, 20) for x in str(i)]
        ...: 
    100000 loops, best of 3: 9.92 µs per loop
    

    时间随输入而变化。 itertools 版本也有效地使用内存,尽管如果与list(map(int, ...)) 一起使用,它比str.join 版本稍慢:

    In [15]: %%timeit
        ...: list(map(int, chain.from_iterable(map(str, range(9, 200)))))
        ...: 
    10000 loops, best of 3: 138 µs per loop
    
    In [16]: %%timeit
        ...: [int(i) for i in ''.join(map(str, range(9, 200)))]
        ...: 
    10000 loops, best of 3: 159 µs per loop
    
    In [17]: %%timeit
        ...: [int(x) for i in range(9, 200) for x in str(i)]
        ...: 
    10000 loops, best of 3: 182 µs per loop
    
    In [18]: %%timeit
        ...: list(map(int, ''.join(map(str, range(9, 200)))))
        ...: 
    10000 loops, best of 3: 130 µs per loop
    

    【讨论】:

      【解决方案2】:

      最简单的方法是,

      >>> [int(i) for i in range(9,12) for i in str(i)]
      [9, 1, 0, 1, 1]
      >>> 
      

      【讨论】:

        【解决方案3】:

        将整数转换为字符串,然后split() 字符串并将数字重新转换回整数。

        li = range(9,12)
        
        digitlist = [int(d) for number in li for d in str(number)]
        

        输出:

        [9,1,0,1,1]
        

        【讨论】:

          【解决方案4】:

          我已经研究了如何提高性能。我编写的第一个函数是naive_single_digits,它使用str 方法,具有非常高效的列表理解。

          def naive_single_digits(l):
              return [int(c) for n in l
                             for c in str(n)]
          

          如您所见,这种方法有效:

          In [2]: naive_single_digits(range(9, 15))
          Out[2]: [9, 1, 0, 1, 1, 1, 2, 1, 3, 1, 4]
          

          但是,我认为总是为列表中的每个项目构建一个str 对象肯定是不必要的——我们真正需要的只是一个基本的数字转换。出于懒惰,我从here复制了这个函数。我通过将其指定为以 10 为底对它进行了一些优化。

          def base10(n):
              if n == 0:
                  return [0]
              digits = []
              while n:
                  digits.append(n % 10)
                  n //= 10
              return digits[::-1]
          

          用这个,我做了

          def arithmetic_single_digits(l):
              return [i for n in l
                        for i in base10(n)]
          

          它的行为也正确:

          In [3]: arithmetic_single_digits(range(9, 15))
          Out[3]: [9, 1, 0, 1, 1, 1, 2, 1, 3, 1, 4]
          

          现在是时候了。我还针对另一个答案进行了测试(完全披露:我对其进行了一些修改以在 Python2 中工作,但这应该不会对性能产生太大影响)

          In [11]: %timeit -n 10 naive_single_digits(range(100000))
          10 loops, best of 3: 173 ms per loop
          
          In [10]: %timeit -n 10 list(map(int, itertools.chain(*map(str, range(100000)))))
          10 loops, best of 3: 154 ms per loop
          
          In [12]: %timeit arithmetic_single_digits(range(100000))
          10 loops, best of 3: 93.3 ms per loop
          

          正如您所见,arithmetic_single_digits 实际上更快一些,尽管这是以更多代码为代价并且可能不太清晰。我已经针对非常大的输入进行了测试,因此您可以看到性能上的差异 - 在任何合理的规模下,这里的每个答案都会非常快。请注意,python 的整数运算实际上可能相对较慢,因为它不使用原始整数类型。如果这要在 C 中实现,我怀疑我的方法会更快一些。

          将此与 viblo 的答案进行比较,使用(纯)Python 3(遗憾的是我还没有为 python 3 安装 ipython):

          print(timeit.timeit("digits(range(1, 100000))", number=10, globals=globals()))
          print(timeit.timeit("arithmetic_single_digits(range(1, 100000))", number=10, globals=globals()))
          

          输出如下:

          3.5284318959747907
          0.806847038998967
          

          我的方法要快很多,大概是因为我纯粹使用整数运算。

          【讨论】:

            【解决方案5】:

            另一种写算术解决方案的方法。与 Izaak van Dongens 解决方案相比,它不使用 while 循环,而是预先计算在列表理解/循环中需要多少次迭代。

            import itertools, math
            
            def digits(ns):
                return list(itertools.chain.from_iterable(
                    [
                        [
                            (abs(n) - (abs(n) // 10 **x)*10**x ) // 10**(x-1) 
                            for x 
                            in range(1+math.floor(math.log10(abs(n) if n!=0 else 1)), 0, -1)] 
                        for n in ns
                    ]
                ))
            
            digits([-11,-10,-9,0,9,10,11])
            

            【讨论】:

            • 真棒:D。你是一个比我更勇敢的人进入这样的算术的胆量。顺便说一句,对于digits([0]),您会遇到数学域错误,尽管 0 确实有数字。
            • 谢谢,我更新了代码来处理 0 和负整数。不幸的是,与其他解决方案相比,它的速度非常慢。
            【解决方案6】:

            把它变成一个字符串然后再变成一个列表:)

            lambda x: list(''.join(str(e) for e in x))
            

            【讨论】:

              【解决方案7】:

              你也可以使用地图功能

              a=range(9,12)
              res = []
              b=[map(int, str(i)) for i in a]
              for i in b:
                  res.extend(i)
              
              print(res)
              

              【讨论】:

                【解决方案8】:

                我是这样做的:

                ls =  range(9,12)
                lsNew = []
                length = len(ls)
                for i in range(length):
                    item = ls[i]
                    string = str(item)
                    if len(string) > 1:
                        split = list(string)
                        lsNew = lsNew + split
                    else:
                        lsNew.append(item)
                ls = lsNew
                print(ls)
                

                【讨论】:

                  【解决方案9】:
                  def breakall(L):
                    if L == []:
                      return []
                    elif L[0] < 10:
                      return [L[0]] + breakall(L[1:])
                    else: 
                      return breakall([L[0]//10]) + [L[0] % 10] + breakall(L[1:]) 
                  
                  print(breakall([9,10,12]))
                  -->
                  [9, 1, 0, 1, 2]
                  

                  【讨论】:

                    猜你喜欢
                    • 2023-02-11
                    • 2017-05-23
                    • 1970-01-01
                    • 2021-07-16
                    • 1970-01-01
                    • 2018-01-15
                    • 2011-02-12
                    • 1970-01-01
                    • 2022-11-03
                    相关资源
                    最近更新 更多