我需要创建一个范围,该范围跳过从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)
这有效,但范围不大。 有更有效的方法吗?
答案 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()
答案 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)