Find the target difference in a pair with recursion - python

Given a list of unsorted integers and a target integer, find out if any pair's difference in the list is equal to the target integer with recursion.
>>> aList = [5, 4, 8, -3, 6]
>>> target = 9
return True
>>> aList = [-1, 5, 4]
>>> target = 3
return False
For and while loops are not allowed.
No imports allowed.
.sort() is not allowed.
I tried this and it didn't work.
def calculate(aList, target):
if len(aList) == 0 and diff != 0:
return False
startIndex = 0
endIndex = len(aList) - 1
return resursive_sum(aList, target, startIndex, endIndex)
def resursive_sum(aList, targ, start, end):
print(f'Start: {start}')
print(f'End: {end}')
if start == end:
return False
elif aList[end] - aList[start] == targ:
return True
elif aList[end] - aList[start] < targ:
return resursive_sum(values, targ, start, end - 1)
return resursive_sum(aList, targ, start + 1, end)
I'm unsure of how this problem could be solved if we aren't able to use loops to sort the list. Even if we could use recursion to sort the list, how should the recursion look so that it can scan every pair's difference?

So I actually implemented it, but for educational purposes I'm not gonna post it until a bit later (I'll update it in a few hours) as I assume this is for a class or some other setting where you should figure it out on your own.
Assume you are trying to hit a difference target t = 5 and you are evaluating an arbitrary element 8. There are only two values that would allow 8 to have a complement in the set: 8 + 5 = 13 and 8 - 5 = 3.
If 3 or 13 had been in any previous elements, you would know that the set has a pair of complements. Otherwise, you'd want to record the fact that 8 had been seen. Thereby, if 3 was found later, 8 would be queried as 3 + 5 = 8 would be considered.
In other words, I am proposing a method where you recursively traverse the list and either
(base case) Are at the end of the list
Have a current element a such that a + t or a - t has been seen
Record that the current element has been seen and go to the next element
Ideally, this should have O(n) time complexity and O(n) space complexity in the worst case (assuming efficient implementation with pass-by-reference or similar, and also amortized constant-time set query). It can also be implemented using a basic array, but I'm not going to say that's better (in python).
I'll post my solution in a few hours. Good luck!
EDIT 1: Hopefully, you had enough time to get it to work. The method I described can be done as follows:
def hasDiffRecur(L, t, i, C):
"""
Recursive version to see if list has difference
:param L: List to be considered
:param t: Target difference
:param i: Current index to consider
:param C: Cache set
"""
# We've reached the end. Give up
if i >= len(L):
return False
print(f" > L[{i}] = {L[i]:2}; is {L[i]-t:3} or {L[i]+t:2} in {C}")
# Has the complement been cached?
if L[i] - t in C:
print(f"! Difference between {L[i]} and {L[i]-t} is {t}")
return True
if L[i] + t in C:
print(f"! Difference between {L[i]} and {L[i]+t} is {t}")
return True
# Complement not seen yet. Cache element and go to next element
C.add(L[i])
return hasDiffRecur(L, t, i+1, C)
###################################################################
def hasDiff(L, t):
"""
Initialized call for hasDiffRecur. Also prints intro message.
See hasDiffRecur for param info
"""
print(f"\nIs a difference of {t} present in {L}?")
return hasDiffRecur(L, t, 0, set())
###################################################################
hasDiff([5, 4, 8, -3, 6], 9)
hasDiff([-1, 5, 4], 3)
hasDiff([-1, 5, 4, -1, 7], 0) # If concerned about set non-duplicity
OUTPUT:
Is a difference of 9 present in [5, 4, 8, -3, 6]?
> L[0] = 5; is -4 or 14 in set()
> L[1] = 4; is -5 or 13 in {5}
> L[2] = 8; is -1 or 17 in {4, 5}
> L[3] = -3; is -12 or 6 in {8, 4, 5}
> L[4] = 6; is -3 or 15 in {8, -3, 4, 5}
! Difference between 6 and -3 is 9
Is a difference of 3 present in [-1, 5, 4]?
> L[0] = -1; is -4 or 2 in set()
> L[1] = 5; is 2 or 8 in {-1}
> L[2] = 4; is 1 or 7 in {5, -1}
Is a difference of 0 present in [-1, 5, 4, -1, 7]?
> L[0] = -1; is -1 or -1 in set()
> L[1] = 5; is 5 or 5 in {-1}
> L[2] = 4; is 4 or 4 in {5, -1}
> L[3] = -1; is -1 or -1 in {4, 5, -1}
! Difference between -1 and -1 is 0
EDIT 2:
This is a pretty clever and efficient solution. I do realize that maybe it is the intention to not allow any traversal at all (i.e. no existance querying for set). If that is the case, the above approach can be done with a constant-size list that is pre-allocated to size equal to the range of the values of the list.
If the notion of pre-allocating to the size of the range of the list is still too much iteration, I can think of the exhaustive approach implemented recursively. There is likely a more efficient approach for this, but you could boil the problem down to a double-for-loop-like problem (O(n^2) time complexity). This is a trivial algorithm and I think you can understand it without documentation, so I'll just throw it in there to be complete:
def hasDiffRecur(L, t, i = 0, j = 1):
if i >= len(L): return False
if j >= len(L): return hasDiffRecur(L, t, i+1, i+2)
if abs(L[i] - L[j]) == t: return True
return hasDiffRecur(L, t, i, j+1)
###################################################################
print(hasDiffRecur([5, 4, 8, -3, 6], 9)) # True
print(hasDiffRecur([-1, 5, 4], 3)) # False
print(hasDiffRecur([-1, 5, 4, -1, 7], 0)) # True

