Hi I am new to Python and am trying to solve a coding challenge where the objective is to create a function which takes an array of integers (both negative and positive) and a target value (also an integer) and then outputs true if any combinations of the numbers sum up to the target value, which will always be larger than any other single element in the input array. I tried to do this but horribly failed. I found someone else's code who did it in 2 lines, but I have no idea how it works and was hoping someone here could point me in the right direction. Here is the code
def subsetsum(target, arr):
if len(arr) == 0:
return target == 0
return subsetsum(target, arr[1:]) or subsetsum(target - arr[0], arr[1:])
I mean I don't even see where they sum up the numbers and compare to the target, any help will be greatly appreciated. Thanks in advance.
Their solution uses recursion to express all possible combinations of the numbers in a pretty clever way. But clever does not always mean readable or easy to understand (which all code should be) so don't feel bad for not getting it right away. Personally, I would not consider this good code.
The simplest way to think of it is to work backwards.
What happens if arr has 0 elements? Well, it's explicitly checked at the beginning of the function: it can only be a solution to the problem if target is already 0. Let's refer to this special case of subsetsum (where arr is []) as is_zero, because that's all it does.
What happens if arr has 1 element [x]? For a solution to exist, you have to get target by either including or not including x in the sum. This is done by converting the problem in to the 0-element version, as arr[1:] will be the empty list ([]). The first part of the or expression, subsetsum(target, arr[1:]), ignores x and basically asks if is_zero(target). The second part of the expression includes x in the sum by asking if is_zero(target - x). Try it with arr = [9]. You get True if is_zero(target) OR if is_zero(target - 9). Which makes sense, right? If the target is 0 or 9, then [0] or [9] are solutions to the problem, respectively.
What happens if arr has 2 elements [x, y]? Again, the problem is reduced into the 1-element-smaller version, as arr[1:] is just [y]. So, is there a solution in subsetsum(target, [y]) OR is there a solution in subsetsum(target - x, [y]). Try it with [3, 9]. The first part of the expression ignores 3 and evaluates True if target is 9 or 0. The second part includes 3 and evaluates True if target - 3 is 9 or 0 (i.e. if target is 12 or 3). Thus, it "tries" all possible combinations: [], [9], [3], and [9, 3].
subsetsum(target, [x, y, z]) is reduced to subsetsum(target, [y, z]) OR subsetsum(target - x, [y, z]), and so on...
Looking from the top down now, what happens is that for every element in arr, the question is split into two subquestions of "is there a solution if we do not include this element?" and "is there a solution if we do include this element?" That way, you end up trying all 2**len(arr) combinations.
A much cleaner (and more efficient) solution to this problem is definitely possible using Python's really sweet module itertools, but I don't have time to write it at the moment. I may try it tomorrow just for fun, though.
Edit: Here's my more readable (and probably faster) version.
import itertools
def subsetsum(target, arr):
for subset_len in range(len(arr) + 1):
if any(sum(subset) == target for subset in itertools.combinations(arr, subset_len)):
return True
return False
It simply finds all subsets, starting with length 0 (so []) and working up to length len(arr), and checks if any of them sum to target. Python is a beautiful language!
edit There's a bug in that code (depending on whether you think there is a combination of the numbers in [1] that sum up to 0). Try
subsetsum(0,[1])
> True
I explain a bit more below.
end edit
So I think the challenge in understanding the code isn't so much about Python as about understanding a recursive function.
Let's assume that the function subsetsum works for lists of length less than N. Now let's take some list of length N. If there is a subset that sums to the target it either involves the first element (arr[0]) or it doesn't. So we'll break this into two problems - checking if there is a subset that has the first element, and checking if there is a subset that doesn't.
Note that arr[1:] is an array of length N-1 starting with the second element of arr.
So if there is a subset that does not involve arr[0], then subsetsum(target,arr[1:]) will give True. If there is a subset that involves arr[0] then subsetsum(target-arr[0],arr[1:]) will return True. If neither holds then it's going to return False.
So basically whether this works for N depends on whether it works for N-1, which depends on whether it works for N-2 etc. Finally we get down to length 0. If the target is 0, at this point, it should return True. If not, the False, or at least, that's what the person writing this code thought. I disagree. Explanation of the bug below.
Personally, I would argue that there is a bug in this code. If I give it any list and a target of 0, it'll return True. The test should be that when it gets down to a length 1 list, is the target equal to that one value. The test should be if the first element is the target, and if it gets down to a length 0 list, it's failed.
def subsetsum(target, arr):
if len(arr)==0:
return False
elif arr[0]==target:
return True
else:
return subsetsum(target, arr[1:]) or subsetsum(target - arr[0], arr[1:])
Related
You are given an array of integers. You should find the sum of the integers with even indexes (0th, 2nd, 4th...). Then multiply this summed number and the final element of the array together. Don't forget that the first element has an index of 0.
For an empty array, the result will always be 0 (zero).
Input: A list of integers.
Output: The number as an integer.
Precondition: 0 ≤ len(array) ≤ 20
all(isinstance(x, int) for x in array)
all(-100 < x < 100 for x in array
result = 0
if array:
for element in array:
i = array.index(element)
if i%2 == 0:
result += element
else:
pass
else:
return 0
return result
Last_digit = array[-1]
final_result = result*Last_digit
return final_result
print(final_result)```
I've figured out the problem, that you've shared the array you're having problem with. Since you have this array :
[-37,-36,-19,-99,29,20,3,-7,-64,84,36,62,26,-76,55,-24,84,49,-65,41]
If you notice here, 84 appears twice, first at index 9 and then 16. The method you're using to get index of elements, .index returns the index of the first instance the element is found in the list.Therefore for the value of 84, the index is taken as 9 and not 16 which is an odd value, this does not add 84 to your sum. You should rather use enumerate for your code:
for idx, element in enumerate(array):
if idx %2 == 0:
result += element
First, I recommend reading the stackexchange guides on posting a well-formed question. You need to state what your goal is, what you've tried, what errors get thrown, and what the output should look like -- along with code examples and a minimal reproducible example as needed.
However, I'll help you out anyway.
You have a dangling return at line 11:
else:
return 0
return result
This makes no sense, as you've already returned 0. This is also apparently a snippet from a function, no? Post the whole function. But based on the instructions, you could try this:
import random
array = random.sample(range(-100, 100), 20)
def etl_func(arr):
arrsum = 0
for i, val in enumerate(arr):
if i%2 == 0: arrsum += val
return (arrsum * arr[-1])
answer = etl_func(array)
print(answer)
Note that importing random and using array = random.sample(range(-100, 100), 20) are not necessary if you're already GIVEN an array to work with. They're included here just as an example.
Also note that it's unnecessary to use an else: pass. If the condition evaluates to true (i.e. i%2 == 0), the if block will be executed. If i%2 != 0, the loop will short circuit automatically and move to the next iteration. Adding else: pass is like telling someone sitting in your chair to sit in your chair. You're telling the program to do what it's already going to do anyway. There's nothing necessarily wrong with including the else: pass, if it really want to... but it's just adding lines of meaningless code, which nobody wants to deal with.
EDIT: I don't know whether you were supposed to write a function or just some code (back to the "ask a well-formed question" issue...), so I went with a function. It should be trivial to turn the function into just plain code -- but you want to get into the habit of writing good functions for reusability and modularity. Makes everything run more smoothly and elegantly, and makes troubleshooting much easier.
This function also works for the array mentioned in the comments to your original post.
In addition, if you need a direct replacement for your code (rather than a function... I'm not familiar with checkio or how your answers are supposed to be formatted), and you already have the array of integers stored in the variable array, try this:
arrsum = 0
for i, val in enumerate(array):
if i%2 == 0: arrsum += val
print(arrsum * array[-1])
Since your question didn't say anything about using or defining functions, return statements shouldn't appear anywhere. There's nothing to return unless you're writing a function.
The biggest problem for me is to express the value of mid according to the range.
plz I need a help Even if it's not a perfect answer, I need a hint. I don't even know if the reason I'm having a hard time is that Python is awkward or that I'm stupid.
def bsearch(ss, x): #just call bsearch_range
return bsearch_range( ss, x, range(len(ss)) )
def bsearch_range(ss, x, r): #ss is list, r=range(len(ss))
left,right=0,0
while len(r)>0:
mid= (r.start+(r.stop-1)) // 2
if ss[mid]==x:
left,right=mid,mid+1
return range(left,right)
elif x<ss[mid]:
right=right-1
else:
left=left+1
return range(left,right)
First. The bsearch function is a binary search function that tells you the range you want to find in range.
Second. Lists are sorted and may contain duplicate numbers.
For example) list ss=[1,2,2,2,3,6]
So. If duplicate elements exist in the list, we need to express the range of duplicate elements.
For example) list ss[1,2,2,2,3,6] , bsearch(ss,2)==range(1,4)
Third. If the element you want to find does not exist in the list, you should be able to tell the place where it should be in range.
For example) ss[1,2,2,2,3,6] bsearch(ss,7)==range(6,6)
Looking at your code, I believe you did not fully understand how the "normal" binary search algorithm works. (What is r for? It does not change throughout the loop body, so the while loop goes on forever...)
As a hint, this is what binary search in Python looks like:
def bi_search_left(array: list[int], x: int) -> int:
a, b = 0, len(array)
while a < b:
mid = (a + b) // 2
if array[mid] < x:
a = mid + 1
else:
b = mid
return a
Notice, the name of the function is bi_search_left. That is because it returns the index of the leftmost element matching the value of the x parameter. (Actually, it returns the index at which x needs to be inserted in in order for it to become the first element in the list with this value, so it also works for empty lists.)
By changing the function just a tiny bit, you could get bi_search_right, which, as the name says, returns the rightmost index. Combining these two methods could get you the desired result.
I think that was enough of a hint :)
My Problem is the following:
Given a sequence of integer values, determines if there is a distinct pair of numbers in the sequence whose product is odd. Please provide two Python functions, oddpair_bf() and oddpair_linear() for this problem. The function will take the given sequence of integers as a list. The oddpair_bf() function uses a Brute-force approach and check the possible pairs sequestially. When there is pair whose product is odd, it returns True; otherwise, it reports False. The second one, oddpair_linear(), uses a linear-scan approach and will visit each element once. Pleace have a way to determine this with a linear-scan.
I tried solving it on my own and got:
def oddpair_bf(list):
for i in list:
for j in list:
if i != j:
product = i*j
if product & 1:
return True
return False
Now my question is, is this a brute-force approach or "linear-scan" approach? And how would I need to approach it differently?
Here is a concise linear function that checks to see if there is more than one odd number in the sequence (which would give at least one odd product) and returns True.
def oddpair_linear(seq):
return len([x for x in seq if x & 1]) > 1
Your approach is brute force as it explores all possible combinations.
Nice usage of the & operator instead of the classical (x % 2) == 1.
Brute force
I would suggest two improvements in your code:
Do not use list as a variable name because it is a reserved language keywords for list.
Halve tests as multiplication is commutative (symmetrical).
It leads to:
def oddpair_bf(seq):
n = len(seq)
for i in range(n):
for j in range(i+1, n):
if seq[i]*seq[j] & 1:
return True
return False
Which can be condensed using itertools:
def oddpair_bf2(seq):
for x, y in itertools.combinations(seq, 2):
if x*y & 1:
return True
return False
This new version is still in O(n^2) for the worst case. But you spare unnecessary comparisons by removing n + n*(n-1)/2 (diagonal and lower triangle) cases from n^2 (square, two nested loops of size n) simply because multiplication is commutative: We do not need to check y*x in addition of x*y.
Linear
Reducing complexity before brute force version is generally done by highlighting an inherent property of the system that makes computations easier, less intensive and thus more tractable.
For linear version, use a well known property of the problem: Any product of an even number will always be an even number because it has at least a 2 factor coming from the even number.
Therefore, solving this problem is equivalent to check if there is at least two odd numbers in the list. This can be written as:
def oddpair_linear(seq):
n = 0
for x in seq:
if x & 1:
n += 1
if n >= 2:
return True
return False
This snippet is O(n) in the worst case (a single loop of size n). This check has been nicely condensed into a one-liner by #pakpe.
since you said distinct, you can use a set:
def oddpair_linear(seq):
return len({s for s in seq if s&1})>1
or a slightly better way
def oddpair_linear(seq):
found=0
for s in seq:
if s&1:
if not found:
found=s
else:
return True
return False
I'm trying to learn more about algorithms and i'm looking into the bubble sort algorithm. I found the script for it on github but I cant really understand it. I'm sorta new to python so can someone explain to me what's going on in this script.
from __future__ import print_function
def bubble_sort(arr):
n = len(arr)
# Traverse through all array elements
for i in range(n):
# Last i elements are already in place
for j in range(0, n-i-1):
# traverse the array from 0 to n-i-1
# Swap if the element found is greater
# than the next element
if arr[j] > arr[j+1] :
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
if __name__ == '__main__':
try:
raw_input # Python 2
except NameError:
raw_input = input # Python 3
user_input = raw_input('Enter numbers separated by a comma:').strip()
unsorted = [int(item) for item in user_input.split(',')]
print(*bubble_sort(unsorted), sep=',')
Visualize the array as a vertical list of numbers, with the first element (index 0) on the bottom, and the last element (index n-1) at the top. The idea of bubble sort is that numbers "bubble up" to the top, into the place where they belong.
For example, [2,3,1] would first look at 2 and 3, and not do anything because they're already in order. Then it would look at 3 and 1, swapping them since 3>1 and getting [2,1,3]. Then we repeat by looking at 2 and 1, swapping them since 2>1 to get [1,2,3], which is in order.
The idea is that "3" and then "2" bubbled up to the correct position.
Note that after the 3 bubbled up, we don't have to compare 2 and 3, because we know the last element is already higher than everything before it. In general, after i iterations of bubble sort, there's no need to compare the last i elements.
from __future__ import print_function Here we are essentially bringing in code that was written by somebody else, so that we may use it.
def bubble_sort(arr): This is is a function definition. A function definition is preceded by the keyword def. Following that is the function's name. In this case it is called bubble_sort. What we have in the parenthesis are called parameters. A parameter is something we give to a function, so that the function may use it, e.g., multiply the parameter by a number, sort the list, or send some information to a server.
Since we are on the topic of functions, I would suggest looking up process abstraction.
arr Here I am referring to arr within the function's definition. It is short for array, which is a list type. In python we could define an array like so fruits = ["banana", "apple", "orange"]. Arrays are useful for grouping together like pieces of information, and in python I believe this are actually known as a list type. So, conceptually, it may be easier to imagine a list rather than the more esoteric array.
n = len(arr) We are literally assigning the length of the array into the variable n. This is probably shorthand for number of elements. len(arr) is a function that takes an array/list, and returns its length. Similarly, one could call print len(arr) or simply len(arr).
for j in range(0, n-i-1): This is a bit more complicated since it requires an understanding of the algorithm in play, i.e., bubblesort. I won't explain how bubblesort works since there is probably a ton of videos online, but I will explain the bit within the parenthesis.
(0, n-i-1) We want to make comparisons between our current element and the ones preceding it. The ones preceding our current element are greater than the current element. This means if we are at element i, then we have no need to compare elements from i to n, inclusive. We subtract i from n, which leaves us with elements 0 through i. We don't need to compare i to itself, so we subtract an additional 1. This is due to j cycling through the array, and being potentially the same as i.
if arr[j] > arr[j+1] : This is a conditional statement, also known as a branching statement, or an if-statement. The condition, arr[j] > arr[j+1], is true with the element at position j is greater than the one at j+1.
arr[j], arr[j+1] = arr[j+1], arr[j] I think this is shorthand for swapping. A simple swap is shown below.
temp = arr[j]
arr[j] = arr[j+1]
arr[j+1] = temp
return arr Returns the sorted array.
The last bit I am not familiar with since I don't use python much. Perhaps this could be some research for you.
Hopefully this helps.
I am asking in reference to this code:
array = [-37,-36,-19,-99,29,20,3,-7,-64,84,36,62,26,-76,55,-24,84,49,-65,41]
print sum(i for i in array if array.index(i) % 2 == 0)*array[-1] if array != [] else 0
You could see it here: Python code for sum with condition
Is this Quadratic because of a for loop followed by an if statement, inside the brackets?
Is this code, proposed by one more person on the same page - array[-1] * sum(array[::2]) free of Quadratic behaviour?
I think, it's again a Quadratic one, as it has to do a traversal and that too an alternate one.
Thanks in advance.
Yes, it's the array.index that makes it quadratic.
Let's first cut all irrelevant stuff away. The conditionals are for the complexity reasoning irrelevant (we will have array != [] and that check takes O(1) time). The same goes with the multiplication with array[-1]. So you're left with:
sum(i for i in array if array.index(i) % 2 == 0)
Now the inner is a generator and it would expand to an annonymous function looping through array and yielding a bunch of values, at most one per iteration. The sum function receives these values and adds them up.
The confusing thing maybe how a generator actually works. It actually works by running the generator intermixed with code from the consumer. This results in the complexity is the sum of the complexity of the generator and the consumer (ie sum). Now sum has linear complexity (it should have, it would if I wrote it).
So for the generator, it loops through the array, but for each element in the array it calls array.index which is of O(N) complexity.
To fix this you might use enumerate to avoid having to call array.index(i), it may or may not be what you want to do since array.index(i) returns the first index for which the element is i which might not be the index where you actually found i:
sum(i for idx, i in enumerate(array) if idx % 2 == 0)
To see the difference consider the list array = [0, 1, 2, 2], the first solution should sum up this to 4 since array.index(2) == 2 so it would also add the second 2. The later solution however will add up to 2 since enumerate will enumerate the elements in array, yielding pairs (0,0), (1,1), (2,2), (3,2) - where the first component is the actual index while the second is the actual element. Here the second 2 is omitted because it's actually got from index 3.
The first solution is indeed quadratic: when you call array.index you basically iterates each time on array again, so it behaves like an embedded loop.
The second solution traverses the list only one time, skipping each odd indexes.