How do I solve a two-sum problem with multiple solutions? - python

So essentially it is a simple two sum problem but there are multiple solutions. At the end I would like to return all pairs that sum up to the target within a given list and then tally the total number of pairs at the end and return that as well. Currently can only seem to return 1 pair of numbers.
So far my solution has to been to try and implement a function that counts the amount of additions done, and while that number is less than the total length of the list the code would continue to iterate. This did not prove effective as it would still not take into account other solutions. Any help would be greatly appreciated

I took your code and did a couple of tweaks to where summations were being tested and how the data was being stored. Following is your tweaked code.
def suminlist(mylist,target):
sumlist = []
count = 0
for i in range(len(mylist)):
for x in range(i+1,len(mylist)):
sum = mylist[i] + mylist[x]
if sum == target:
count += 1
worklist = []
worklist.append(mylist[i])
worklist.append(mylist[x])
sumlist.append(worklist)
return count, sumlist
list = [0, 5, 4, -6, 2, 7, 13, 3, 1]
print(suminlist(list,4))
Things to point out.
The sumlist variable is defined as a list with no initial values.
When a summation of two values in the passed list equate to the test value, they are placed into a new interim list and then that list is appended to the sumlist list along with incrementing the count value.
Once all list combinations are identified, the count value and sumlist are returned to the calling statement.
Following was the test output at the terminal for your list.
#Dev:~/Python_Programs/SumList$ python3 SumList.py
(2, [[0, 4], [3, 1]])
To split the count value out from the list, you might consider splitting the returned data as noted in the following reference Returning Multiple Values.
Give that a try to see if it meets the spirit of your project.

You can use the itertools module for this job.
my_list = [1, 2, 3, 4]
target = 3
out = [x for x in itertools.combinations(my_list, r=2) if sum(x) == target]
print(out)
>>> [(0, 3), (1, 2)]
If you feel like using a python standard library import is cheating, the the official documentation linked above showcases example code for a "low level" python implementation.

Issue:
The issue for returning one set of possible several sets remains in the first return line (return sumlist). Based on the code, the function will automatically ends the function as the first set of value that their sum is the same as the target value. Therefore, we need to adjust it.
Adjustment:
I add a list(finallist[]) at the begining of the function for collecting all the applicable sets that can sum up to the target value. Then, I add a list(list[]) right after the if statement (*since I create an empty list(list[]) right after the if statement, when any sum of two values fulfills the target value, the function will empty the list again to store the new set of two values and append to the finallist[] again). Hence, as long as a set of two numbers can sum up to the target value, we can append them to the list(list[]). Accordingly, I add two more lines of code to append two values into the list(list[]). At the end, I append this list(list[]) to finallist[]. Also, I move the return statement to the final line and adjust the spacing. After this adjustment, the function will not end right after discovering the first possible set of values. Instead, the function will iterate repeatedly until getting all sets of the values and storing in the finalist[].
Originally, the function puts the return statement (return -1) at the end of the function for the situation that none of the sets can sum up to the target value. However, after the previous adjustment, the original return statement (return -1) will not have the opportunity to function as everything will end in the previous return line (return finallist). Therefore, I change it to the else part in the if statement (*meaning: when none of the sum of two values adds up to the target value, we will return 'No two values in the list can add up to the target value.')
Changes in Function:
def suminlist(mylist,target):
# count = 0 # delete
# while count < len(mylist): # delete
finallist=[] # add
for i in range(len(mylist)):
for x in range(i+1,len(mylist)):
sum = mylist[i]+mylist[x]
# count = count + 1 # delete
if sum == target:
# sumlist = mylist[i],mylist[x] # delete
# return sumlist # delete
list=[] # add
list.append(mylist[i]) # add
list.append(mylist[x]) # add
finallist.append(list) # add
else: # add
return 'No two values in the list can add up to the target value.' # add
return finallist # add
# return -1 # delete
Final Version:
def suminlist(mylist,target):
finallist=[]
for i in range(len(mylist)):
for x in range(i+1,len(mylist)):
sum = mylist[i]+mylist[x]
if sum == target:
list=[]
list.append(mylist[i])
list.append(mylist[x])
finallist.append(list)
else:
return 'No two values in the list can add up to the target value.'
return finallist
Test Code and Output:
list = [0, 5, 4, -6, 2, 7, 13, 3, 1]
print(suminlist(list,100))
# Output: No two values in the list can add up to the target value.
print(suminlist(list,4))
# Output: [[0, 4], [3, 1]]

Related

