Is it possible to make this code run faster?
a,b=map(int,input().split())
c=list(map(int,input().split()))
d=list(map(int,input().split()))
e=list(map(int,input().split()))
happy=0
for i in c:
if i in d:
happy=happy+1
elif i in e:
happy=happy-1
print(happy)
The code has to increment or decrement happy variable depending on if elements of c list are present in d or e lists. This code runs fine for small number of elements in c, d and e lists. But when there are many elements , the code execution is terminated due to timeout.
What can I do to make it run faster?
You can avoid the loop.
The variable happy is essentially the difference between the number of elements found in d and the number of elements found in e.
Could there be duplicates in c?
If you want to count the same element only once, then you can use set, that implicitly remove duplicates:
set_c = set(c)
happy_match = set_c.intersect(d)
unhappy_match = set_c.intersect(e)
happy = len(happy) - len(unhappy_match)
If you want to count each occurrence (including duplicates), then you can apply the same logic to lists:
happy_match = sum(1 if el in d else 0 for el in c)
unhappy_match = sum(1 if el in e else 0 for el in c)
happy = len(happy) - len(unhappy_match)
Related
I have a list with repeating elements, i.e. orig = [1,1,1,2,2,3].
I want to create a derangement b = f(orig) such that for every location value in b is different from value in orig:
b[i] != orig[i], for all i
I know a solution when all element in orig are unique, but this is a harder case.
Developing a solution in python, but any language will do.
The not so-efficient solution is clearly
import itertools
set([s for s in itertools.permutations(orig) if not any([a == b for a, b in zip(s, orig)])])
A second method and first improvement is using this perm_unique:
[s for s in perm_unique(orig) if not any([a == b for a, b in zip(s, orig)])]
A third method is to use this super quick unique_permutations algorithm.
import copy
[copy.copy(s) for s in unique_permutations(orig) if not any([a == b for a, b in zip(s, orig)])]
In my notebook with %%timeit the initial method takes 841 µs, and we improve to 266 µs and then to 137 µs.
Edit
Couldn't stop considering, made a small edit of the second method. Didn't have the time to dive into the last method. For explanation, first see the original post (link above). Then I only added the check and el != elements[depth] which forces the condition of the derangement. With this we arrive at a running time of 50 µs.
from collections import Counter
def derangement_unique(elements):
list_unique = Counter(elements)
length_list = len(elements) # will become depth in the next function
placeholder = [0]*length_list # will contain the result
return derangement_unique_helper(elements, list_unique, placeholder, length_list-1)
def derangement_unique_helper(elements, list_unique, result_list, depth):
if depth < 0: # arrived at a solution
yield tuple(result_list)
else:
# consider all elements and how many times they should still occur
for el, count in list_unique.items():
# ... still required and not breaking the derangement requirement
if count > 0 and el != elements[depth]:
result_list[depth] = el # assignment element
list_unique[el] -= 1 # substract number needed
# loop for all possible continuations
for g in derangement_unique_helper(elements, list_unique, result_list, depth-1):
yield g
list_unique[el] += 1
list(derangement_unique(orig))
If your list contains significant part of duplicates, it might be hard to find derangement quickly.
In this case you can try graph approach.
Treat initial list to make a graph where every item is connected with non-equal elements (easy for sorted list).
Then build perfect matching (if number of element is even) or near-perfect matching (for odd count, here you'll need find some suitable pair and join single node to it).
Edges of matching indicate swaps to make derangement.
Python library networkx should contain needed methods.
I am trying to compare two lists for the same element at the same index. The idea is to verify whether both lists contain same element at the same index. If yes, I want to count such occurrences. Here is my code:
count = 0
a = ['.ps2\n >|<4 *|*.ps2xml', '.c\n >|<2 *|*.wsc', '.h\n >|<2 *|*.wsh', '.c\n >|<2 *|*.chm', '.h\n >|<2 *|*.hta' ]
b = ['.ps2xml', '.chm', '.hta']
for x in a:
for y in b:
if y==x[x.index(" *|*")+4:]:
print "match"
count += 1
print count
This gives me a count of 3. What I expect is 1 because only first element of b matched with a's first element. The second element of both lists differ. The third elements are also different. The remaining elements in list a should not count as there is no such index in b.
Hope it makes sense. Thanks
In that case, you should not use nested loops (since this means you will repeat the search over b for each line in a); but use a zip(..):
for x,y in zip(a,b):
if y==x[x.index(" *|*")+4:]:
print "match"
count += 1
print count
zip takes some iterators and generates tuples. In this case the i-th tuple is thus (a[i],b[i]) so to speak.
Short solution using min() function(to get a limit size of compared sequences):
for i in range(min([len(a), len(b)])):
if (a[i][a[i].index('*|*') + 3:] == b[i]):
count += 1
print(count)
The output:
1
does the match have to be qualified as following '*|*' ?
if not then really simple is:
sum([1 for e, f in zip(a, b) if f in e])
or in later versions of python where iterator args are automatically unpacked:
sum(f in e for e, f in zip(a, b)) # relies on bools True, False = ints 1, 0
if the match is just the last bit you could split
'.ps2\n >|<4 *|*.ps2xml'.split(" *|*")
Out[13]: ['.ps2\n >|<4', '.ps2xml']
'.ps2\n >|<4 *|*.ps2xml'.split(" *|*")[1]
Out[14]: '.ps2xml'
sum([1 for e, f in zip(a, b) if f in e.split(" *|*")[1]])
and while sum() is more "intentional" len() could be used for a speed advantage since it doesn't have to iterate over the list
Here's my issue:
I have a large integer (anywhere between 0 and 2^32-1). Let's call this number X.
I also have a list of integers, unsorted currently. They are all unique numbers, greater than 0 and less than X. Assume that there is a large amount of items in this list, let's say over 100,000 items.
I need to find up to 3 numbers in this list (let's call them A, B and C) that add up to X.
A, B and C all need to be inside of the list, and they can be repeated (for example, if X is 4, I can have A=1, B=1 and C=2 even though 1 would only appear once in the list).
There can be multiple solutions for A, B and C but I just need to find one possible solution for each the quickest way possible.
I've tried creating a for loop structure like this:
For A in itemlist:
For B in itemlist:
For C in itemlist:
if A + B + C == X:
exit("Done")
But since my list of integers contains over 100,000 items, this uses too much memory and would take far too long.
Is there any way to find a solution for A, B and C without using an insane amount of memory or taking an insane amount of time? Thanks in advance.
you can reduce the running time from n^3 to n^2 by using set something like that
s = set(itemlist)
for A in itemlist:
for B in itemlist:
if X-(A+B) in s:
print A,B,X-(A+B)
break
you can also sort the list and use binary search if you want to save memory
import itertools
nums = collections.Counter(itemlist)
target = t # the target sum
for i in range(len(itemlist)):
if itemlist[i] > target: continue
for j in range(i+1, len(itemlist)):
if itemlist[i]+itemlist[j] > target: continue
if target - (itemlist[i]+itemlist[j]) in nums - collections.Counter([itemlist[i], itemlist[j]]):
print("Found", itemlist[i], itemlist[j], target - (itemlist[i]+itemlist[j]))
Borrowing from #inspectorG4dget's code, this has two modifications:
If C < B then we can short-circuit the loop.
Use bisect_left() instead of collections.Counter().
This seems to run more quickly.
from random import randint
from bisect import bisect_left
X = randint(0, 2**32 - 1)
itemset = set(randint(0,X) for _ in range(100000))
itemlist = sorted(list(itemset)) # sort the list for binary search
l = len(itemlist)
for i,A in enumerate(itemlist):
for j in range(i+1, l): # use numbers above A
B = itemlist[j]
C = X - A - B # calculate C
if C <= B: continue
# see https://docs.python.org/2/library/bisect.html#searching-sorted-lists
i = bisect_left(itemlist, C)
if i != l and itemlist[i] == C:
print("Found", A, B, C)
To reduce the number of comparisons, we enforce A < B < C.
if i have these two lists...
a = [[1,y,y],[2,x,x],[3,x,x],[4,y,y]
b = [[1,x,x],[4,x,x]
And i want to combine them such that a new list (c) is formed that contains each row of (a) if the first value of (a) is equal with the first value of (b)
c = [[1,y,y],[4,y,y]]
this is what i have tried so far...
for i in xrange(0,1):
for j in xrange(0,3):
if b[i][0] == a[j][0]:
c[i:] = a[[j:]
overwriting values in the c array isnt a problem due to the type of data in (a).
Im just really stuck on this, any help on whether im on the right track or if i should try something else would be greatly appreciated!
Alternatively, you can consider using sets and list comprehensions.
a = [[1,'y','y'],[2,'x','x'],[3,'x','x'],[4,'y','y']]
b = [[1,'x','x'],[4,'x','x']]
b0s = set(l[0] for l in b)
c = [l for l in a if l[0] in b0s]
print(c)
Try this:
c.append(a[j])
inside your IF statement.
I am currently writing a program that uses itertools, and one piece of it does not seems to functioning properly. I would like the input that determines the length of the lists the permutation function outputs to be equal to length of the list from which it generates its outputs. In other words, I have
import itertools
b = 0
c = 9
d = [0,1,2]
e = len(d)
while b < c:
d.append(b)
b = b+1
print([x for x in itertools.permutations(d,e)])
And I would like this to generate all the possible permutations of d that are equal to this length. I have been experimenting with this and it seems that second determiner must be an integer. I even tried making a new variable, f, and having f = int(e) and then replacing e with f in the print statement, but with no success. With either of these all I got was [()]
Thanks for your help.
You need to set e after you build the list. len(d) returns a value, not a reference to the list's length.
d = range(0,9) # build an arbitrary list here
# this creates a list of numbers: [0,1,2,3,4,5,6,7,8]
e = len(d)
print list(itertools.permutations(d, e))
Note that the number of permutations is very large, so storing all of them in a list will consume large amounts of memory - you'd be better off with this:
d = range(0,9)
e = len(d)
for p in itertools.permutations(d, e):
print p