choose
I'll start with a generic function that takes a list, t, and a number of elements to choose, n -
def choose(t, n):
if n == 0:
return [[]]
elif not t:
return []
else:
return append \
( map \
( choose(rest(t), n - 1)
, lambda c: append([first(t)], c)
)
, choose(rest(t), n)
)
print(choose(["a", "b", "c", "d"], 2))
[['a', 'b'], ['a', 'c'], ['a', 'd'], ['b', 'c'], ['b', 'd'], ['c', 'd']]
helpers
Your question imposes quite a few restrictions and Python is a multi-paradigm language and so we're going to use a number of helpers to make things readable
def first(t):
return t[0]
def rest(t):
return t[1:]
def append(t0, t1):
return t0 + t1
I don't know if map counts as an import, but we will define our own just in case -
def map(t, f):
if not t:
return []
else:
return append \
( [f(first(t))]
, map(rest(t), f)
)
solve
Great, now that we've finished implementing choose, let's see how we can apply it to our problem
print(choose([5, 4, 8, -3, 6], 2))
[[5, 4], [5, 8], [5, -3], [5, 6], [4, 8], [4, -3], [4, 6], [8, -3], [8, 6], [-3, 6]]
As you can see, we've found all combinations of 2 elements. We just need to loop through these and check if a pair can be subtracted to reach our target, q -
def solve(t, q):
def check(p):
(x, y) = p
return x - y == q or y - x == q
def loop(c):
if not c:
return False
else:
return check(first(c)) or loop(rest(c))
return loop(choose(t, 2))
print(solve([5, 4, 8, -3, 6], 9))
print(solve([-1, 5, 4], 3))
True
False
allowing for
This is a great exercise to build your recursion skills. Disallowing for is the most challenging restriction to overcome. Here's what it could look like if we could use it -
def choose(t, n):
if n == 0:
yield []
elif not t:
return
else:
for c in choose(t[1:], n - 1):
yield [t[0]] + c
yield from choose(t[1:], n)
def solve(t, q):
for (x,y) in choose(t, 2):
if x - y == q or y - x == q:
return True
return False
print(solve([5, 4, 8, -3, 6], 9))
print(solve([-1, 5, 4], 3))
True
False
This variant has an added advantage that it will stop computing combinations as soon as a solution is found. The first variant must compute all combinations first and then begin iterating through them.
allowing other built-ins
Python built-in functions include map and any and offer us another way to get around the for restriction, but I'm unsure if those are allowed -
def choose(t, n):
if n == 0:
yield []
elif not t:
return
else:
yield from map \
( lambda c: [t[0]] + c
, choose(t[1:], n - 1)
)
yield from choose(t[1:], n)
def solve(t, q):
def check(p):
(x,y) = p
return x - y == q or y - x == q
return any(map(check, choose(t, 2)))
print(solve([5, 4, 8, -3, 6], 9))
print(solve([-1, 5, 4], 3))
True
False