How to append to a list two numbers from within the list that add up to a number in the list?

First, I want to find the highest number in the list which is the second number in the list, then split it in two parts. The first part contains the 2nd highest number, while the second part contains the number from the list that sums to the highest number. Then, return the list
eg: input: [4,9,6,3,2], expected output:[4,6,3,6,3,2] 6+3 sums to 9 which is the highest number in the list
Please code it without itertools.
python
def length(s):
val=max(s)
s.remove(val)
for j in s:
if j + j == val:
s.append(j)
s.append(j)
return s
Here's what I have but it doesn't return what the description states.
Any help would be appreciated as I spent DAYS on this.
Thanks,
The main issue in your code seems to be that you are editing the list s whilst iterating through it, which can cause issues with the compiler and is generally just something you want to avoid doing in programming. A solution to this could be iterating through a copy of the original list.
The second problem is that your program doesn't actually find the second biggest value in the list, just a value which doubles to give you the biggest value.
The final problem (which I unfortunately only noticed after uploading what I thought was a solution) is that the split values are appended to the end of the list rather than to the position where originally the largest value was.
Hopefully this helps:
def length(array):
val = max(array)
idx = array.index(val) # gets the position of the highest value in the array (val)
array.remove(val)
for i in array.copy(): # creates a copy of the original list which we can iterate through without causing buggy behaviour
if max(array) + i == val:
array = array[:idx] + [max(array), i] + array[idx:]
# Redefines the list by placing inside of it: all values in the list upto the previous highest values, the 2 values we got from splitting the highest value, and all values which previously went after the highest value.
return array
This will return None if there is no value which can be added to the second highest value to get the highest value in the given array.
Input:
print(length([1,2,3,4,5]))
print(length([4,8,4,3,2]))
print(length([11,17,3,2,20]))
print(length([11,17,3,2,21]))
Output:
[1, 2, 3, 4, 4, 1]
[4, 4, 4, 4, 3, 2]
[11, 17, 3, 2, 17, 3]
None
Here are the docs on list slicing (which are impossible to understand) and a handy tutorial.
when you say "The first part contains the 2nd highest number" does that mean second highest number from the list or the larger of the two numbers that add up the largest number from list?
Here I assume you just wanted the larger of the two numbers that add up to the largest number to come first.
def length(s:list):
#start by finding the largest value and it's position in the list:
largest_pos = 0
for i in range(len(s)):
if s[i] > s[largest_pos]:
largest_pos = i
# find two numbers that add up to the largest number in the s
for trail in range(len(s)):
for lead in range(trail, len(s)):
if (s[trail] + s[lead]) == s[largest_pos]:
if s[trail] > s[lead]:
s[largest_pos] = s[trail]
s.insert(largest_pos +1, s[lead])
else:
s[largest_pos] = s[lead]
s.insert(largest_pos + 1, s[trail])
return s
# if no two numbers add up to the largest number. return s
return s
Since you are limited to 2 numbers, a simple nested loop works.
def length(s):
val = max(s)
idx = s.index(val)
s.remove(val)
for i in range(len(s) - 1):
for j in range(i + 1, len(s)):
if s[i] + s[j] == val:
s = s[:idx] + [s[i], s[j]] + s[idx:]
return s
print(length([4,9,6,3,2]))
Output:
[4, 6, 3, 6, 3, 2]
I used deque library
first to find the highest element or elements then remove all of them and replace them with second high value and rest like : 9 replace with 6 and 3 in example:
from collections import deque
l = [4, 9, 6, 3, 2]
a = deque(l)
e = a.copy()
s = max(a)
while s in a:
a.remove(s) # remove all highest elements
s2 = max(a) # find second high value
c = s - s2
for i in l:
if i == s:
w = e.index(i) # find index of high values
e.remove(max(e))
e.insert(w, s2)
e.insert(w+1, c)
print(list(e))

How can I change the index correct in a list? (Python)

