compare elements of two lists - python

A = [a1, a2, a3...] #a1<a2<a3...
B = [b1, b2...] #b1<b2<b3...
A and B are disjoint. are I do not know the number of elements and the value of them in A/B in advance. I want to compare the value of the elements in both list and delete elements iff:
delete a[i+1] if there is no b[j] such that a[i]<b[j]<a[i+1]
delete b[i+1] if there is no a[j] such that b[i]<a[j]<b[i+1]
At the end, I want to separate list, not a combination of A and B.
For example, If A[0] < B[0], A = [1, 10, 40], B = [15, 30]. Compare A[1] and B[0] first. Delete 10 because no element in B are in between 1 and 15.
Then delete 15 since no element exist anymore btw 15 and 30. The output should be: if you try to order the elements of the new 2 lists, it should be A[0]<B[0]<A[1]<B[1]<...
If A[0] > B[0], vice versa.

a = [1, 10, 40]
b = [15, 30]
srcs = [a, b]
dsts = [[], []]
prev_which = -1
while all(srcs):
which = int(srcs[0][0] > srcs[1][0])
elem = srcs[which].pop(0)
if prev_which != which:
dsts[which].append(elem)
prev_which = which
for src, dst in zip(srcs,dsts):
if src:
dst.append(src.pop(0))
a, b = dsts
returns:
a = [1, 40]
b = [15]
and for
a = [3, 4, 6, 7, 8, 9]
b = [1, 2, 5, 10]
it returns [3, 6] and [1, 5, 10].
EDIT: another possibility:
import itertools as it
import operator as op
a = [3, 4, 6, 7, 8, 9]
b = [1, 2, 5, 10]
srcs = [a, b]
dsts = [[], []]
for which, elems in it.groupby(sorted((x, i) for i in (0,1) for x in srcs[i]), key=op.itemgetter(1)):
dsts[which].append(next(elems)[0])
a, b = dsts

I came up with this before you edit. But it seems the output is not what you expect. Anyway, it might help you get on the right track:
a = range(0, 30, 3)
b = range(0, 20, 2)
a.sort()
b.sort()
A = [a[i+1] for i in range(len(a)-1) if any(a[i]<b[j]<a[i+1] for j in range(len(b)-1))]
B = [b[i+1] for i in range(len(b)-1) if any(b[i]<a[j]<b[i+1] for j in range(len(a)-1))]
result = sorted(A+B)
print a, b
print result
This is "literally" what you expressed, but the result here is not what you expect. I'll try to improve this.

So, if I am reading correctly, the desired output from your example is [1,40] and [15], yes?
If so, the following would get the right result, but I am certain there is a tighter way to do it.
a = [1, 10, 40]
b = [15, 30]
c = sorted([[e_a,'a'] for e_a in a] + [[e_b,'b'] for e_b in b])
indices = []
for i in range(len(c)-1):
if c[i][1] == c[i+1][1]:
indices.append(i+1)
for e in sorted(indices, reverse=True):
del c[e]
a,b = [e[0] for e in c if e[1]=='a'],[e[0] for e in c if e[1]=='b']
First - Merging the lists and sorting them, while keeping track of which list they came from.
Second - Then remove all instances where the next item in the merged list is from the same source list.
Third - updating a and b.

I knew using the bisect module could be a good solution:
>>> def sort_relative(L1, L2):
# Switch if needed
if L1[0] > L2[0]:
L1, L2 = L2, L1
i = 0
while i + 1 < max(len(L1), len(L2)):
try:
# We know that L1[i] < L2[i]
# Get indexes where L2[i] and L2[i + 1] should be inserted
i11 = bisect.bisect_left(L1, L2[i])
i12 = bisect.bisect_left(L1, L2[i + 1])
# This condition allows to know if one element of L1
# was found between L2[i] and L2[i + 1]:
# - if so, we have L1[i] < L2[i] < L1[i + 1] < L2[i + 1]
# - else we have L1[i] < L2[i] < L1[i + 1] but
# we don't know between L1[i + 1] and L2[i + 1]
if L1[i11] < L2[i + 1]:
L1 = L1[:i + 1] + [L1[i11]] + L1[i12:]
index1, index2 = i + 1, i + 2
else:
L1 = L1[:i + 1] + L1[i12:]
index1, index2 = i, i + 1
# Do the same kind of symetric search,
# with indexes computed above
i21 = bisect.bisect_left(L2, L1[index1])
i22 = bisect.bisect_left(L2, L1[index2])
if L2[i21] < L1[index2]:
L2 = L2[:index1] + [L2[i21]] + L2[i22:]
else:
L2 = L2[:index1] + L2[i22:]
# Little trick not to test indexes everywhere:
# lists are browsed at the same time
except IndexError:
pass
# Next index !
i += 1
# Show result
print 'L1:', L1, '- L2:', L2
>>> sort_relative([1, 10, 50], [15, 30])
L1: [1, 50] - L2: [15]
>>> sort_relative([17, 18, 50], [15, 30])
L1: [15, 30] - L2: [17, 50]
>>> sort_relative([1, 10, 12, 25, 27, 50], [15, 30, 70])
L1: [1, 25, 50] - L2: [15, 30, 70]
>>> sort_relative([1, 10, 12, 25, 27, 50], [15, 30, 34, 70])
L1: [1, 25, 50] - L2: [15, 30, 70]
>>>
I didn't take into account the case when a number is both in A and B.