Problem:
given an array of int aList and int target,
check if the difference between each element in aList equals to target
use recursion
do not use .sort()
do not use while and for
do not use import
Example:
>>> aList = [5, 4, 8, -3, 6]
>>> target = 9
return True
>>> aList = [-1, 5, 4]
>>> target = 3
return False
Comparing the differences:
5 4 8 -3 6
-------------------------
5 | X
4 | 1 X
8 | 3 4 X
-3 | 6 7 11 X
6 | 1 2 2 9 X
where X means that there's no difference (same number)
since we find 9 there, so it should return True (target is 9)
Traditional for loops
To solve recursion problem, first try to solve it with traditional for loops:
def compareAll(lst, tgt):
for x in lst: # let's call this x loop
for y in lst: # let's call this y loop
if abs(x-y) == tgt:
return True
return False
print( compareAll([5,4,8,-3,6],9) )
print( compareAll([-1,5,4],3) )
This returns True then False
Recursion
Now we can try using recursion loop. Since we already got the for loop, we can convert it like this:
def compareAll(lst, tgt, x=0, y=0):
if(len(lst)-1 == x and len(lst) == y):
return False
if(len(lst) == x or len(lst) == y):
return compareAll(lst, tgt, x+1, 0)
if(abs(lst[x] - lst[y])==tgt):
return True
return compareAll(lst, tgt, x, y+1)
print( compareAll([5,4,8,-3,6],9) )
print( compareAll([-1,5,4],3) )
How I convert for loop into this:
python's for loop is actually foreach loop in most other languages
so, pure for loop in python will be like:
def compareAll(lst, tgt):
x = 0
while x < len(lst): # let's call this x loop
y = 0
while y < len(lst): # let's call this y loop
if abs(lst[x]-lst[y]) == tgt:
return True
y = y+1
x = x+1
return False
print( compareAll([5,4,8,-3,6],9) )
print( compareAll([-1,5,4],3) )
notice the stopping condition of x loop: when all the array element have been looped
so we add stopping condition here: if(len(lst)-1 == x and len(lst) == y): return False
notice the stopping condition of y loop: when all the array element have been looped
so we add stopping condition here: if(len(lst) == x or len(lst) == y): return compareAll(lst, tgt, x+1, 0)
this stops the current y loop and continue with the x loop
then, we add the actual content of the loop: if(abs(lst[x] - lst[y])==tgt): return True
last, we have to continue the loop: return compareAll(lst, tgt, x, y+1)
The key to convert for loop into recursive loop is just to identify when the loop should end, and when the loop should continue.

This should work and is quite concise:
def q(target, aList, memo=set()):
if len(aList) == 0:
return False
num = aList.pop()
memo.add(num)
if target + num in memo:
return True
return q(target, aList, memo)
q(target=9, aList=[5, 4, 8, -3, 6]) # True
q(target=3, aList=[-1,5,4]) # False
The key insight for me is that a target t and a given number n, the difference d is known. Dict/set/hashmaps are fast at detecting membership, regardless of how many items are added. So... just pop through the list of values and chuck them into a hashmap for later comparison.

The problem can be solved by checking every possible pair of numbers in the list for a solution. If you are allowed to use Python's standard library, then the solution is pretty straight forward.
from itertools import product
def check(xs, target):
return any(map(lambda x: x[0]-x[1] == target, product(xs, xs)))
Breakdown
product(xs, xs) gives the cross product of xs with itself
any(iterable) returns true if any element of iterable is truthy
map(function, iterable) lazily (applies function to every element of iterable)
lambda arg_tuple: expression annonymous function with arguments arg_tuple and returns the result of expression
The return statement in check uses lazy structures so it only does as much work as
is needed, and is space efficient.

Assuming that this is just an exercise in recursion, it probably doesn't preclude a "brute force" approach. You can use the recursion to pair up every value with the remaining ones until you find a matching difference.
For example:
def hasDiff(L,diff,base=None):
if not L: return False # empty list: no match
if base is None: # search with & without first as base
return hasDiff(L[1:],diff,L[0]) or hasDiff(L[1:],diff)
return abs(base-L[0]) == diff or hasDiff(L[1:],diff,base) # match or recurse
print(hasDiff([5, 4, 8, -3, 6],9)) # True
print(hasDiff([-1, 5, 4],3)) # False
When the function recurses with a base value, it merely checks the first item in the remainder of the list and recurses for the other values. When the function recurses without a base value, it tries to find new pairs that don't involve the first item (i.e. in the remainder of the list)

Related

min sum of consecutive values with Divide and Conquer

Given an array of random integers
N = [1,...,n]
I need to find min sum of two consecutive values using divide and conquer.
What is not working here but my IQ?
def minSum(array):
if len(array) < 2:
return array[0]+array[1]
if (len(a)%2) != 0:
mid = int(len(array)/2)
leftArray = array[:mid]
rightArray = array[mid+1:]
return min(minSum(leftArray),minSum(rightArray),crossSum(array,mid))
else:
mid = int(len(array)/2)
leftArray = array[:mid]
rightArray = array[mid:]
return min(minSum(leftArray), minSum(rightArray), array[mid]+array[mid+1])
def crossSum(array,mid):
return min(array[mid-1]+array[mid],array[mid]+array[mid+1])
The main problem seems to be that the first condition is wrong: If len(array) < 2, then the following line is bound to raise an IndexError. Also, a is not defined. I assume that that's the name of the array in the outer scope, thus this does not raise an exception but just silently uses the wrong array. Apart from that, the function seems to more-or-less work (did not test it thoroughly, though.
However, you do not really need to check whether the array has odd or even length, you can just use the same code for both cases, making the crossSum function unneccesary. Also, it is kind of confusing that the function for returning the min sum is called maxSum. If you really want a divide-and-conquer approach, try this:
def minSum(array):
if len(array) < 2:
return 10**100
elif len(array) == 2:
return array[0]+array[1]
else:
# len >= 3 -> both halves guaranteed non-empty
mid = len(array) // 2
leftArray = array[:mid]
rightArray = array[mid:]
return min(minSum(leftArray),
minSum(rightArray),
leftArray[-1] + rightArray[0])
import random
lst = [random.randint(1, 10) for _ in range(20)]
r = minSum(lst)
print(lst)
print(r)
Random example output:
[1, 5, 6, 4, 1, 2, 2, 10, 7, 10, 8, 4, 9, 5, 7, 6, 5, 1, 4, 9]
3
However, a simple loop would be much better suited for the problem:
def minSum(array):
return min(array[i-1] + array[i] for i in range(1, len(array)))

Given an array of integers, return indices of the two numbers such that they add up to a specific target [duplicate]

I'm new to Python and have just started to try out LeetCode to build my chops. On this classic question my code misses a test case.
The problem is as follows:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
I miss on test case [3,2,4] with the target number of 6, which should return the indices of [1,2], but hit on test case [1,5,7] with the target number of 6 (which of course returns indices [0,1]), so it appears that something is wrong in my while loop, but I'm not quite sure what.
class Solution:
def twoSum(self, nums, target):
x = 0
y = len(nums) - 1
while x < y:
if nums[x] + nums[y] == target:
return (x, y)
if nums[x] + nums[y] < target:
x += 1
else:
y -= 1
self.x = x
self.y = y
self.array = array
return None
test_case = Solution()
array = [1, 5, 7]
print(test_case.twoSum(array, 6))
Output returns null on test case [3,2,4] with target 6, so indices 1 and 2 aren't even being summarized, could I be assigning y wrong?
A brute force solution is to double nest a loop over the list where the inner loop only looks at index greater than what the outer loop is currently on.
class Solution:
def twoSum(self, nums, target):
for i, a in enumerate(nums, start=0):
for j, b in enumerate(nums[i+1:], start=0):
if a+b==target:
return [i, j+i+1]
test_case = Solution()
array = [3, 2, 4]
print(test_case.twoSum(array, 6))
array = [1, 5, 7]
print(test_case.twoSum(array, 6))
array = [2, 7, 11, 15]
print(test_case.twoSum(array, 9))
Output:
[1, 2]
[0, 1]
[0, 1]
Bit different approach. We will build a dictionary of values as we need them, which is keyed by the values we are looking for.If we look for a value we track the index of that value when it first appears. As soon as you find the values that satisfy the problem you are done. The time on this is also O(N)
class Solution:
def twoSum(self, nums, target):
look_for = {}
for n,x in enumerate(nums):
try:
return look_for[x], n
except KeyError:
look_for.setdefault(target - x,n)
test_case = Solution()
array = [1, 5, 7]
array2 = [3,2,4]
given_nums=[2,7,11,15]
print(test_case.twoSum(array, 6))
print(test_case.twoSum(array2, 6))
print(test_case.twoSum(given_nums,9))
output:
(0, 1)
(1, 2)
(0, 1)
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
ls=[]
l2=[]
for i in nums:
ls.append(target-i)
for i in range(len(ls)):
if ls[i] in nums :
if i!= nums.index(ls[i]):
l2.append([i,nums.index(ls[i])])
return l2[0]
x= Solution()
x.twoSum([-1,-2,-3,-4,-5],-8)
output
[2, 4]
import itertools
class Solution:
def twoSum(self, nums, target):
subsets = []
for L in range(0, len(nums)+1):
for subset in itertools.combinations(nums, L):
if len(subset)!=0:
subsets.append(subset)
print(subsets) #returns all the posible combinations as tuples, note not permutations!
#sums all the tuples
sums = [sum(tup) for tup in subsets]
indexes = []
#Checks sum of all the posible combinations
if target in sums:
i = sums.index(target)
matching_combination = subsets[i] #gets the option
for number in matching_combination:
indexes.append(nums.index(number))
return indexes
else:
return None
test_case = Solution()
array = [1,2,3]
print(test_case.twoSum(array, 4))
I was trying your example for my own learning. I am happy with what I found. I used the itertools to make all the combination of the numbers for me. Then I used list manipulation to sum all the possible combination of numbers in your input array, then I just check in one shot if the target is inside the sum array or not. If not then return None, return the indexes otherwise. Please note that this approach will return all the three indexes as well, if they add up to the target. Sorry it took so long :)
This one is more comprehensive cohesive efficient one even so shorter lines of code.
nums = [6, 7, 11, 15, 3, 6, 5, 3,99,5,4,7,2]
target = 27
n = 0
for i in range(len(nums)):
n+=1
if n == len(nums):
n == len(nums)
else:
if nums[i]+nums[n] == target:
# to find the target position
print([nums.index(nums[i]),nums.index(nums[n])])
# to get the actual numbers to add print([nums[i],nums[n]])

