Two Sum function not working with recurring elements in list - python-3.x

I'm trying to complete "Two Sum", which goes as such:
Write a function that takes an array of numbers (integers for the tests) and a target number. It should find two different items in the array that, when added together, give the target value. The indices of these items should then be returned in a tuple like so: (index1, index2).
Efficiency of my code aside, this is what I have so far:
def two_sum(numbers, target):
for i in numbers:
for t in numbers:
if i + t == target:
if numbers.index(i) != numbers.index(t):
return (numbers.index(i), numbers.index(t))
return False
It works for inputs such as:
>>> two_sum([1,2,3,4,5,6,7,8,9,10], 11)
(0, 9)
But when I try a list of numbers that have recurring numbers that add up to the target, the code doesn't work:
>>> two_sum([2, 2], 4)
False
The code, for some reason that I cannot figure out, does not reach index [1] of the list, and thus returns False.
Why is that?

The list method index() always returns the first occurence of an item in a list, so numbers.index(i) != numbers.index(t) evaluates to 1 != 1 which is False.
You should use the builtin enumerate() to store the indices while looping over the list.
def two_sum(numbers, target):
for i, number_a in enumerate(numbers):
for j, number_b in enumerate(numbers):
if number_a + number_b == target and i != j:
return (i, j)
return False

'''
return will break the loop and come out of function, so first you need to complete the cycle, store the result in list as you cant write to tuple,
once your loop gets completed convert list to tuple and return
'''
def two_sum(numbers, target):
result = []
for i in numbers:
for t in numbers:
if (i + t == target) and (numbers.index(i) != numbers.index(t)):
result.append(i)
result.append(t)
if (len(result)> 0):
return tuple(result)
else:
return False

Your code looks fine except this part:
if numbers.index(i) != numbers.index(t):
return (numbers.index(i), numbers.index(t))
return False
Because the index method returns only the first occurrence of a value, i and t are always the same. It will always return false. The index of the value 2 is always 0 in the list even though there is another 2 at index 1.
Source: https://www.w3schools.com/python/ref_list_index.asp
What you want to do is this:
def two_sum(numbers, target):
i_index = 0
t_index = 0
for i in numbers:
for t in numbers:
if i + t == target:
if i_index != t_index:
return (i_index, t_index)
t_index +=1
i_index +=1
return False
This way the index is not associated with the value

def pairs_sum_to_target(list1, list2, target):
'''
This function is about a game: it accepts a target integer named target and
two lists of integers (list1 and list2).
Then this function should return all pairs of indices in the form [i,j]
where list1[i] + list[j] == target.
To summarize, the function returns the pairs of indices where the sum of
their values equals to target.
Important: in this game list1 and list2 will always have the same number of
elements and returns the pairs in that order.
'''
pairs = [] #make a list, which is empty in the beginning. But store the sum pairs == target value.
#loop for all indices in list1 while looping all the same indices in list2 and comparing if the sum == target variable.
for i, value1 in enumerate (list1):
for j, value2 in enumerate(list2):
if value1 + value2 == target: ## if the value of element at indice i + value of element at indice j == target, then append the pairs to list pairs []- in order.
pairs.append(i,j)
return pairs
Simple Input #1
"""This is one example of input for list1, list2, and target. In order to properly test this function"""
list1 = [1,-2,4,5,9]
list2 = [4,2,-4,-4,0]

Related

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

Incorporate a while loop in a function in Python

Very new to any kind of coding. I would like to write a function that will return the elements of a numeric list, up to the first even number. For example, if the list is [1,5,7,8,9] it will return [1,5,7]
I know the below is not correct, but I am having trouble passing the list into the while loop.
def iter_up_to_even(num_lst):
i=0
new_lst=[]
while i < len(num_lst):
if i%2!=0:
new_lst.append(num_lst)
i=i+1
if i %2==0:
break
return new_lst
Looks like you might have some indentation issues. Try this solution:
def iter_up_to_even(num_list):
to_return = []
current_index = 0 if len(num_list) > 0 else len(num_list)
while current_index < len(num_list):
if num_list[current_index] % 2 == 1:
to_return.append(num_list[current_index])
else:
break
current_index += 1
return to_return
Explanation
We start with an empty list to_return, which we will return at the end of our function. Next, we iterate through each item in the input list num_list. If the input list num_list is empty to begin with, we don't even enter the while loop (see Line 3). If the item is odd, we append it to our to_return list. If it's even, we break from our loop and return to_return.
In addition, you should compare values in the list (e.g. num_list[i]), rather than the current index in the list (e.g. i).

Need help writing a function which returns a dictionary with the keys as recursive digit sums

