簡體   English   中英

如果它們的索引 mod 4 大於 1,如何將列表的元素變成它們的負數?

[英]How to turn elements of a list into their negative counterpart if their index mod 4 is greater than 1?

我正在嘗試將一個列表,例如L = [1, 2, 3, 4, 5, 6, 7, 8, ... , n]轉換為另一個列表L' = [1, 2, -3, -4, 5, 6, -7, -8, ...., ±n]在 Python 中。 我的問題是,是否有比使用 for 循環更短/更有效的方法:

for i in range(len(L)):
    if i%4 > 1:
        L[i] *= -1

例如通過切片。

for循環相當有效,因為它不會為新的L'浪費空間,除非您當然需要保留原始列表,在這種情況下您的代碼是錯誤的。

如果您只關心速度而不是效率,則可以從 numpy 數組而不是列表開始,因為您可以想出一個比列表操作執行得更快的操作。

如果您關心更短,@wkl 提供的理解是通往 go 的方式。

l_prime = [-x if i%4 > 1 else x for i, x in enumerate(l)]

這是使用timeit (標准庫)計時的實現:

from timeit import timeit
import numpy as np
import itertools, operator


def samuel(l):
    for i in range(len(l)):
        if i % 4 > 1:
            l[i] *= -1
    return l


def chai(l):
    return list(map(operator.mul, l, itertools.cycle([1, 1, -1, -1])))


def wkl(l):
    return [-x if i % 4 > 1 else x for i, x in enumerate(l)]


def vladimir(l):
    ar = np.array(l)
    ar[2::4] *= -1
    ar[3::4] *= -1
    return ar.tolist()


# ensure all outcomes are the same
assert samuel(list(range(1000))) == chai(list(range(1000))) == wkl(list(range(1000))) == vladimir(list(range(1000)))

print('samuel: ', timeit(lambda: samuel(list(range(1000))), number=100000))
print('chai: ', timeit(lambda: chai(list(range(1000))), number=100000))
print('wkl: ', timeit(lambda: wkl(list(range(1000))), number=100000))
print('vladimir: ', timeit(lambda: vladimir(list(range(100000))), number=1000))

結果:

samuel:  6.736065300000519
chai:  3.7625152999999045
wkl:  7.069251500000064
vladimir:  6.424349999997503

numpy 解決方案將變得更快,無需列表轉換,如下所述:

def vladimir_a(ar):
    ar[2::4] *= -1
    ar[3::4] *= -1
    return ar.tolist()


ar = np.array(list(range(1000)))
print('vladimir array: ', timeit(lambda: vladimir_a(ar), number=100000))

結果:

vladimir array:  1.269356699999662

(我知道ar會被修改 100,000 次,但不會影響性能)

編輯:實際上,這是不公平的 - 范圍都在定時部分,這將是公平的(而且不是那么好)

def vladimir_a(ar):
    ar[2::4] *= -1
    ar[3::4] *= -1
    return ar.tolist()


print('vladimir array: ', timeit(lambda: vladimir_a(np.array(range(1000))), number=100000))

結果:

vladimir array:  6.5144264999998995

所以你可能需要在你的實際用例中做一些時間來找到那里最快的。 構建相同的數組 100000 次(或相同的列表)顯然不是您正在做的事情,並且人們會期望您在大型數據集上進行操作以提高速度甚至是一個考慮因素。

使用 Python 3,您可以使用itertoolscycle一遍又一遍地重復一個迭代,然后使用map將兩個迭代與一些 ZC1C425268E68385D1AB5074FZ:

>>> import itertools, operator
>>> list(map(operator.mul, range(10), itertools.cycle([1, 1, -1, -1])))
[0, 1, -2, -3, 4, 5, -6, -7, 8, 9]
>>> list(map(operator.mul, [2, 3, 5, 7, 11, 13, 17, 19], itertools.cycle([1, 1, -1, -1])))
[2, 3, -5, -7, 11, 13, -17, -19]

以下是各種方法的比較:

import time
import numpy as np

n = int(1e7)

s1 = time.time()
L1 = list(range(1, n))
for i in range(len(L1)):
    if i % 4 > 1:
        L1[i] *= -1
e1 = time.time()

s2 = time.time()
L2 = list(range(1, n))
L2 = np.array(L2)
L2[np.arange(n - 1) % 4 > 1] *= -1
L2 = L2.tolist()
e2 = time.time()

s3 = time.time()
L3 = list(range(1, n))
L3 = np.array(L3)
k = (n - 1) // 4
pat = [False, False, True, True]
idx = np.concatenate((np.tile(pat, k), pat[:n - k * 4 - 1])).astype(bool)
L3[idx] *= -1
L3 = L3.tolist()
e3 = time.time()

s4 = time.time()
L4 = list(range(1, n))
L4 = np.array(L4)
L4[2::4] *= -1
L4[3::4] *= -1
L4 = L4.tolist()
e4 = time.time()

assert all(np.array(L1) == np.array(L2))
assert all(np.array(L1) == np.array(L3))
assert all(np.array(L1) == np.array(L4))

print(f'time1: {e1 - s1:.2f}s, time2: {e2 - s2:.2f}s, time3: {e3 - s3:.2f}s, time4: {e4 - s4:.2f}s')

印刷

time1: 3.76s, time2: 1.72s, time3: 1.58s, time4: 1.52s

為了補充@Grismar的答案,numpy arrays 可以比迭代快得多,因為它們並行地對 arrays 進行操作(甚至在 CPU 上)。

以下是如何使用 numpy arrays(帶切片)解決您的問題:

import numpy as np

# If you already have a list `L`:
ar = np.array(L)

# But it’s faster to use numpy to 
# generate the array of numbers 
# (here: integers from 1 to n):
n = 15
ar = np.arange(1, n + 1)

ar[2::4] *= -1
ar[3::4] *= -1

# You can convert a numpy array into a list. 
# Note that this step may not be necessary 
# at all, because numpy arrays can be
# used where you would use lists. 
# But for completeness, here is how to do it:
L_prime = ar.tolist()

一個 function 你可以使用:

def make_array(n):
    ar = np.arange(1, n + 1)
    ar[2::4] *= -1
    ar[3::4] *= -1
    return ar

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM