A Pythonic approach to a list comparison/generation script - python-3.x

Consider I've got a list of 2-tuples named listuple and another simple list named list0. I want to generate a list of 1s and -1s based on comparing my two given lists.
def Vectomparison (listuple, list0):
result = []
for EachElement in listuple:
if EachElement [0] in list0:
result.append (1)
else:
result.append (-1)
return result
But I really think that this not a Pythonic approach. Any idea for making this Pythonically more compressed?

A direct rewrite using a list comprehesion, and ternary operator:
def Vectomparison (listuple, list0):
return [1 if item[0] in list0 else -1 for item in listuple]
Note that item[0] in list0 uses a linear search if list0 is a list. That makes the algorithms time complexity O(N*M) where N = len(listuple), M = len(list0)
You can make it faster:
def Vectomparison (listuple, list0):
set0 = set(list0)
return [1 if item[0] in set0 else -1 for item in listuple]
This version has a time complexity of O(N+M)

Related

Time complexity of a function in Python

I have 2 functions which perform same task of identifying if the 2 lists have any common element between them. I want to analyze their time complexity.
What i know is: for loop if iterated n times gives O(n) complexity. But, I am confused with the situation when we use 'in' operator. eg: if element in mylist
Please look at the functions to have better understanding of the scenario:
list1 = ['a','b','c','d','e']
list2 = ['m','n','o','d']
def func1(list1, list2):
for i in list1: # O(n), assuming number of items in list1 is n
if i in list2: # What will be the BigO of this statement??
return True
return False
z = func1(list1, list2)
print(z)
I have another function func2, please help determine its BigO as well:
def func2(list1, list2):
dict = {}
for i in list1:
if i not in dict.keys():
dict[i] = True
for j in list2:
if j in dict.keys():
return True
return False
z = func2(list1, list2)
print(z)
What is the time complexity of func1 and func2? Is there any difference in performance between 2 functions?
Regarding func1:
searching in lists is a linear operation with respect to the number of elements,
assuming items are randomly ordered and order of checking is also not related then statistically you come across an existing element in n/2 steps and n when not found (which simplifies to O(n))
if x in list_ is a linear search as described above, hence func1 has complexity of n^2.
Regarding func2:
instead of dictionary you may want to consider using a set. It has O(1) complexity for checking the existence of element. which would improve the complexity over func1, and also you can use set(list) to create a list instead of iterating over list directly in python (which is slower than initialization of a set directly from list - but does not affect the O complexity, as it is just slower, but constant).

Number of sub sequences of length K having total sum S, given 2d array

