Naïve Benchmarks
In place
gboffi1| 727.3899 μs, 729.9263 μs, 730.8136 μs, 731.3269 μs, 733.5562 μs
gboffi2| 19271.8119 μs, 19280.2418 μs, 19290.8053 μs, 19684.0108 μs, 20424.9170 μs
gboffi3| 8867.9423 μs, 8871.5615 μs, 8875.3720 μs, 8876.5216 μs, 8879.1700 μs
Cabu| 822.5495 μs, 835.4780 μs, 835.7711 μs, 844.2948 μs, 853.0149 μs
joanis| 506.0253 μs, 506.1155 μs, 506.1627 μs, 506.1810 μs, 506.5230 μs
Soumya| 1203.9832 μs, 1206.2782 μs, 1208.5386 μs, 1245.2450 μs, 1254.6893 μs
Copying
gboffi4| 414.7999 μs, 418.4400 μs, 418.5836 μs, 418.7423 μs, 418.7452 μs
Ilya_1| 524.9701 μs, 525.1853 μs, 526.0796 μs, 526.6274 μs, 526.9053 μs
Ilya_2| 306.7650 μs, 306.8397 μs, 307.0605 μs, 307.1060 μs, 307.3800 μs
Ilya_3| 308.0310 μs, 311.2493 μs, 311.8763 μs, 314.4209 μs, 321.3265 μs
Timur| 406.5968 μs, 406.6816 μs, 406.8116 μs, 406.9451 μs, 407.1210 μs
Copying is faster than working in place.
If copying, the simpler, the faster and, list comprehensions are the real thing.
Working in place, it seems to me that
- the easy optimizations used by joanis make a difference,
- my method no. 1 is rather fast and quite simple, and
- it's easy to incur in abysmal performance (my code no. 2 and no.3).
If you think that some code is misrepresented in my implementation, I'll be glad to correct any issue.
from random import randint, seed
from timeit import Timer
def timer(stmt):
tmr = Timer(stmt, globals=globals())
n, *t = tmr.autorange()
t += tmr.repeat(number=n, repeat=4)
return sorted([t_i/n for t_i in t])
def print_times(name, stmt):
print("%12s|"%name,
', '.join("%10.4f μs"%(1E6*t_i) for t_i in timer(stmt)))
seed(20230326)
l = [randint(0, 20) for _ in range(10_000)]
def gboffi2(l, c=0):
while True:
try:
l.remove(0)
c += 1
except ValueError:
break
l += [0]*c
def gboffi3(l):
for i in reversed([i for i, v in enumerate(l) if v==0]): l[i:] = l[i+1:]+[0]
def gboffi4(l):
for i in reversed([i for i, v in enumerate(l) if v==0]): l = l[i:]+l[i+1:]+[0]
def Soumya(data: list) -> list:
size = len(data)
left = right = 0
while right < size:
if data[right] == 0:
right += 1
else:
data[left], data[right] = data[right], data[left]
right += 1
left += 1
def Cabu(nums):
n = len(nums)
j = 0
for i in range(n):
nums[j] = nums[i]
j += 1 if nums[i] else 0
nums[j:] = [0] * (n-j)
def Ilya3(l): return [x for x in l if x !=0]+[0]*l.count(0)
def joanis(nums) -> None:
pos = 0 # keep track of where to place the next non-zero value
for num in nums:
if num != 0:
nums[pos] = num
pos += 1
for i in range(pos, len(nums)):
nums[i] = 0
def Timur(l):
l = [x for x in l if x != 0 ] + [x for x in l if x == 0 ]
print("In place")
print_times('gboffi1', 'l[::].sort(key = lambda n:n==0)')
print_times('gboffi2', 'gboffi2(l[::])')
print_times('gboffi3', 'gboffi3(l[::])')
print_times('Cabu', 'Cabu(l[::])')
print_times('joanis', 'joanis(l[::])')
print_times('Soumya' , 'Soumya(l[::])')
print("Copying")
print_times('gboffi4', 'gboffi4(l[::])')
print_times('Ilya_1', 'a=list(filter(lambda x: x!= 0, l))+[0]*l.count(0)')
print_times('Ilya_2', 'a=[x for x in l if x !=0]+[0]*l.count(0)')
print_times('Ilya_3', 'a=Ilya3(l[::])')
print_times('Timur', 'a=[x for x in l[::] if x!=0 ]+[x for x in l[::] if x== 0]')