I wrote some code to calculate the maximum path in a triangle.
75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
The maximum path sum is 75+95+82+87+82. I want that it calculates the maximum path from the adjecent numbers under the current layer. For example the maximum path sum must be: 75+95+47+87+82, because 47 'touches' 95 and 82doesn't. In the layer under this there is a choice between 35 and 87. So there is always a choice between two numbers. Does anyone how I can do this? This is my code:
lst = [[72],
[95,64],
[17,47,82],
[18,35,87,10],
[20,4,82,47,65]]
something = 1
i = 0
mid = 0
while something != 0:
for x in lst:
new = max(lst[i])
print(new)
i += 1
mid += new
something = 0
print(mid)
For each row the choice should be between two values. If you always want to pick the maximum between those two values, the first solution works. If you want to maximize the sum by sometimes picking the lower value, the second solution works.
values = []
index = 0 # the index of the 'max' number in the first row
for row in lst:
section = row[index:index+2]
value = max(section)
index = row.index(value)
values.append(value)
In this code we loop through each row, where the first row is just [75], the second row is [95, 64], etc.
For each row, we take the two numbers that are directly below our previous choice. For the first row, the choice must always be number in position 0, as it's the only number.
Then, we take the max of those two numbers. Then we take the index of that max number, which we will use to select the new two numbers on the next iteration.
Now all the values are stored in value. We can use sum(values) to get the sum.
Second solution
import itertools
# Create a new pyramid where each cell is the index of each number
index_lst = [[x for x in range(len(l))] for l in lst]
# Now index_lst looks like this:
# [[0],
# [0, 1],
# [0, 1, 2],
# [0, 1, 2, 3],
# [0, 1, 2, 3, 4]]
# get all possible paths, including those that are not valid such as [0, 0, 0, 0, 4]
all_possible_paths = itertools.product(*index_lst)
def check_validity(path):
# check the step size for each path.
# A step it should always be less then 2
# If the differene is too large, then it's not a valid path
for x in range(0,len(path)-1):
difference = abs(path[x] - path[x+1])
if difference > 1:
return False
return True
# Filter out all the paths that are not valid
valid_path = []
for path in all_possible_paths:
if check_validity(path):
valid_path.append(path)
# For all the valid paths, get the path that returns the maximum value
# here the max funciton takes in the paths, and then converts the path to the sum of the values
optimal_path = max(valid_path, key = lambda e: sum([lst[row][index] for row, index in enumerate(e)]))
# Actually conver the optimal path to values
optimal_values = [lst[row][index] for row, index in enumerate(optimal_path)]
print(optimal_path)
print(optimal_values)
print(sum(optimal_values))
edit: Some extra details about this line
optimal_path = max(valid_path, key = lambda e: sum([lst[row][index] for row, index in enumerate(e)]))
First, the max function can take a key parameter. This just tells the max function how it should actually determine the max. For example, with input [[0,5],[1,0]] I can use the key parameter to tell max to use the second elements (i..e, 5 and 0) in each list to determine the max, instead of 0 and 1.
In this case, we give it a lambda function. This basically means we pass a function as the key parameter, without defining the function before hand. It is conceptually imilar to doing this
def fun(e):
# do some stuff
optimal_path = max(valid_path, key = fun(e))
Next, I use a list comprehension. That's just a shorter way of writing a for loop.
[lst[row][index] for row, index in enumerate(e)]
Is the same thing as doing
output = []
for row, index in enumerate(e):
output.append(lst[row][index])
The enumerate takes an iterable (like ['a','b','c']) and also gives the index for each element. Thus list(enumerate(a)) returns [(0, 'a'), (1, 'b'), (2, 'c')]. In the for loop I immediately assign them to row and index.
The "triangle" is an example of a graph (in the computer science sense, not the maths sense).
You will have to use recursion in order to solve this, or some kind of stochastic algorithm. Personally, I'd use the genetic algorithm since the problem is combinatorial.

How remove elements in array with out messing with the counter or list length

Codewars: Given a list lst and a number N, create a new list that contains each number of lst at most N times without reordering. For example if N = 2, and the input is [1,2,3,1,2,1,2,3], you take [1,2,3,1,2], drop the next [1,2] since this would lead to 1 and 2 being in the result 3 times, and then take 3, which leads to [1,2,3,1,2,3].
delete_nth ([1,1,1,1],2) # return [1,1]
delete_nth ([20,37,20,21],1) # return [20,37,21]
I'll loop through the array to find the elements that have more than "x" amount. But The problem for me is that when I want to remove that element it changes the length of the array there for throwing off my loop counter. And then when I try another way by creating another list and then again looping through the original list and seeing if the element has more than "x" amount then I'll copy that element into the new array leaving the old array alone and it keeping its index and the loop is fine but now I do not know how to stop copying the element once it hits its desired amount. Please help me. I have been on this answer for a week now.
Maybe you could try this snippet to see that helps?
Have not done too many edge cases - so please raise questions, if run into some edges.
def delete_nth(lst, N):
seen = {}
res = []
for x in lst:
if x not in seen :
seen[x] = 0
else:
seen[x] += 1
if seen[x] <N:
res.append(x)
return res
print(delete_nth([1, 1, 1, 1], 2)) # [1, 1]
print(delete_nth([20, 37, 20, 22], 1)) # [20, 37, 22]