Related

Change value of index in a for loop python

AN implementation of Crohn(Крона) algorithm used in Scheduling theory, is it possible to change the data of the current index in a for loop in python?
I have a code like so;
link to the full code
#list1 is a 2d array
list1 = [[12, 3, 17], [14], [10, 12, 15]]
cond = 1
while cond:
d = delta(sum_calc(list1))
#delta() finds the difference between the list with the highest sum
#and the list with the minimum sum, then returns the
#difference(d[0]), index_of_list_with_max_sum(d[1]), #index_of_list_with_min_sum(d[2])
#d = [23, 2, 1]
if cond == 0:
break
else:
for i in list1[d[1]]:
if d[0] > i:
move(list1[d[1]], list1[d[2]])
#move() moves the min element from list1, to list2
else:
cond = 0
what I am trying to do is, given an index, loop through the elements of that list, then check if num is greater than i (element), after that we do some operations, like moving the smallest element from the current list we are looping from, to another list. then we have some operations, after that I want to change the value of i, to something like
#the index might change
i = l[index]
the problem I am facing is that when I do that, it continues looping from the first index.
Is there a way I can reset it, so that it starts looping from other elements?
I HAVE USED A WHILE LOOP, because I want the procedure to repeat itself and until d[0] !> i:
#since the list with the highest sum is list1[2], we got 2 from d,
#next step is to loop through the elements in that list and check if
#there's an element which is less than d[0](the difference between max and min sum)
#expected results
#after first iteration
list1 = [[12, 3, 17], [14, 10], [12, 15]]
d = [8, 0, 1]
#after second iteration
list1 = [[12, 17], [14, 10, 3], [12, 15]]
d = [2, 0, 1]
#the problem is it stops here, I want it to calculate delta again then #repeat the process, but after debugging, i found that in the second #iteration i = 15, which the second element of the list, but it should #be like that.
Try to combine while loop with index ?
example:
lst = [1,2,3]
idx = 0
while idx < len(lst):
print(lst[idx])
idx += 1
if idx == len(lst):
# Reset index
idx = 0
EDIT
After debugging I found your errors - You havent been assigning the new delta result to d where you have been referencing from your code thus you never got the new indexes
cond = True
idx = 0
while cond and idx < len(l[d[1]]):
if d[0] > l[d[1]][idx]:
move(l[d[1]], l[d[2]])
s = sum_calc(l)
d = delta(s)
print("l =", l)
print("s =", s)
print("d =", d)
print("")
idx = 0
else:
cond = False
idx += 1
Output:
l = [[12, 3, 17], [14, 10], [12, 15]]
s = [32, 24, 27]
d = [8, 0, 1]
l = [[12, 17], [14, 10, 3], [12, 15]]
s = [29, 27, 27]
d = [2, 0, 1]

Is there a fast way to compare every two rows in a 2-dimensional array?

