I have three lists and want to call a function which takes 3 arguments with all possible combinations of values of that 3 lists.
And if a condition is met, print the 3 values of the combination.
What is the fastest and best way to do that?
Here are my three lists:
a = np.linspace(0.01,constants.pi/2,50)
b = np.arange(20,62,2)
c = np.arange(0.3,1.5,0.1)
And I want to call a function let's say testAllCombination(a[i],b[j],c[k]) in each iteration, and if a the value returned is > 0, print the 3 values a[i], b[j] and c[k]. Is it possible to do this in a simple way?
It seems you need the Cartesian product of your lists.
import itertools
list(itertools.product(a,b,c))
Note that this operation results in 50*21*12=12600 triples of items from a,b,c.
if the position is fixed to (a,b,c), you may consider simple loop. Otherwise if you need to change to other combinations like (b,c,a), (c,b,a)... use itertools
a = np.linspace(0.01,3.14/2,50)
b = np.arange(20,62,2)
c = np.arange(0.3,1.5,0.1)
myCombination=[]
for i in a:
for j in b:
for k in c:
myCombination.append((i,j,k))
print(myCombination)
for item in myCombination:
testCondition(item)
https://docs.python.org/2/library/itertools.html
Related
Given a list containing N sublists of multiple lengths, find all unique combinations of a k size, selecting only one element from each sublist.
The order of the elements in the combination is not relevant: (a, b) = (b, a)
sample_k = 2
sample_list = [['B1','B2','B3'], ['T1','T2'], ['L1','L2','L3','L4']]
expected_output =
[
('B1', 'T1'),('B1', 'T2'),('B1', 'L1'),('B1', 'L2'),('B1', 'L3'),('B1', 'L4'),
('B2', 'T1'),('B2', 'T2'),('B2', 'L1'),('B2', 'L2'),('B2', 'L3'),('B2', 'L4'),
('B3', 'T1'),('B3', 'T2'),('B3', 'L1'),('B3', 'L2'),('B3', 'L3'),('B3', 'L4'),
('T1', 'L1'),('T1', 'L2'),('T1', 'L3'),('T1', 'L4'),
('T2', 'L1'),('T2', 'L2'),('T2', 'L3'),('T2', 'L4')
]
Extra points for a pythonic way of doing it
Speed/Efficiency matters, the idea is to use in a list with hundreds of lists ranging from 5 to 50 in length
What I have been able to accomplish so far:
Using for and while loops to move pointers and build the answer, however I am having a hard time figuring out how to include K parameter to set the size of tuple combination dinamically. (not really happy about it)
def build_combinations(lst):
result = []
count_of_lst = len(lst)
for i, sublist in enumerate(lst):
if i == count_of_lst - 1:
continue
else:
for item in sublist:
j = 0
while i < len(lst)-1:
while j <= len(lst[i+1])-1:
comb = (item, lst[i+1][j])
result.append(comb)
j = j + 1
i = i + 1
j = 0
i = 0
return result
I've seen many similar questions in stack overflow, but none of them addressed the parameters the way I am trying to (one item from each list, and the size of the combinations being a params of function)
I tried using itertools combinations, product, permutation and flipping them around without success. Whenever using itertools I have either a hard time using only one item from each list, or not being able to set the size of the tuple I need.
I tried NumPy using arrays and a more math/matrix approach, but didn't go too far. There's definitely a way of solving with NumPy, hence why I tagged numpy as well
You need to combine two itertools helpers, combinations to select the two unique ordered lists to use, then product to combine the elements of the two:
from itertools import combinations, product
sample_k = 2
sample_list = [['B1','B2','B3'], ['T1','T2'], ['L1','L2','L3','L4']]
expected_output = [pair
for lists in combinations(sample_list, sample_k)
for pair in product(*lists)]
print(expected_output)
Try it online!
If you want to get really fancy/clever/ugly, you can push all the work down to the C layer with:
from itertools import combinations, product, starmap, chain
sample_k = 2
sample_list = [['B1','B2','B3'], ['T1','T2'], ['L1','L2','L3','L4']]
expected_output = list(chain.from_iterable(starmap(product, combinations(sample_list, sample_k))))
print(expected_output)
That will almost certainly run meaningfully faster for huge inputs (especially if you can loop the results from chain.from_iterable directly rather than realizing them as a list), but it's probably not worth the ugliness unless you're really tight for cycles (I wouldn't expect much more than a 10% speed-up, but you'd need to benchmark to be sure).
How do I get two numbers in a loop?
I tried to use the zip function.
matrix = [[0,1,1,2],[0,5,0,0],[2,0,3,3]]
print(len(matrix[0]))
print(range(len(matrix[0])))
for i, x in zip(range(len(matrix)), range(len(matrix[0]))):
print(x)
But instead of my output being 0,1,2,3, it only prints up to 2
Is this what you're looking for?
matrix = [[0,1,1,2],
[0,5,0,0],
[2,0,3,3]]
for i in matrix:
for i, x in enumerate(i):
print(i, x)
Your matrix has only 3 indexes, so its range will produce 0, 1, 2. zip will only zip objects up to the length of the shortest object. If you want to zip to the length of the longest then you should import zip_longest from the itertools module. It will then zip based on the longest list and produce None in place of the items in the shorter list.
from itertools import zip_longest
matrix = [[0,1,1,2],
[0,5,0,0],
[2,0,3,3]]
print(len(matrix[0]))
print(range(len(matrix[0])))
for i, x in zip_longest( range(len(matrix)) , range(len(matrix[0])) ):
print(i, x)
OUTPUT
3
range(0, 4)
0 0
1 1
2 2
None 3
However this seems very verbose and clunky, you might be better to state what your really trying to achieve as there may be a much cleaner way to do it.
I think you need range for your given list.....
I am improving #Martin Schere answer based on your doubt....
matrix = [[0,1,1,2],
[0,5,0,0],
[2,0,3,3]]
for i,x in enumerate(matrix):
print("range="+str(i))#0,1,2
print("list="+str(x))#prints your matrix
hope...it will help you...
I'm currently trying to do project Euler problem 18 (https://projecteuler.net/problem=18), using the 'brute force' method to check all possible paths. I've just been trying the smaller, 'model' triangle so far.
I was using list comprehension to create a list of lists where the inner lists would contain the indices for that line, for example:
lst = [[a,b,c,d] for a in [0] for b in [0,1] for c in [0,1,2] for d in
[0,1,2,3] if b == a or b == a + 1 if c == b or c == b + 1 if d == c or d ==
c + 1]
This gives me the list of lists I want, namely:
[[0,0,0,0],[0,0,0,1],[0,0,1,1],[0,0,1,2],[0,1,1,1],[0,1,1,2],[0,1,2,2],
[0,1,2,3]]
Note: the if conditions ensure that it only moves to adjacent numbers in the next row of the triangle, so that
lst[i][j] = lst[i][j-1] or lst[i][j] = lst[i][j]-1
After I got to this point, I intended that for each of the inner lists, I would take the numbers associated with those indices (so [0,0,0,0] would be 3,7,2,8) and sum over them, and this way get all of the possible sums, then take the maximum of those.
The problem is that if I were to scale this up to the big triangle I'd have fifteen 'for's and 'if's in my list comprehension. It seems like there must be an easier way! I'm pretty new to Python so hopefully there's some obvious feature I can make use of that I've missed so far!
What an interesting question! Here is a simple brute force approach, note the use of itertools to generate all the combinations, and then ruling out all the cases where successive row indices differ by more than one.
import itertools
import numpy as np
# Here is the input triangle
tri = np.array([[3],[7,4],[2,4,6],[8,5,9,3]])
indices = np.array([range(len(i)) for i in tri])
# Generate all the possible combinations
indexCombs = list(itertools.product(*indices))
# Generate the difference between indices in successive rows for each combination
diffCombs = [np.array(i[1:]) - np.array(i[:-1]) for i in indexCombs]
# The only combinations that are valid are when successive row indices differ by 1 or 0
validCombs = [indexCombs[i] for i in range(len(indexCombs)) if np.all(diffCombs[i]**2<=1)]
# Now get the actual values from the triangle for each row combination
valueCombs = [[tri[i][j[i]] for i in range(len(tri))] for j in validCombs]
# Find the sum for each combination
sums = np.sum(valueCombs, axis=1)
# Print the information pertaining to the largest sum
print 'Highest sum: {0}'.format(sums.max())
print 'Combination: {0}'.format(valueCombs[sums.argmax()])
print 'Row indices: {0}'.format(indexCombs[sums.argmax()])
The output is:
Highest sum: 23
Combination: [3, 7, 4, 9]
Row indices: (0, 0, 1, 0)
Unfortunately this is hugely intensive computationally, so it won't work with the large triangle - but there are definitely some concepts and tools that you could extend to try get it to work!
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.