Python Two Sum - Brute Force Approach

I'm new to Python and have just started to try out LeetCode to build my chops. On this classic question my code misses a test case.
The problem is as follows:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
I miss on test case [3,2,4] with the target number of 6, which should return the indices of [1,2], but hit on test case [1,5,7] with the target number of 6 (which of course returns indices [0,1]), so it appears that something is wrong in my while loop, but I'm not quite sure what.
class Solution:
def twoSum(self, nums, target):
x = 0
y = len(nums) - 1
while x < y:
if nums[x] + nums[y] == target:
return (x, y)
if nums[x] + nums[y] < target:
x += 1
else:
y -= 1
self.x = x
self.y = y
self.array = array
return None
test_case = Solution()
array = [1, 5, 7]
print(test_case.twoSum(array, 6))
Output returns null on test case [3,2,4] with target 6, so indices 1 and 2 aren't even being summarized, could I be assigning y wrong?
A brute force solution is to double nest a loop over the list where the inner loop only looks at index greater than what the outer loop is currently on.
class Solution:
def twoSum(self, nums, target):
for i, a in enumerate(nums, start=0):
for j, b in enumerate(nums[i+1:], start=0):
if a+b==target:
return [i, j+i+1]
test_case = Solution()
array = [3, 2, 4]
print(test_case.twoSum(array, 6))
array = [1, 5, 7]
print(test_case.twoSum(array, 6))
array = [2, 7, 11, 15]
print(test_case.twoSum(array, 9))
Output:
[1, 2]
[0, 1]
[0, 1]
Bit different approach. We will build a dictionary of values as we need them, which is keyed by the values we are looking for.If we look for a value we track the index of that value when it first appears. As soon as you find the values that satisfy the problem you are done. The time on this is also O(N)
class Solution:
def twoSum(self, nums, target):
look_for = {}
for n,x in enumerate(nums):
try:
return look_for[x], n
except KeyError:
look_for.setdefault(target - x,n)
test_case = Solution()
array = [1, 5, 7]
array2 = [3,2,4]
given_nums=[2,7,11,15]
print(test_case.twoSum(array, 6))
print(test_case.twoSum(array2, 6))
print(test_case.twoSum(given_nums,9))
output:
(0, 1)
(1, 2)
(0, 1)
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
ls=[]
l2=[]
for i in nums:
ls.append(target-i)
for i in range(len(ls)):
if ls[i] in nums :
if i!= nums.index(ls[i]):
l2.append([i,nums.index(ls[i])])
return l2[0]
x= Solution()
x.twoSum([-1,-2,-3,-4,-5],-8)
output
[2, 4]
import itertools
class Solution:
def twoSum(self, nums, target):
subsets = []
for L in range(0, len(nums)+1):
for subset in itertools.combinations(nums, L):
if len(subset)!=0:
subsets.append(subset)
print(subsets) #returns all the posible combinations as tuples, note not permutations!
#sums all the tuples
sums = [sum(tup) for tup in subsets]
indexes = []
#Checks sum of all the posible combinations
if target in sums:
i = sums.index(target)
matching_combination = subsets[i] #gets the option
for number in matching_combination:
indexes.append(nums.index(number))
return indexes
else:
return None
test_case = Solution()
array = [1,2,3]
print(test_case.twoSum(array, 4))
I was trying your example for my own learning. I am happy with what I found. I used the itertools to make all the combination of the numbers for me. Then I used list manipulation to sum all the possible combination of numbers in your input array, then I just check in one shot if the target is inside the sum array or not. If not then return None, return the indexes otherwise. Please note that this approach will return all the three indexes as well, if they add up to the target. Sorry it took so long :)
This one is more comprehensive cohesive efficient one even so shorter lines of code.
nums = [6, 7, 11, 15, 3, 6, 5, 3,99,5,4,7,2]
target = 27
n = 0
for i in range(len(nums)):
n+=1
if n == len(nums):
n == len(nums)
else:
if nums[i]+nums[n] == target:
# to find the target position
print([nums.index(nums[i]),nums.index(nums[n])])
# to get the actual numbers to add print([nums[i],nums[n]])