So I've got a 2-dimensional array, say list:
list = [[x11, x12, x13, x14],
[x21, x22, x23, x24],
...]
Some samples of list are:
# numbers in list are all integers
list = [[0, 17, 6, 10],
[0, 7, 6, 10],
]
list = [[6, 50, 6, 10],
[0, 50, 6, 10],
]
list = [[6, 16, 6, 10],
[6, 6, 6, 10],
]
list = [[0, 50, 6, 10],
[6, 50, 6, 10],
[6, 40, 6, 10]
]
list = [[0, 27, 6, 10],
[0, 37, 6, 10],
]
I need to iterate every two rows, for example [x11, x12, x13, x14] and [x21, x22, x23, x24], and do some complex comparisons:
cnt1 = cnt2 = cnt3 = cnt4 = cnt5 = 0
for i in range(0, length):
for j in range(i + 1, length):
if (list[i][0] + list[i][2] == list[j][0] or list[j][0] + list[j][2] == list[i][0]) and \
list[i][1] == list[j][1]:
cnt1 += 1
if list[i][3] == list[j][3]:
cnt2 += 1
else
cnt3 += 1
elif (list[i][1] + list[i][3] == list[j][1] or list[j][1] + list[j][3] == list[i][1]) and \
list[i][0] == list[j][0]:
cnt4 += 1
if list[i][2] == list[j][2]:
cnt2 += 1
else
cnt3 += 1
else
cnt5 += 1
# do something with the counts
length here is usually small, but this nested loop runs thousands of times, so it takes very long to finish the program. I've read some tutorials of vectorizing in Numpy, but cannot figure out how to edit the code since the logic is kind of complex. Is there a way to optimize my code, even for a little bit? Any help would be highly appreciated. Thanks in advance!
I am posting a solution for how to do this for the first if and the subsequent if and else conditions.
You can follow similar logic to do the same for the rest as well.
import numpy as np
arr = np.array([[0, 17, 6, 10],
[0, 7, 6, 10],
[6, 50, 6, 10],
[0, 50, 6, 10],
[6, 16, 6, 10],
[6, 6, 6, 10],
[0, 50, 6, 10],
[6, 50, 6, 10],
[6, 40, 6, 10],
[0, 27, 6, 10],
[0, 37, 6, 10]])
N = len(arr)
cnt1 = cnt2 = cnt3 = cnt4 = cnt5 = 0
for i in range(0, N):
for j in range(i + 1, N):
if (arr[i][0] + arr[i][2] == arr[j][0] or arr[j][0] + arr[j][2] == arr[i][0]) and \
arr[i][1] == arr[j][1]:
cnt1 += 1
if arr[i][3] == arr[j][3]:
cnt2 += 1
else:
cnt3 += 1
elif (arr[i][1] + arr[i][3] == arr[j][1] or arr[j][1] + arr[j][3] == arr[i][1]) and \
arr[i][0] == arr[j][0]:
cnt4 += 1
if arr[i][2] == arr[j][2]:
cnt2 += 1
else:
cnt3 += 1
else:
cnt5 += 1
# this corresponds to (arr[i][0] + arr[i][2] == arr[j][0] or arr[j][0] + arr[j][2] == arr[i][0])
cnt1_bool_c1 = ((arr[:, 0] + arr[:, 2])[:, None] == arr[:, 0][None, :])
# arr[i][1] == arr[j][1]:
cnt1_bool_c2 = arr[:, 1][:, None] == arr[:, 1][None, :]
# So that i and j are compared only if i != j
cnt1_bool_c2[np.arange(N), np.arange(N)] = False
# doing and of the two previous conditions finishing the very first if condition
cnt1_bool = np.bitwise_and(cnt1_bool_c1, cnt1_bool_c2)
# corresponds to cnt1
cnt1_n = cnt1_bool.sum()
# verified
print(cnt1 == cnt1_n)
# corresponds to arr[i][3] == arr[j][3]
cnt2_bool_c = arr[:, 3][:, None] == arr[:, 3][None, :]
# So that i and j are compared only if i != j
cnt2_bool_c[np.arange(N), np.arange(N)] = False
# correspond to the inner if, count only if these elemets share the same position as the previous elements
cnt2_n1 = np.bitwise_and(cnt1_bool, cnt2_bool_c).sum() # corresponds to the cnt2 += 1 in the first inner condition
# correspond to the inner else, count only if these elemets do not share the same position as the previous elements
cnt3_n1 = np.bitwise_and(cnt1_bool, ~cnt2_bool_c).sum() # corresponds to the cnt3 += 1 in the first inner else condition
In your for loops you are comparing the array [x11, x12, x13, x14] with all following elements ([x21, x22, x23, x24], [x31, x32, x33, x34], [x41, x42, x43, x44], etc..),
then you move on to comparing [x21, x22, x23, x24] with all following elements ([x31, x32, x33, x34], [x41, x42, x43, x44], etc..).
To iterate every 2 rows and compare them 2 by 2 (that means x1 with x2, and then x3 with x4) you want something like this instead:
for i in range(0, length - 1, 2):
j = i + 1;
if (list[i][0] + list[i][2] == list[j][0] or list[j][0] + list[j][2] == list[i][0]) and list[i][1] == list[j][1]:
# do something
if list[i][3] == list[j][3]:
# do something
else
# do something
note that you have also to address the case where the list array has odd size.