I wish to find Number of sub sequences of length K having total sum S, given an array.
Sample Input:
a=[1,1,1,2,2] & K=2 & S=2
Sample Output:
3 {because a[0],a[1]; a[1]a[2]; a[0]a[2] are only three possible for the case}
I have tried to write a recursive loop in Python for starter but it isn't giving output as expected.Please can you help me find a loophole I might be missing on.
def rec(k, sum1, arr, i=0):
#print('k: '+str(k)+' '+'sum1: '+str(sum1)) #(1) BaseCase:
if(sum1==0 and k!=0): # Both sum(sum1) required and
return 0 # numbers from which sum is required(k)
if(k==0 and sum1 !=0): # should be simultaneously zero
return 0 # Then required subsequences are 1
if(k==0 and sum1==0 ): #
return 1 #
base_check = sum1!=0 or k!=0 #(2) if iterator i reaches final element
if(i==len(arr) and base_check): # in array we should return 0 if both k
return 0 # and sum1 aren't zero
# func rec for getting sum1 from k elements
if(sum1<arr[0]): # takes either first element or rejects it
ans=rec(k-1,sum1,arr[i+1:len(arr)],i+1) # so 2 cases in else loop
print(ans) # i is taken in as iterator to provide array
else: # input to rec func from 2nd element of array
ans=rec(k-1, sum1-arr[0], arr[i+1:len(arr)],i+1)+rec(k, sum1, arr[i+1:len(arr)],i+1)
#print('i: '+str(i)+' ans: '+str(ans))
return(ans)
a=[1,1,1,2,2]
print(rec(2,2,a))
I am still unable to process how to make changes. Once this normal recursive code is written I might go to DP approach accordinlgy.
Using itertools.combinations
Function itertools.combinations returns all the subsequences of a given lengths. Then we filter to keep only subsequences who sum up to the desired value.
import itertools
def countsubsum(a, k, s):
return sum(1 for c in itertools.combinations(a,k) if sum(c)==s)
Fixing your code
Your code looks pretty good, but there are two things that appear wrong about it.
What is this if for?
At first I was a bit confused about if(sum1<arr[0]):. I think you can (and should) always go to the else branch. After thinking about it some more, I understand you are trying to get rid of one of the two recursive calls if arr[0] is too large to be taken, which is smart, but this makes the assumption that all elements in the array are nonnegative. If the array is allowed to contain negative numbers, then you can include a large a[0] in the subsequence, and hope for a negative element to compensate. So if the array can contain negative numbers, you should get rid of this if/else and always execute the two recursive calls from the else branch.
You are slicing wrong
You maintain a variable i to remember where to start in the array; but you also slice the array. Pretty soon your indices become wrong. You should use slices, or use an index i, but not both.
# WRONG
ans=rec(k-1, sum1-arr[0], arr[i+1:len(arr)],i+1)+rec(k, sum1, arr[i+1:len(arr)],i+1)
# CORRECT
ans = rec(k-1, sum1-arr[i], arr, i+1) + rec(k, sum1, arr, i+1)
# CORRECT
ans = rec(k-1, sum1-arr[0], arr[1:]) + rec(k, sum1, arr[1:])
To understand why using both slicing and an index gives wrong results, run the following code:
def iter_array_wrong(a, i=0):
if (a):
print(i, a)
iter_array_wrong(a[i:], i+1)
def iter_array_index(a, i=0):
if i < len(a):
print(i, a)
iter_array_index(a, i+1)
def iter_array_slice(a):
if a:
print(a)
iter_array_slice(a[1:])
print('WRONG')
iter_array_wrong(list(range(10)))
print()
print('INDEX')
iter_array_index(list(range(10)))
print()
print('SLICE')
iter_array_slice(list(range(10)))
Also note that a[i:len(a)] is exactly equivalent to a[i:] and a[0:j] is equivalent to a[:j].
Clean version of the recursion
Recursively count the subsequences who use the first element of the array, and the subsequences who don't use the first element of the array, and add the two counts. To avoid explicitly slicing the array repeatedly, which is an expensive operation, we keep a variable start to remember we are only working on subarray a[start:].
def countsubsum(a, k, s, start=0):
if k == 0:
return (1 if s == 0 else 0)
elif start == len(a):
return 0
else:
using_first_element = countsubsum(a, k-1, s-a[start], start+1)
notusing_first_elem = countsubsum(a, k, s, start+1)
return using_first_element + notusing_first_elem

Implementation of Depth-First-Search on a permutation tree in Python

I have a quadratic Matrix of size n, say A, with non-negative real entries a_ij.
Furthermore I have a permutation tree. For n = 3 it looks like this: .
Now I would like to do a Depth-search (I don't know really, whether "Depth-search" is the correct description for this, but let's use it for now) along the branches of the tree in the following way:
On the first partial tree on the very left do the following starting with an "empty" Permutation (x,x,x):
If a_12 > a_21 set (1,2,x) and then check whether a_23 > a_32. If this is true as well, save (1,2,3) in a list, say P. Then go back to the first Level and check whether a_13 > a_31 and so on.
If a_21 > a_12 or a_32 > a_23 do not save the Permutation in P and go back to the first Level and check whether a_13 > a_31. If this is true set (1,3,x) and then check whether a_23 > a_32. If this is true save (1,3,2) in P and continue with the next partial tree. If a_31 > a_13 or a_32 > a_23 do not save the Permutation in P and continue with the same procedure for the next partial tree.
This procedure/algorithm I would like to implement for an arbitrary natural n > 0 with Input just the Matrix A and n and as an Output all permutations of size n that fullfill these conditions. By now I am not able to implement this in a general way.
Preferably in Python, but Pseudo Code would be nice as well. I also want to avoid functions like "itertools Permutation", because in the use case I Need to apply this for large n, for example n = 100, and then itertools Permutation is very slow.
If I understand correctly, this should get you what you want:
import numpy as np
from itertools import permutations
def fluboxing_permutations(a, n):
return [p for p in permutations(range(n))
if all(a[i, j] > a[j, i] for i, j in zip(p, p[1:]))]
n = 3
a = np.random.random([n, n])
fluboxing_permutations(a, n)
itertools.permutations will yield permutations in lexicographical order, which corresponds to your tree; then we check that for each consecutive pair of indices in the permutation, the element in the matrix is greater than the element at swapped indices. If so, we retain the permutation.
(No idea how to describe what the function does, so I made a new name. Hope you like it. If anyone knows a better way to describe it, please edit! :P )
EDIT: Here's a recursive function that should do the same, but with pruning:
def fluboxing_permutations_r(a, n):
nset = set(range(n))
def inner(p):
l = len(p)
if l > 1 and a[p[-2]][p[-1]] <= a[p[-1]][p[-2]]:
return []
if l == n:
return [p]
return [r for i in nset - set(p)
for r in inner(p + (i,))]
return inner(())
p starts as empty tuple, but it grows in recursion. Once there's at least two elements in the partial permutation, we can test the last two elements and see if it fails the test, and reject it if it does (pruning its subtree out of the search space). If it is a full permutation that wasn't rejected, we return it. If it's not full yet, we append to it all possible indices that are not already in there, and recurse.
tinyEDIT: BTW, parameter n is kind of redundant, because n = len(a) at the top of the function should take care of it.