Is there a way to "fork" a list in two based on a condition [duplicate]

This question already has answers here:
How can I partition (split up, divide) a list based on a condition?
(41 answers)
Closed 4 years ago.
I have seen a pattern repeated a couple times in my team's code, it looks like this
numbers = [1, 2, 3, 4]
even_numbers = [n for n in numbers if n % 2 == 0]
odd_numbers = [n for n in numbers if n % 2 != 0]
I was wondering if there is a function somewhere (I have looked around but haven't been able to find it) that would do something like this
numbers = [1, 2, 3, 4]
even_numbers, odd_numbers = fork(numbers, lambda x: x % 2 == 0)
So, this function I am looking for, would receive an iterable and a function, and return two lists, one would be the values that match a provided condition, and the other would be the ones that didn't.
Is there something around python's standard library that achieves this?
I usually call this sift, but partition is fine too.
Another, itertools-less implementation might be
def sift(iterable, predicate):
t = []
f = []
for value in iterable:
(t if predicate(value) else f).append(value)
return (t, f)
even, odd = sift([1, 2, 3, 4, 5], lambda x: x % 2 == 0)
EDIT: for a slightly more complex implementation that is about 30% faster (on my Python installation anyway):
def sift2(iterable, predicate):
t = []
f = []
ta = t.append
fa = f.append
for value in iterable:
(ta if predicate(value) else fa)(value)
return (t, f)
You can use the following function:
from itertools import filterfalse, tee
def fork(pred, iterable):
'Use a predicate to partition entries into false entries and true entries'
t1, t2 = tee(iterable)
return list(filterfalse(pred, t1)), list(filter(pred, t2))
Source: itertools
I did not find anything in standard library performing what you want. I suggest you this user-defined implementation, which is not optimized at all but very simple and easy to read:
def myFunc(iterable, func):
first = [i for i in iterable if func(i)]
second = [i for i in iterable if not func(i)]
return first,second
numbers = [1, 2, 3, 4]
even_numbers, odd_numbers = myFunc(numbers, lambda x: x % 2 == 0)
print(even_numbers) # [2, 4]
print(odd_numbers) # [1, 3]
Full code following #jonrsharpe suggestion.
import itertools
def fork(iterable):
"Returns list of even, odd elements of a list"
t1, t2 = itertools.tee(iterable)
pred = lambda i: i % 2 == 0
return list(filter(pred, t2)), list(itertools.filterfalse(pred, t1))
odd, even = fork([1,2,3,4,5])
print(odd)
print(even)
Alternative numpy version which might be faster for big arrays
import numpy as np
def fork(iterable):
"Returns array of even, odd elements of an array"
iterable_array = np.asarray(iterable)
mask = (iterable_array % 2 == 0)
return iterable_array[~mask], iterable_array[mask]
You can create your own function:
l = [1, 2, 3, 4]
def fork(l,key):
return list(filter(key,l)), [i for i in l if i not in list(filter(key,l))]
even_numbers, odd_numbers = fork(l, lambda x: x % 2 == 0)
print(even_numbers)
print(odd_numbers)
Output:
[2, 4]
[1, 3]

How to test if a list contains another list as a contiguous subsequence?

How can I test if a list contains another list (ie. it's a contiguous subsequence). Say there was a function called contains:
contains([1,2], [-1, 0, 1, 2]) # Returns [2, 3] (contains returns [start, end])
contains([1,3], [-1, 0, 1, 2]) # Returns False
contains([1, 2], [[1, 2], 3]) # Returns False
contains([[1, 2]], [[1, 2], 3]) # Returns [0, 0]
Edit:
contains([2, 1], [-1, 0, 1, 2]) # Returns False
contains([-1, 1, 2], [-1, 0, 1, 2]) # Returns False
contains([0, 1, 2], [-1, 0, 1, 2]) # Returns [1, 3]
If all items are unique, you can use sets.
>>> items = set([-1, 0, 1, 2])
>>> set([1, 2]).issubset(items)
True
>>> set([1, 3]).issubset(items)
False
There's an all() and any() function to do this.
To check if big contains ALL elements in small
result = all(elem in big for elem in small)
To check if small contains ANY elements in big
result = any(elem in big for elem in small)
the variable result would be boolean (TRUE/FALSE).
Here is my version:
def contains(small, big):
for i in xrange(len(big)-len(small)+1):
for j in xrange(len(small)):
if big[i+j] != small[j]:
break
else:
return i, i+len(small)
return False
It returns a tuple of (start, end+1) since I think that is more pythonic, as Andrew Jaffe points out in his comment. It does not slice any sublists so should be reasonably efficient.
One point of interest for newbies is that it uses the else clause on the for statement - this is not something I use very often but can be invaluable in situations like this.
This is identical to finding substrings in a string, so for large lists it may be more efficient to implement something like the Boyer-Moore algorithm.
Note: If you are using Python3, change xrange to range.
May I humbly suggest the Rabin-Karp algorithm if the big list is really big. The link even contains almost-usable code in almost-Python.
This works and is fairly fast since it does the linear searching using the builtin list.index() method and == operator:
def contains(sub, pri):
M, N = len(pri), len(sub)
i, LAST = 0, M-N+1
while True:
try:
found = pri.index(sub[0], i, LAST) # find first elem in sub
except ValueError:
return False
if pri[found:found+N] == sub:
return [found, found+N-1]
else:
i = found+1
If we refine the problem talking about testing if a list contains another list with as a sequence, the answer could be the next one-liner:
def contains(subseq, inseq):
return any(inseq[pos:pos + len(subseq)] == subseq for pos in range(0, len(inseq) - len(subseq) + 1))
Here unit tests I used to tune up this one-liner:
https://gist.github.com/anonymous/6910a85b4978daee137f
After OP's edit:
def contains(small, big):
for i in xrange(1 + len(big) - len(small)):
if small == big[i:i+len(small)]:
return i, i + len(small) - 1
return False
I've Summarized and evaluated Time taken by different techniques
Used methods are:
def containsUsingStr(sequence, element:list):
return str(element)[1:-1] in str(sequence)[1:-1]
def containsUsingIndexing(sequence, element:list):
lS, lE = len(sequence), len(element)
for i in range(lS - lE + 1):
for j in range(lE):
if sequence[i+j] != element[j]: break
else: return True
return False
def containsUsingSlicing(sequence, element:list):
lS, lE = len(sequence), len(element)
for i in range(lS - lE + 1):
if sequence[i : i+lE] == element: return True
return False
def containsUsingAny(sequence:list, element:list):
lE = len(element)
return any(element == sequence[i:i+lE] for i in range(len(sequence)-lE+1))
Code for Time analysis (averaging over 1000 iterations):
from time import perf_counter
functions = (containsUsingStr, containsUsingIndexing, containsUsingSlicing, containsUsingAny)
fCount = len(functions)
for func in functions:
print(str.ljust(f'Function : {func.__name__}', 32), end=' :: Return Values: ')
print(func([1,2,3,4,5,5], [3,4,5,5]) , end=', ')
print(func([1,2,3,4,5,5], [1,3,4,5]))
avg_times = [0]*fCount
for _ in range(1000):
perf_times = []
for func in functions:
startTime = perf_counter()
func([1,2,3,4,5,5], [3,4,5,5])
timeTaken = perf_counter()-startTime
perf_times.append(timeTaken)
for t in range(fCount): avg_times[t] += perf_times[t]
minTime = min(avg_times)
print("\n\n Ratio of Time of Executions : ", ' : '.join(map(lambda x: str(round(x/minTime, 4)), avg_times)))
Output:
Conclusion: In this case, Slicing operation proves to be the fastest
Here's a straightforward algorithm that uses list methods:
#!/usr/bin/env python
def list_find(what, where):
"""Find `what` list in the `where` list.
Return index in `where` where `what` starts
or -1 if no such index.
>>> f = list_find
>>> f([2, 1], [-1, 0, 1, 2])
-1
>>> f([-1, 1, 2], [-1, 0, 1, 2])
-1
>>> f([0, 1, 2], [-1, 0, 1, 2])
1
>>> f([1,2], [-1, 0, 1, 2])
2
>>> f([1,3], [-1, 0, 1, 2])
-1
>>> f([1, 2], [[1, 2], 3])
-1
>>> f([[1, 2]], [[1, 2], 3])
0
"""
if not what: # empty list is always found
return 0
try:
index = 0
while True:
index = where.index(what[0], index)
if where[index:index+len(what)] == what:
return index # found
index += 1 # try next position
except ValueError:
return -1 # not found
def contains(what, where):
"""Return [start, end+1] if found else empty list."""
i = list_find(what, where)
return [i, i + len(what)] if i >= 0 else [] #NOTE: bool([]) == False
if __name__=="__main__":
import doctest; doctest.testmod()
Smallest code:
def contains(a,b):
str(a)[1:-1].find(str(b)[1:-1])>=0
Here is my answer. This function will help you to find out whether B is a sub-list of A. Time complexity is O(n).
`def does_A_contain_B(A, B): #remember now A is the larger list
b_size = len(B)
for a_index in range(0, len(A)):
if A[a_index : a_index+b_size]==B:
return True
else:
return False`
a=[[1,2] , [3,4] , [0,5,4]]
print(a.__contains__([0,5,4]))
It provides true output.
a=[[1,2] , [3,4] , [0,5,4]]
print(a.__contains__([1,3]))
It provides false output.
I tried to make this as efficient as possible.
It uses a generator; those unfamiliar with these beasts are advised to check out their documentation and that of yield expressions.
Basically it creates a generator of values from the subsequence that can be reset by sending it a true value. If the generator is reset, it starts yielding again from the beginning of sub.
Then it just compares successive values of sequence with the generator yields, resetting the generator if they don't match.
When the generator runs out of values, i.e. reaches the end of sub without being reset, that means that we've found our match.
Since it works for any sequence, you can even use it on strings, in which case it behaves similarly to str.find, except that it returns False instead of -1.
As a further note: I think that the second value of the returned tuple should, in keeping with Python standards, normally be one higher. i.e. "string"[0:2] == "st". But the spec says otherwise, so that's how this works.
It depends on if this is meant to be a general-purpose routine or if it's implementing some specific goal; in the latter case it might be better to implement a general-purpose routine and then wrap it in a function which twiddles the return value to suit the spec.
def reiterator(sub):
"""Yield elements of a sequence, resetting if sent ``True``."""
it = iter(sub)
while True:
if (yield it.next()):
it = iter(sub)
def find_in_sequence(sub, sequence):
"""Find a subsequence in a sequence.
>>> find_in_sequence([2, 1], [-1, 0, 1, 2])
False
>>> find_in_sequence([-1, 1, 2], [-1, 0, 1, 2])
False
>>> find_in_sequence([0, 1, 2], [-1, 0, 1, 2])
(1, 3)
>>> find_in_sequence("subsequence",
... "This sequence contains a subsequence.")
(25, 35)
>>> find_in_sequence("subsequence", "This one doesn't.")
False
"""
start = None
sub_items = reiterator(sub)
sub_item = sub_items.next()
for index, item in enumerate(sequence):
if item == sub_item:
if start is None: start = index
else:
start = None
try:
sub_item = sub_items.send(start is None)
except StopIteration:
# If the subsequence is depleted, we win!
return (start, index)
return False
I think this one is fast...
def issublist(subList, myList, start=0):
if not subList: return 0
lenList, lensubList = len(myList), len(subList)
try:
while lenList - start >= lensubList:
start = myList.index(subList[0], start)
for i in xrange(lensubList):
if myList[start+i] != subList[i]:
break
else:
return start, start + lensubList - 1
start += 1
return False
except:
return False
Dave answer is good. But I suggest this implementation which is more efficient and doesn't use nested loops.
def contains(small_list, big_list):
"""
Returns index of start of small_list in big_list if big_list
contains small_list, otherwise -1.
"""
loop = True
i, curr_id_small= 0, 0
while loop and i<len(big_list):
if big_list[i]==small_list[curr_id_small]:
if curr_id_small==len(small_list)-1:
loop = False
else:
curr_id_small += 1
else:
curr_id_small = 0
i=i+1
if not loop:
return i-len(small_list)
else:
return -1
Here's a simple and efficient function to check whether big list contains a small one in matching order:
def contains(big, small):
i = 0
for value in big:
if value == small[i]:
i += 1
if i == len(small):
return True
else:
i = 1 if value == small[0] else 0
return False
Usage:
"""
>>> contains([1,2,3,4,5], [2,3,4])
True
>>> contains([4,2,3,2,4], [2,3,4])
False
>>> contains([1,2,3,2,3,2,2,4,3], [2,4,3])
True
"""
The problem of most of the answers, that they are good for unique items in list. If items are not unique and you still want to know whether there is an intersection, you should count items:
from collections import Counter as count
def listContains(l1, l2):
list1 = count(l1)
list2 = count(l2)
return list1&list2 == list1
print( listContains([1,1,2,5], [1,2,3,5,1,2,1]) ) # Returns True
print( listContains([1,1,2,8], [1,2,3,5,1,2,1]) ) # Returns False
You can also return the intersection by using ''.join(list1&list2)
Here a solution with less line of code and easily understandable (or at least I like to think so).
If you want to keep order (match only if the smaller list is found in the same order on the bigger list):
def is_ordered_subset(l1, l2):
# First check to see if all element of l1 are in l2 (without checking order)
if not set(l1).issubset(l2):
return False
length = len(l1)
# Make sublist of same size than l1
list_of_sublist = [l2[i:i+length] for i, x in enumerate(l2)]
#Check if one of this sublist is l1
return l1 in list_of_sublist
You can use numpy:
def contains(l1, l2):
""" returns True if l2 conatins l1 and False otherwise """
if len(np.intersect1d(l1,l2))==len(l1):
return = True
else:
return = False

Categories

Resources