So I have written a function which calculates the sum of the digits when a number is input to the function. Now I am trying to write another function which would return a dictionary with the values from my digitsum function as the keys and the values would be how many times the count of that specific digitsum has occurred. Any ideas on how to go about writing the second function?
def digitsum(x):
if x < 10:
return x
else:
return (x%10) + digitsum(x//10)
def digitsumdictionary(lnum=0, hnum=100):
L =[digitsum(num) for num in range(100)]
counter = Counter(L).items()
return counter
Digitsum function is called depending on the length of the number.
You can simply find it by using len(list(str(num))). But if you want to count as the function calls itself, Then try this,
def digitsum(x, count=1):
if x < 10:
return { x : count }
else:
return {(x%10) + int(list(digitsum(x//10 , count+1).keys())[0]) : int(list(digitsum(x//10 , count+1).values())[0])}
Setting the count to 1 or 0 initially, includes or excludes the first call respectively.
The below code returns a list of dictionaries of the desired output.
[digitsum(i) for i in range(10)]

How to compare character frequency and return the most occurring character?

I am trying to build a function which returns the most occurred character in a given string and it's working pretty nicely, but how do I return None if the characters have same frequency?
Like for input: 'abac'
Expected output is: 'a'
and for input: 'abab'
Expected output is: None
I have tried using a dictionary to store character frequency and then returning the element with largest value.
def most_occuring_char(str1):
count = {}
max = 0
c = ''
for char in str1:
if char in count.keys():
count[char]+=1
else:
count[char] = 1
for char in str1:
if max < count[char]:
max = count[char]
c = char
return c
I don't know how to check whether the count dictionary elements have same frequency.
You can do that counting with the dict using collections.Counter.
You basically only have to add a check to see if the maximum count is unique (if so, return the char with maximum number of occurrences) or not (if so, return None):
from collections import Counter
def most_occurring_char(string):
counter = Counter(string)
max_char_count = max(counter.values())
is_unique = len([char_count for char_count in counter.values() if char_count == max_char_count]) == 1
if is_unique:
char = [char for char, count in counter.items() if count == max_char_count][0]
return char
return None
# Tests
assert most_occurring_char('abac') == 'a'
assert most_occurring_char('abab') is None
Once you have a dictionary containing the counts of every character (after your first for loop), you can inspect this to determine whether certain counts are the same or not.
If you wish to return None only when all the character counts are the same, you could extract the values (i.e. the character counts) from your dictionary, sort them so they are in numerical order, and compare the first and last values. Since they are sorted, if the first and last values are the same, so are all the intervening values. This can be done using the following code:
count_values = sorted(count.values())
if count_values[0] == count_values[-1]: return None
If you wish to return None whenever there is no single most frequent character, you could instead compare the last value of the sorted list to the second last. If these are equal, there are two or more characters that occur most frequently. The code for this is very similar to the code above.
count_values = sorted(count.values())
if count_values[-1] == count_values[-2]: return None
Another possibility:
def most_occuring_char(s):
from collections import Counter
d = Counter(s)
k = sorted(d, key=lambda x:d[x], reverse=True)
if len(k) == 1: return k[0]
return None if len(k) == 0 or d[k[0]] == d[k[1]] else k[0]
#Test
print(most_occuring_char('abac')) #a
print(most_occuring_char('abab')) #None (same frequencies)
print(most_occuring_char('x')) #x
print(most_occuring_char('abcccba')) #c
print(most_occuring_char('')) #None (empty string)

Python losing track of index location in for loop when my list has duplicate values

I'm trying to iterate over pairs of integers in a list. I'd like to return pairs where the sum equals some variable value.
This seems to be working just fine when the list of integers doesn't have repeat numbers. However, once I add repeat numbers to the list the loop seems to be getting confused about where it is. I'm guessing this based on my statements:
print(list.index(item))
print(list.index(item2))
Here is my code:
working_list = [1,2,3,4,5]
broken_list = [1,3,3,4,5]
def find_pairs(list, k):
pairs_list = []
for item in list:
for item2 in list:
print(list.index(item))
print(list.index(item2))
if list.index(item) < list.index(item2):
sum = item + item2;
if sum == k:
pair = (item, item2)
pairs_list.append(pair)
return pairs_list
### First parameter is the name is the list to check.
### Second parameter is the integer you're looking for each pair to sum to.
find_pairs(broken_list, 6)
working_list is fine. When I run broken_list looking for pairs which sum to 6, I'm getting back (1,5) but I should also get back (3,3) and I'm not.
You are trying to use list.index(item) < list.index(item2) to ensure that you do not double count the pairs. However, broken_list.index(3) returns 1 for both the first and second 3 in the list. I.e. the return value is not the actual index you want (unless the list only contains unique elements, like working_list). To get the actual index, use enumerate. The simplest implementation would be
def find_pairs(list, k):
pairs_list = []
for i, item in enumerate(list):
for j, item2 in enumerate(list):
if i < j:
sum = item + item2
if sum == k:
pair = (item, item2)
pairs_list.append(pair)
return pairs_list
For small lists this is fine, but we could be more efficient by only looping over the elements we want using slicing, hence eliminating the if statement:
def find_pairs(list, k):
pairs_list = []
for i, item in enumerate(list):
for item2 in list[i+1:]:
sum = item + item2
if sum == k:
pair = (item, item2)
pairs_list.append(pair)
return pairs_list
Note on variable names
Finally, I have to comment on your choice of variable names: list and sum are already defined by Python, and so it's bad style to use these as variable names. Furthermore, 'items' are commonly used to refer to a key-value pair of objects, and so I would refrain from using this name for a single value as well (I guess something like 'element' is more suitable).

Resources