Python - Create a recursion function

my question is basically this: Create a recursion function that takes a nested list as a
parameter and returns the sub-list that has minimum difference between its maximum and minimum elements.
For example: Function should return [1,2] for input [[1,199,59],[1,2],[3,8]]
I searched Google and stackoverflow, but i could not find this specific example.
What i would like to get help is with iteration. I want to, using recursion, iterate over each sub-list(can be as many as possible). I have achieved this with a for loop, but i cannot grasp the idea of iteration by using recursion method.
So far, i have this:
def sublist(mylist):
if len(mylist) == 0:
return []
elif len(mylist) == 1:
return mylist
else:
a = (mylist[0][0]) - (mylist[0][-1])
if a < sublist(mylist[1:]):
return mylist[0]
sublist([[1,199,58],[1,2],[3,8]])
This part, ( sublist(mylist[1:]) ) i know is clearly wrong. I'm trying to compare the value a, with the values from the mylist[1:]. I would appreciate much advice here.
Updated:
def differences(mylist):
diff = max(mylist) - min(mylist)
return diff
def sublist(nestedlist):
if len(nestedlist) == 1:
return nestedlist[0]
else:
if differences(nestedlist[0]) < differences(sublist(nestedlist[1:])):
return nestedlist[0]
else:
return sublist(nestedlist[1:])
print(sublist([[1,199,59],[1,2],[3,8]]))
i am assuming that you want to use recursion for the first level of the list. So, without giving you the code 100%, you have to do something like that:
1) create a method e.g diferences(list) that calculates the differences of a list and returns a list with the parameter list and the min difference i.e differences([1,2]) should return [1, [1,2]]. call it once on the first sublist i.e min = differences(mylist[0])
2) create your sublist method like this:
def sublist(initial_list):
# 1) call differences() method for the first sublist of the 'initial_list'
# 2) update 'min' with differences(initial_list[0])if differences(inilitial_list[0])[0] < min[0];
# 3) call sublist() again now removing the sublist you checked before from the arguement
# 4) (the following should be at the start of your sublist() method)
if len(initial_list) = 1:
if differences(initial_list) < min:
return initial_list
else: return min[1]
Hope that helps

k way merge sort divide and conquer

from math import ceil
def merge(all_lst):
sorted_lst = []
while all_lst:
min_value,index = all_lst[0][0],0
for lst in all_lst:
if lst[0]<min_value:
min_value = lst[0]
index = all_lst.index(lst)
sorted_lst.append(min_value)
all_lst[index].pop(0)
if not all_lst[index]:
all_lst.remove(all_lst[index])
return sorted_lst
def merge_sort(lst, k):
def split(lst):
split_lst = []
j = ceil(len(lst)/k) if len(lst)>=k else 1
for i in range(0,len(lst),j):
split_lst.append(lst[i:i+j])
return split_lst
lst=split(lst)
if len(lst[0])==1:
return lst
else:
for i in range(len(lst)):
lst[i]=merge(merge_sort(lst[i],k))
return merge(lst)
Above is my code for k-way merge sort. Basically what it does is split the list into k smaller list by calling the split function until each sublist in the list is a single element. Then the list containing sublists will be merged into one single list.
My code works fine when splitting is done twice. (eg.[3,6,8,5,2,1,4,7] -> [3,6,8],[5,2,1],[4,7] -> [3],[6],[8],[5],[2],[1],[4],[7]). But when the splitting is done more than twice, (eg,[3,6,8,5,2,1,4,7] -> [3,6,8,5],[2,1,4,7] -> [3,6],[8,5],[2,1],[4,7] -> [3],[6],[8],[5],[2],[1],[4],[7]), the code will fail. Can anyone help find me find out what goes wrong in my code? Thanks in advance.
I believe the problem you're having is that merge_sort sometimes returns a flattened list and other times returns a list of lists. You should probably return a flat list in all cases. There's some other cruft: You don't need split to be its own function, since you only call it the one time.
Here's a greatly simplified version of your code:
def merge_sort(lst, k):
if len(lst) == 1: # simpler base case
return lst
j = ceil(len(lst)/k) # no need to check for k < len(lst) (ceil handles it)
#split and recursively sort in one step
lst = [merge_sort(lst[i:i+j], k) for i in range(0, len(lst), j)]
return merge(lst) # always return a merged list (never a list of lists)

Resources