Generating Padovan Sequence

The Padovan sequence is governed by the relationship P(n+1) = P(n-1) + P(n-2), for n is a
non-negative integer, where P(0) = P(1) = P(2) = 1. So, for instance, P(3) = 2, P(4) = 2, and
P (5) = 3, and so on.
I want to write a Python program Pad(n) that generates the sequence P(0), P(1), ..., P(n - 1).
This is what I have this far, but it only produces a list with the ith number replicated up to the largest number in the sequence:
def ith(n):
first, sec, third, fourth = 1, 1, 1, 1
for i in range(3, n+1):
fourth = first + sec
first = sec
sec = third
third = fourth
return fourth
def pad(n):
pad = ith(n)
lst = []
for i in range(n):
lst.append(pad)
return lst
I want it to produce this as an output:
>>> Pad(6)
>>>[1,1,1,2,2,3]
Currently my code only produces:
>>>[4,4,4,4,4,4]
I now that I append the ith value ith number of times to the list, but I dont know how to append each number in series up to and including the value for the last number. Pad(6) yields 4 since this is all the previous relationships put together.
Sorry for my bad description and formulating of the problem.
You have a two minor errors in your pad() function.
First you should be calling the ith() function inside the loop (also don't name the variable pad as that's the name of the function and it can cause problems).
Secondly, you are calling ith(n) inside the loop when you should be calling ith(i). This is the reason that you were always getting the same number- the argument to ith() was not changing inside the loop.
A fixed version of your pad() function would be:
def pad(n):
lst = []
for i in range(n):
val = ith(i)
lst.append(val)
return lst
You can verify that this indeed produces the correct output of [1, 1, 1, 2, 2, 3] for pad(6).
More efficient non-recursive method
Now, while your method works, it's also very inefficient. You are calling the ith() function for every value in range(n) which recomputes the entire sequence from the beginning each time.
A better way would be to store the intermediate results in a list, rather than by calling a function to get the ith() value.
Here is an example of a better way:
def pad2(n):
lst = [1, 1, 1] # initialize the list to the first three values in the sequence
for i in range(3,n):
lst.append(lst[i-2] + lst[i-3]) # use already computed values!
# slice the list to only return first n values (to handle case of n <= 3)
return lst[:n]
Recursive method
As seen on Wikipedia, the recurrence relation shows us that pad(n) = pad(n-2) + pad(n-3).
Use this as the starting point for a recursive function: return pad(n-2) + pad(n-3)
This is almost everything you need, except we have to define the starting values for the sequence. So just return 1 if n < 3, otherwise use the recurrence relation:
def pad_recursive(n):
if n < 3:
return 1
else:
return pad_recursive(n-2) + pad_recursive(n-3)
Then you can get the first n values in the sequence via list comprehension:
print([pad_recursive(n) for n in range(6)])
#[1, 1, 1, 2, 2, 3]
But this suffers from the same drawback as your original function as it computes the entire sequence from scratch in each iteration.

Recursive Selection sort to return a descending list in python

I'd been working through a problem, to sort the elements in a descending order through a recursive approach, the code is as follows..
import operator
def do_stuff(elem_list):
if not elem_list:
return None
max_index , max_element = max(enumerate(elem_list) , key = operator.itemgetter(1))
elem_list[max_index],elem_list[0] = elem_list[0],elem_list[max_index]
return do_stuff(elem_list[1:])
p_list = [4,2,3,5,1]
do_stuff(p_list)
print(p_list)
Output -
[5, 2, 3, 4, 1]
And I can't seem to figure wherein lies the problem and why won't I get the desired output ?
I was able to fix your problem by adding an extra parameter, since you seem to be using a recursive implementation of insertion sort, you need some way to track the next open place to swap values in the list.
import operator
def sortl(l, last):
# base case
if last + 1 >= len(l):
return l
# find the max index (mi) and max value in the list
mi, _ = max(enumerate(l[last:]), key = operator.itemgetter(1))
mi += last # caculate the offset in the sublist
l[mi], l[last] = l[last], l[mi] # swap the values
# recursive call
return sortl(l, last + 1)
By using "last + 1" every time, you are able to simulate using the underlying sublist since calling do_stuff([some_list[1:]) wont work
Python's slices are not true references to the underlying list. See: Can I create a "view" on a Python list?

Categories

Resources