Inserting elements of one list into another list at different positions in python

Consider the two lists:
a=[1,2,3]
and
b=[10,20,30],
and a list of positions
pos=[p1,p2,p3]
giving the positions that the elements of b should take in the final list of 6 elements given by the union of a and b, where p1 is the position of b[0]=10, p2 is the position of b[1]=20 and p3 is the position of b[2]=30.
What is the best python approach to this problem?
You could create the output list by extending it with slices of a and appending the next item of b where needed:
def insert(a, b, positions):
# reorder b and positions so that positions are in increasing order
positions, b = zip(*sorted(zip(positions, b)))
out = []
a_idx = 0
it_b = iter(b)
for pos in positions:
slice_length = pos - len(out)
out.extend(a[a_idx:a_idx + slice_length])
out.append(next(it_b))
a_idx += slice_length
out.extend(a[a_idx:])
return out
An example:
a=[1,2,3]
b=[10,20,30]
pos=[0, 1, 5]
insert(a, b, pos)
# [10, 20, 1, 2, 3, 30]
pos = [0, 2, 4]
insert(a, b, pos)
# [10, 1, 20, 2, 30, 3]
pos=[5, 3, 0]
insert(a, b, pos)
# [30, 1, 2, 20, 3, 10]
If you make the indices and values into a dictionary, you can then loop over the range of the combined lengths. If the index is in the dict, use the value, otherwise take the next value from a:
a = [1,2,3]
b = [10,20,30]
pos =[2,0,5]
p_b = dict(zip(pos, b))
it_a = iter(a)
[p_b[i] if i in p_b else next(it_a) for i in range(len(a) + len(b))]
# [20, 1, 10, 2, 3, 30]
You will need to insure that the lengths of the arrays and the positions all make sense. If they don't you can run out of a values which will produce a StopIteration exception.
You use a defaultdict for similar approach, which simplifies the list comprehension at the expense of a slightly more complicated setup:
from collections import defaultdict
a = [1,2,3]
b = [10,20,30]
pos =[4,0,2]
it_a = iter(a)
d = defaultdict(lambda: next(it_a))
d.update(dict(zip(pos, b)))
[d[i] for i in range(len(a) + len(b))]
# [20, 1, 30, 2, 10, 3]

How to get many sums stepping through varying length lists that have specific orders?

