I wrote this code:
arr = [1, 2, 3, 4, 5, 6]
arr1 = []
count = 0
arr.append(0) # i forgot to wrote this line.
for i in range(0, len(arr)):
count = sum(arr)
arr.remove(arr[0])
arr1.append(count)
print(arr1)
The output is:
[20, 20, 19, 16, 10, 0]
But I have a little problem. Time of execution it is a little bit to long for large lists.
Can u tell me if exist another mode to write it?
Thx!
I'd suggest itertools.accumulate
from itertools import accumulate
from operator import add
result = list(accumulate(reversed(arr), add))[::-1]
With a few test it's a lot more performant, for 20k digits
accumulate 0:00:00.004001
question 0:00:05.340281
In python, removing the first element of a list requires all the subsequent elements to be moved and therefore takes O(n). Since you're doing that n times (with n being the length of your array), the overall time complexity of your solution is O(n2)
Any solution that only runs in O(n) time complexity should be fine for you. Here is one that doesn't require any external imports and is similar in style to your original solution:
arr = [1, 2, 3, 4, 5, 6]
total = sum(arr)
sums = [total]
for to_remove in arr[:-1]:
total -= to_remove
sums.append(total)
print(sums)
[21, 20, 18, 15, 11, 6]
It is not perfect but a little faster:
import timeit
def function():
arr = [1, 2, 3, 4, 5, 6]
arr1 = []
count = 0
for i in range(0, len(arr)):
count = sum(arr)
arr.remove(arr[0])
arr1.append(count)
return arr1
print(timeit.timeit(function))
Time: 1.9620031519999657
import timeit
def function():
arr = [1, 2, 3, 4, 5, 6]
arr1 = [sum(arr)]
i = 0
while i < len(arr)-1:
arr1.append(arr1[i] - arr[i])
i = i +1
arr1.append(arr1[-1]-arr[-1])
return arr1
print(timeit.timeit(function))
Time: 1.408351424999978
If you are every time conscious, always try to use libraries like numpy.
In numpy this will be every easy, and efficient.
import numpy
a = numpy.arange(7) # since you also want to include 6 in array, 0, 1, 2, ..., 5, 6
np.cumsum(a[::-1])[::-1]
You would need to install numpy separately. If you don't know you can install numpy by:
pip install numpy
Related
This question already has answers here:
Combining two sorted lists in Python
(22 answers)
Closed 6 months ago.
I have a function insarrintomain which takes 2 arguments. The first one is main list, the second one is an insert list. I need to create a new list, where I will have all numbers from both arrays in increasing order. For example: main is [1, 2, 3, 4, 8, 9, 12], ins is [5, 6, 7, 10]. I should get [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12]
Here is my code:
def insarrintomain(main, ins):
arr = []
c = 0
for i, el in enumerate(main):
if c < len(ins):
if el > ins[c]:
for j, ins_el in enumerate(ins):
if ins_el < el:
c += 1
arr.append(ins_el)
else:
break
else:
arr.append(el)
else:
arr.append(el)
return arr
What did I miss?
Why not
new_array = main + insert
new_array.sort()
The pyhonic way of solving this problem is something like this:
def insarrintomain(main, ins):
new_list = main + ins
new_list.sort()
return new_list
In Python readability counts.
This code is pythonic because it’s easy to read: the function takes two lists, concatenates them into one new list, sorts the result and returns it.
Another reason why this code is pythonic is because it uses built-in functions. There is no need to reinvent the wheel: someone already needed to concatenate two lists, or to sort one. Built-in functions such as sort have been optimised for decades and are mostly written in C language. By no chance we can beat them using Python.
Let’s analyse the implementation from #RiccardoBucco.
That is perfect C code. You barely can understand what is happening without comments. The algorithm is the best possible for our case (it exploits the existing ordering of the lists) and if you can find in the standard libraries an implementation of that algorithm you should substitute sort with that.
But this is Python, not C. Solving your problem from scratch and not by using built-ins results in an uglier and slower solution.
You can have a proof of that by running the following script and watching how many time each implementation needs
import time
long_list = [x for x in range(100000)]
def insarrintomain(main, ins):
# insert here the code you want to test
return new_list
start = time.perf_counter()
_ = insarrintomain(long_list, long_list)
stop = time.perf_counter()
print(stop - start)
On my computer my implementation took nearly 0.003 seconds, while the C-style implementation from #RiccardoBucco needed 0.055 seconds.
A simple solution would be:
def insarrintomain(main, ins):
return (main + ins).sorted()
But this solution is clearly not optimal (the complexity is high, as we are not using the fact that the input arrays are already sorted). Specifically, the complexity here is O(k * log(k)), where k is the sum of n and m (n is the length of main and m is the length of ins).
A better solution:
def insarrintomain(main, ins):
i = j = 0
arr = []
while i < len(main) and j < len(ins):
if main[i] < ins[j]:
arr.append(main[i])
i += 1
else:
arr.append(ins[j])
j += 1
while i < len(main):
arr.append(main[i])
i += 1
while j < len(ins):
arr.append(ins[j])
j += 1
return arr
Example:
>>> insarrintomain([1, 2, 3, 4, 8, 9, 12], [5, 6, 7, 10])
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12]
This solution is much faster for big arrays (O(k), where k is the sum of n and m, n is the length of main and m is the length of ins).
I have a list arr and a value dif. I need to create an algorithm that will go through the values in arr, subtract them (the largest value after subtraction can be maximally dif) and then output the resulting values in new lists.
I created this code. The first problem is that it is broken if there are 3+ lists in the output. The second problem is the time complexity - if I'm not mistaken, it is O(n^2) so it's really slow.
I've tried to create this using Insertion sort and Binary search, but I never get the right result. The output was even worse than in the above code.
Can someone help me? How to simplify the algorithm and how to make it work correctly?
Code :
arr = [9, 3, 11, 5, 10, 4]
arr2 = [16, 13, 3, 1, 8, 2]
dif = 3
def func(arr):
pre_list = []
for i in range(0, len(arr)):
result = []
for j in range(0, len(arr)):
sub = arr[i] - arr[j]
if (abs(sub) <= dif):
result.append(arr[j])
if (result not in pre_list):
pre_list.append(result)
print(result)
func(arr)
Output:
arr:
[9, 11, 10]
[3, 5, 4]
arr 1:
[16, 13]
[3, 1, 2]
[8]
I'm trying my hand at converting the following loop to a comprehension.
Problem is given an input_list = [1, 2, 3, 4, 5]
return a list with each element as multiple of all elements till that index starting from left to right.
Hence return list would be [1, 2, 6, 24, 120].
The normal loop I have (and it's working):
l2r = list()
for i in range(lst_len):
if i == 0:
l2r.append(lst_num[i])
else:
l2r.append(lst_num[i] * l2r[i-1])
Python 3.8+ solution:
:= Assignment Expressions
lst = [1, 2, 3, 4, 5]
curr = 1
out = [(curr:=curr*v) for v in lst]
print(out)
Prints:
[1, 2, 6, 24, 120]
Other solution (with itertools.accumulate):
from itertools import accumulate
out = [*accumulate(lst, lambda a, b: a*b)]
print(out)
Well, you could do it like this(a):
import math
orig = [1, 2, 3, 4, 5]
print([math.prod(orig[:pos]) for pos in range(1, len(orig) + 1)])
This generates what you wanted:
[1, 2, 6, 24, 120]
and basically works by running a counter from 1 to the size of the list, at each point working out the product of all terms before that position:
pos values prod
=== ========= ====
1 1 1
2 1,2 2
3 1,2,3 6
4 1,2,3,4 24
5 1,2,3,4,5 120
(a) Just keep in mind that's less efficient at runtime since it calculates the full product for every single element (rather than caching the most recently obtained product). You can avoid that while still making your code more compact (often the reason for using list comprehensions), with something like:
def listToListOfProds(orig):
curr = 1
newList = []
for item in orig:
curr *= item
newList.append(curr)
return newList
print(listToListOfProds([1, 2, 3, 4, 5]))
That's obviously not a list comprehension but still has the advantages in that it doesn't clutter up your code where you need to calculate it.
People seem to often discount the function solution in Python, simply because the language is so expressive and allows things like list comprehensions to do a lot of work in minimal source code.
But, other than the function itself, this solution has the same advantages of a one-line list comprehension in that it, well, takes up one line :-)
In addition, you're free to change the function whenever you want (if you find a better way in a later Python version, for example), without having to change all the different places in the code that call it.
This should not be made into a list comprehension if one iteration depends on the state of an earlier one!
If the goal is a one-liner, then there are lots of solutions with #AndrejKesely's itertools.accumulate() being an excellent one (+1). Here's mine that abuses functools.reduce():
from functools import reduce
lst = [1, 2, 3, 4, 5]
print(reduce(lambda x, y: x + [x[-1] * y], lst, [lst.pop(0)]))
But as far as list comprehensions go, #AndrejKesely's assignment-expression-based solution is the wrong thing to do (-1). Here's a more self contained comprehension that doesn't leak into the surrounding scope:
lst = [1, 2, 3, 4, 5]
seq = [a.append(a[-1] * b) or a.pop(0) for a in [[lst.pop(0)]] for b in [*lst, 1]]
print(seq)
But it's still the wrong thing to do! This is based on a similar problem that also got upvoted for the wrong reasons.
A recursive function could help.
input_list = [ 1, 2, 3, 4, 5]
def cumprod(ls, i=None):
i = len(ls)-1 if i is None else i
if i == 0:
return 1
return ls[i] * cumprod(ls, i-1)
output_list = [cumprod(input_list, i) for i in range(len(input_list))]
output_list has value [1, 2, 6, 24, 120]
This method can be compressed in python3.8 using the walrus operator
input_list = [ 1, 2, 3, 4, 5]
def cumprod_inline(ls, i=None):
return 1 if (i := len(ls)-1 if i is None else i) == 0 else ls[i] * cumprod_inline(ls, i-1)
output_list = [cumprod_inline(input_list, i) for i in range(len(input_list))]
output_list has value [1, 2, 6, 24, 120]
Because you plan to use this in list comprehension, there's no need to provide a default for the i argument. This removes the need to check if i is None.
input_list = [ 1, 2, 3, 4, 5]
def cumprod_inline_nodefault(ls, i):
return 1 if i == 0 else ls[i] * cumprod_inline_nodefault(ls, i-1)
output_list = [cumprod_inline_nodefault(input_list, i) for i in range(len(input_list))]
output_list has value [1, 2, 6, 24, 120]
Finally, if you really wanted to keep it to a single , self-contained list comprehension line, you can follow the approach note here to use recursive lambda calls
input_list = [ 1, 2, 3, 4, 5]
output_list = [(lambda func, x, y: func(func,x,y))(lambda func, ls, i: 1 if i == 0 else ls[i] * func(func, ls, i-1),input_list,i) for i in range(len(input_list))]
output_list has value [1, 2, 6, 24, 120]
It's entirely over-engineered, and barely legible, but hey! it works and its just for fun.
For your list, it might not be intentional that the numbers are consecutive, starting from 1. But for cases that that pattern is intentional, you can use the built in method, factorial():
from math import factorial
input_list = [1, 2, 3, 4, 5]
l2r = [factorial(i) for i in input_list]
print(l2r)
Output:
[1, 2, 6, 24, 120]
The package numpy has a number of fast implementations of list comprehensions built into it. To obtain, for example, a cumulative product:
>>> import numpy as np
>>> np.cumprod([1, 2, 3, 4, 5])
array([ 1, 2, 6, 24, 120])
The above returns a numpy array. If you are not familiar with numpy, you may prefer to obtain just a normal python list:
>>> list(np.cumprod([1, 2, 3, 4, 5]))
[1, 2, 6, 24, 120]
using itertools and operators:
from itertools import accumulate
import operator as op
ip_lst = [1,2,3,4,5]
print(list(accumulate(ip_lst, func=op.mul)))
For a given exclude_list = [3, 5, 8], n = 30, k = 5
I'd like to pick 5(k) random numbers between 1 and 30.
But I should not pick numbers in the exclude_list
Suppose exclude_list, n could be potentially large.
When there's no need for exclusion, it is easy to get k random samples
rand_numbers = sample(range(1, n), k)
So to get the answer, I could do
sample(set(range(1, n)) - set(exclude_numbers), k)
I read that range keeps one number in memory at a time.
I'm not quite sure how it affects the two lines above.
The first question is, does the following code puts all n numbers in memory or does it put each number at a time?
rand_numbers = sample(range(1, n), k)
2nd question is, if the above code indeed puts one number at a time in memory, can I do the similar with the additional constraint of the exclusion list?
Sample notes in sample's docstring:
To choose a sample in a range of integers, use range as an argument.
This is especially fast and space efficient for sampling from a
large population: sample(range(10000000), 60)
I can test this on my machine:
In [11]: sample(range(100000000), 3)
Out[11]: [70147105, 27647494, 41615897]
In [12]: list(range(100000000)) # crash/takes a long time
One way to sample with an exclude list efficiently is to use the same range trick but "hop over" the exclusions (we can do this in O(k * log(len(exclude_list))) with the bisect module:
import bisect
import random
def sample_excluding(n, k, excluding):
# if we assume excluding is unique and sorted we can avoid the set usage...
skips = [j - i for i, j in enumerate(sorted(set(excluding)))]
s = random.sample(range(n - len(skips)), k)
return [i + bisect.bisect_right(skips, i) for i in s]
and we can see it working:
In [21]: sample_excluding(10, 3, [2, 4, 7])
Out[21]: [6, 3, 9]
In [22]: sample_excluding(10, 3, [1, 2, 8])
Out[22]: [0, 4, 3]
In [23]: sample_excluding(10, 6, [1, 2, 8])
Out[23]: [0, 7, 9, 6, 3, 5]
Specifically we've done this without using O(n) memory:
In [24]: sample_excluding(10000000, 6, [1, 2, 8])
Out[24]: [1495143, 270716, 9490477, 2570599, 8450517, 8283229]
Trying to 'shuffle' a list with even number of items. Splitting the list, L in half and alternating taking an element from each.
I've tried pop, but that approach was unable to get me to a one-liner. (while loop) and I know there is likely some more succinct way to move through it.
The shuffle from random isn't exactly what I need, either – because that randomizes the entire order instead of alternating between the split list.
If a one-liner isn't possible, is that because it's more readable in a while loop?
def shuffle(L):
'''
I apologize in advance for how wet the following code is...
Example:
>>> shuffle([1, 2, 3, 4, 5, 6])
[1, 4, 2, 5, 3, 6]
'''
return [L[:(len(L)//2)][0], L[(len(L)//2):][0], L[:(len(L)//2)][1], L[(len(L)//2):][1], L[:(len(L)//2)][2], L[(len(L)//2):][2]]
other attempt:
def shuffle(L):
x, L_first, L_next, L = len(L), L[:(len(L)//2)], L[(len(L)//2):], []
while len(L) != x:
L.extend([L_first.pop(0), L_next.pop(0)])
return L
Use slice assignment with a step:
def shuffle(l):
result = [None] * len(l)
# Put the first half of l in the even indices of result
result[::2] = l[:len(l)//2]
# Put the second half of l in the odd indices of result
result[1::2] = l[len(l)//2:]
return result
If I understand correctly, you could also opt for itertools.chain.from_iterable after zipping to get the alternating effect.
from itertools import chain
def shuff(l):
return list(chain.from_iterable(zip(l[:len(l)//2], l[len(l)//2:])))
Demo
>>> shuff(list(range(1, 7))
[1, 4, 2, 5, 3, 6]
One possibility (requires an external library but the recipe can also be found in the itertools-recipes section) is:
from iteration_utilities import roundrobin
def shuffle(L):
return list(roundrobin(L[:len(L)//2], L[len(L)//2:]))
This is probably slower than list assignment but it also works for arbitary amounts of iterables without problems and it doesn't require odd-sized-input handling:
>>> shuffle([1, 2, 3, 4, 5, 6, 7])
[1, 4, 2, 5, 3, 6, 7]
>>> shuffle([1, 2, 3, 4, 5, 6])
[1, 4, 2, 5, 3, 6]
I did some timings and #user2357112 definetly has the fastest solution but my solution is at least on the second place (note that this graph is in log-log, that means the difference in absolute terms may seem smaller than it really is!):
Disclaimer: I'm the author of that iteration_utilities library.
list comprehension with index calculation using modulo and floor division
[ L[(i + (i % 2)*len(L))//2] for i in range(len(L)) ] # for case of even len(L)
still one line for the general case
[ L[i//2 + (i % 2)*len(L)//2] for i in range(2*(len(L)//2)) ] + [L[-1]]*(len(L) % 2)
the index calc (i + (i % 2)*len(L))//2
can be parsed as adding
i//2 which gives 0, 0, 1, 1, 2, 2 ...
and
(i % 2)*len(L)//2 where (i % 2) alternates 0, 1 for even/odd i
0, len(L)//2, 0, len(L)//2, 0, len(L)//2 ...
sum:
0, len(L)//2, 1, 1 + len(L)//2, 2, 2 + len(L)//2 ...
Found two solutions. First one is very unpythonic (using python 2.7)
a = [1, 2, 3, 4, 5, 6] # intial array
Method One (using string magic):
[int(p) for p in ' '.join([str(x) + ' ' + str(y) for x, y in zip(a[:len(a) / 2], a[len(a) / 2:])]).split(' ')]
Method Two:
[i for Tuple in zip(a[:len(a) / 2], a[len(a) / 2:]) for i in Tuple]