Python:如何生成一个范围,该范围跳过第四个数字?

时间:2018-08-03 00:57:49

标签: python python-3.x

我需要创建一个范围,该范围跳过从5开始的第4个数字。例如,如果范围a是1-20,则数字5,9,13,17将被排除。

a = [1, 2, 3, 4, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20]

我尝试的是创建一个常规范围,然后创建第二个范围,该范围包括我要跳过的数字,然后从第一个范围中删除第二个范围。

a = list(range(1,21))
b = list(range(5,21,4))
for x in b:
   if x in a:
      a.remove(x)

这有效,但范围不大。 有更有效的方法吗?

7 个答案:

答案 0 :(得分:6)

解决方案:

为了提高效率,我建议使用这样的生成器表达式:

r = (x for x in range(1,21) if x not in range(5,21,4))

或等效地,并且不需要两次编写上限:

r = (x for x in range(1,21) if x == 1 or x % 4 != 1)

您可以像使用常规序列(列表/元组)*一样使用此生成器,并在绝对需要时使用list()将生成器转换为列表。

合理性:

这种方法的优点是它不需要将所有项目存储在内存中,因此您可以任意增大上限,而不会出现任何性能问题。


*(有一些警告,如下面的评论者所述。例如,如果要快速进行成员资格检查,最好单独使用这两个范围)

答案 1 :(得分:2)

使用set和另一个列表理解:

a = range(1, 21)
b = set(range(5, 21, 4))

[i for i in a if i not in b]
# [1, 2, 3, 4, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20]

您可以从第二个范围中删除set,但我发现这比设置包含检查要慢:

功能

def chris(m):
  a = range(1, m)
  b = set(range(5, m, 4))
  return [i for i in a if i not in b]

def chris2(m):
  a = range(1, m)
  b = range(5, m, 4)
  return [i for i in a if i not in b]


def ollin(m):
  return list(x for x in range(1,m) if x not in range(5,m,4))

def ollin2(m):
  return list(x for x in range(1,m) if x == 1 or x % 4 != 1)

def smac(m):
  return [v for i, v in enumerate(range(1,m)) if i == 0 or i % 4 != 0]

设置

from timeit import timeit

import pandas as pd
import matplotlib.pyplot as plt

res = pd.DataFrame(
       index=['chris', 'chris2', 'ollin', 'ollin2', 'smac'],
       columns=[10, 50, 100, 500, 1000, 5000, 10000],
       dtype=float
)

for f in res.index: 
    for c in res.columns:
        stmt = '{}(c)'.format(f)
        setp = 'from __main__ import c, {}'.format(f)
        res.at[f, c] = timeit(stmt, setp, number=50)

ax = res.div(res.min()).T.plot(loglog=True) 
ax.set_xlabel("N"); 
ax.set_ylabel("time (relative)");

plt.show()

enter image description here

答案 2 :(得分:2)

您可以使用此列表理解:

>>> print ([v for i, v in enumerate(range(1,21)) if i == 0 or i % 4 != 0])
[1, 2, 3, 4, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20]

答案 3 :(得分:1)

如果我们考虑numpy

import numpy as np 
a=np.arange(1,21)
a[np.logical_or(a%4!=1,a==1)]
Out[209]: array([ 1,  2,  3,  4,  6,  7,  8, 10, 11, 12, 14, 15, 16, 18, 19, 20])

答案 4 :(得分:1)

编写自己的生成器函数可能会有用:

def weird_range():
   for i in range(1, 21):
      if i < 5 or i % 4 != 1:
         yield i

>>> list(weird_range())
[1, 2, 3, 4, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20]

这将使您进行各种奇怪的序列。


替代方法是创建您自己的范围对象:

class WeirdRange:
   def __contains__(self, val):
      return val < 5 or val % 4 != 1

>>> list(_ for _ in range(1,21) if _ in WeirdRange())
[1, 2, 3, 4, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20]

或者您可以创建自己的Iterator:

class WeirdIterator:

   def __init__(self):
      self._idx = 0

   def __iter__(self):
      return self

   def __next__(self):
      self._idx += 1
      if self._idx >= 5  and  self._idx % 4 == 1:
         self._idx += 1
      if self._idx > 20:
         raise StopIteration()
      return self._idx

>>> list(WeirdIterator())
[1, 2, 3, 4, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20]

答案 5 :(得分:0)

也许有区别吗?

numItems = 21
r1 = set(range(1,numItems))
r2 = set(range(5,numItems,4))

r3 = list(r1 - r2)

答案 6 :(得分:0)


a = list(range(1,21))

for x in a:
    if x!=1 and x%4==1:
        continue
    print(x)