My Problem:
I have a list of lists. These lists are varying length e.g. [[2, 1, 5, 3], [2,4,8]
For each item in each list I need to print its sum with the next list item, then the next 2 list items, until I print the sum of all of the list items. Then I move to the second list item and do the same until I have reached the last list item.
The output I need is:
My Desired Output:
2 + 1 = 3
2 + 1 + 5 = 8
2 + 1 + 5 + 3 = 11
1 + 5 = 6
1 + 5 + 3 = 9
5 + 3 = 8
2 + 4 = 6
2 + 4 + 8 = 14
4 + 8 = 12
My (bad) Attempt:
I have tried for hours but have not been able to get close. I was doing something along the lines of the below code but I am wondering if I need to make a recursive function??
for cluster in [[2, 1, 5, 3], [2,4,8]]:
for trip in cluster:
for trip_cluster_index in range(len(cluster)):
if trip != cluster[trip_cluster_index]:
print(cluster, trip, cluster[trip_cluster_index])
O(n^3)
list_sum = [[2, 1, 5, 3], [2,4,8]]
list_out = []
for l in list_sum:
for i in range(1, len(l)):
aux = l[i-1]
for j in range(i, len(l)):
aux += l[j]
list_out.append(aux)
print(list_out)
[3, 8, 11, 6, 9, 8, 6, 14, 12]
O(n^2)
list_sum = [[2, 1, 5, 3], [2,4,8]]
list_out = []
for l in list_sum:
list_1 = []
aux = l[0]
for i in range(1, len(l)):
aux += l[i]
list_1.append(aux)
list_out.extend(list_1)
sum_list = 0
for j in range(0, len(list_1)-1):
sum_list += l[j]
list_2 = [x-sum_list for x in list_1[j+1:]]
list_out.extend(list_2)
print(list_out)
[3, 8, 11, 6, 9, 8, 6, 14, 12]
Inverted O(n^3)
list_sum = [[2, 1, 5, 3], [2,4,8]]
list_out = []
for l in list_sum:
for i in range(0,len(l)-1):
aux = sum(l[i:])
list_out.append(aux)
for j in range(len(l)-1,i+1,-1):
aux -= l[j]
list_out.append(aux)
print(list_out)
[11, 8, 3, 9, 6, 8, 14, 6, 12]
This should give you what you want.
n = -1
listy = [[1,1,1],[2,2,2],[3,3,3]]
for l in listy:
while n < len(listy)-1:
n +=1
total = sum(l) + sum(listy[n])
print(total)
I assumed that your output must contain the whole equations, and this is what I came up with:
L=[[2, 1, 5, 3], [2,4,8]]
for i in L:
for j in range(len(i)):
for k in range(j+2, len(i)+1):
print(' + '.join([str(n) for n in i[j:k]]), '=', sum(i[j:k]))
Hope it is what you were looking for!

Retaining information through recursion under strict conditions (Python)

My partner and I are working on a problem in which we need to reduce an array and run operations on the pieces (in this case, of 2), we are reducing with recursion and sending the left-right hand sides of the array back into function.
We have all this working fine, and for example, using the array [2, 3, 4, 5, 6, 7, 8, 9] we get all the pieces we need.
The problem is, we need each of these pieces to then run the math operation on the next piece we get.
So, for example we recur down to [2, 3] and transform that into [5, -1]. Then we recur down to [4, 5] and change that into [9, -1] and combine them into [14, -2, -4, 0]. This is the left hand side of our array. And this works great. And then it does the right hand side, and it gets the answer we want. This works great.
The problem, is we now need both parts together (can't use global variables). And we have been stuck here for several hours. We can only pass the simplified array through the recursion, and if we initialize an array to hold both parts it will get reset when the right-hand side starts.
Thanks
EDIT: Code: The H is starting matrix that is given, but it doesn't matter, it as no relevance its just there so the unit test goes through (We could use it, but we don't really know how)
The imput for x is [2,3,4,5,6,7,8,9]
def hadmatmult(H, x):
d = 0
n = len(x)
first = 0
last = len(x)
a = [0] * math.floor(n/2)
b = [0] * math.floor(n/2)
if n == 2:
temp1 = x[0]
x[0] = temp1 + x[1]
x[1] = temp1 - x[1]
else:
mid = math.floor((first+last)/2)
for i in range(first, mid):
a[i] = x[i]
hadmatmult(H, a)
for j in range(mid, last):
b[d] = x[j]
d = d + 1
hadmatmult(H, b)
if(len(a) == 2:
adds = [0] * len(a)
subs = [0] * len(a)
for t in range(0, len(a)):
adds[t] = a[t] + b[t]
subs[t] = a[t] - b[t]
#alladds = alladds + adds
#allsubs = allsubs + subs
print(adds)
print(subs)
Output: This outputs the parts, [14, -2, -4, 0] and [30, -2, -4, 0]
If you must recurse, do so in a simpler way. Strip off the elements you need and recurse through the rest.
x = [2,3,4,5,6,7,8,9]
def recurse_and_stuff(x):
if len(x) == 0:
# base case
return []
a, b, = x[:2], x[2:4]
m,n,o,p = [a[0]+a[1], a[0]-a[1], b[0]+b[1], b[0]-b[1]]
result = [[m+o, n+p, m-o, n-p]] + recurse_and_stuff(x[4:])
return result
>>> x = [2,3,4,5,6,7,8,9]
>>> recurse_and_stuff(x)
[[14, -2, -4, 0], [30, -2, -4, 0]]
>>> x = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
>>> recurse_and_stuff(x)
[[14, -2, -4, 0], [30, -2, -4, 0], [46, -2, -4, 0], [62, -2, -4, 0]]
# works for longer inputs as long as len(x) % 4 == 0
To recurse all the way down:
import itertools
def do_the_needful(a,b):
try:
zipped = zip(a,b)
except TypeError:
# a and b are ints, not lists of ints
zipped = [(a,b)]
return itertools.chain.from_iterable(zip(*[[aa+bb, aa-bb] for aa,bb in zipped]))
def recurse_and_stuff(x):
if len(x) == 1:
return list(x[0])
# if you only have to iterate through this, there's no
# need to call `list(...)` here.
return recurse_and_stuff([do_the_needful(x[i], x[i+1]) for i in range(0, len(x), 2)])

Categories