Suggestion for best solution - Python - python

I am learning python since last few days, I have read a problem on a site where they provided the solution with some built-in function. I have tried to do it without it, could you please check if i have done this correctly or should it be done with some other logic.
Problem: Write a Python program to find the list in a list of lists whose sum of elements is the highest.
Solution:
Code
num = [1,2,3], [4,5,6], [10,11,12], [7,8,9], [87,6,9], [87,7,9]
def func(l):
#function will return the element (list) whose sum is greater
res = []
flag = 0
for index, items in enumerate(l):
total = 0
for item in items:
total = total + item
res.append(total)
if total > res[index]:
flag = index
return l[flag]
#function call
print(func(num))

No, your code is not correct. The problem is with the res list. Not sure that that's supposed to do, but you just keep appending items and then check the item at the index of the current outer loop. Instead, you should just keep track of the best_sum seen so far. Also, I'd suggest using better variable names.
def func(l):
best_sum = 0
best_index = 0
for index, items in enumerate(l):
total = 0
for item in items:
total = total + item
if total > best_sum:
best_index = index
best_sum = total
return l[best_index]
num = [1,2,3], [4,5,6], [10,11,12], [7,8,9], [87,6,9], [87,7,9], [1,1,1]
print(func(num))
Of course, using builtins max and sum, you can just do
print(max(num, key=sum))

If you want the actual sum, you could take the max of the sum of each list.
m = max(sum(x) for x in num)
If you want the list:
for x in num:
if sum(x) == m:
print(x)

Related

Given a list of numbers return a list in which all equal adjacent elements have been reduced to a single element

I need to write a code that Given a list of numbers return a list in which all
equal adjacent elements have been reduced
to a single element.
example:
list=[1,1,3,4,5,5,6,7,7,7,8,5,7]
becomes
[1,3,4,5,6,7,8,5,7]
i wrote this code:
list = []
list1 = []
for i in range(10):
c = input("inserisci un numero: ")
list.append(c)
k = 0
for i in range(len(list)-1):
if list[i+1] != list[i]:
list1[k].append(list[i])
k+=1
print(list1)
ut it's giving me index error could you explain me why?
You want list1.append(list[i]), not list1[k]. The k variable is useless.
The reason it explodes is that, the first time through the second loop, you refer to list1[0], but list1 is empty. It has no elements, so index 0 is out of bounds.
Add the first element to result list
result.append(list[0])
then just loop from 1 to the end of the original list.
On each iteration just check if list[i-1] != list[i]: if true add to result list like this result.append(list[i])
Using itertools.groupby function from the standard library you can group similar terms.
import itertools as it
l = [1,1,3,4,5,5,6,7,7,7,8,5,7]
ll = []
_ = [ll.append(next(grp)) if check else ll.append(grp) for check, grp in it.groupby(l)]
print(ll)

Use list comprehensions to make a list of count of elements smaller than the element in an array

I was solving this leetcode problem - https://leetcode.com/problems/how-many-numbers-are-smaller-than-the-current-number/
I solved it easily by using nested for loops but list comprehensions have always intrigued me. Ive spent a lot of time to make that one liner work but I always get some syntax error.
here's the solution:
count = 0
ans = []
for i in nums:
for j in nums:
if i > j:
count = count + 1
ans.append(count)
count = 0
return ans
these were the ones so far I think shouldve worked:
return [count = count + 1 for i in nums for j in nums if i > j]
return [count for i in nums for j in nums if i > j count = count + 1]
return [count:= count + 1 for i in nums for j in nums if i > j]
Ill also be happy if there's some resource or similar to put it together, Ive been searching the python docs but didnt find something that'll help me
I will transform the code step by step in order to show the thought process.
First: we don't care what the value of count is afterward, but we need it to be 0 at the start of each inner loop. So it is simpler logically to set it there, rather than outside and then also at the end of the inner loop:
ans = []
for i in nums:
count = 0
for j in nums:
if i > j:
count = count + 1
ans.append(count)
return ans
Next, we focus on the contents of the loop:
count = 0
for j in nums:
if i > j:
count = count + 1
ans.append(count)
A list comprehension is not good at math; it is good at producing a sequence of values from a source sequence. The transformation we need to do here is to put the actual elements into our "counter" variable1, and then figure out how many there are (in order to append to ans). Thus:
smaller = []
for j in nums:
if i > j:
smaller.append(j)
ans.append(len(smaller))
Now that the creation of smaller has the right form, we can replace it with a list comprehension, in a mechanical, rule-based way. It becomes:
smaller = [j for j in nums if i > j]
# ^ ^^^^^^^^^^^^^ ^^^^^^^^
# | \- the rest of the parts are in the same order
# \- this moves from last to first
# and then we use it the same as before
ans.append(len(smaller))
We notice that we can just fold that into one line; and because we are passing a single comprehension argument to len we can drop the brackets2:
ans.append(len(j for j in nums if i > j))
Good. Now, let's put that back in the original context:
ans = []
for i in nums:
ans.append(len(j for j in nums if i > j))
return ans
We notice that the same technique applies: we have the desired form already. So we repeat the procedure:
ans = [len(j for j in nums if i > j) for i in nums]
return ans
And of course:
return [len(j for j in nums if i > j) for i in nums]
Another popular trick is to put a 1 in the output for each original element, and then sum them. It's about the same either way; last I checked the performance is about the same and I don't think either is clearer than the other.
Technically, this produces a generator expression instead. Normally, these would be surrounded with () instead of [], but a special syntax rule lets you drop the extra pair of () when calling a function with a single argument that is a generator expression. This is especially convenient for the built-in functions len and sum - as well as for any, all, max, min and (if you don't need a custom sort order) sorted.
Hmm, three people write sum solutions but every single one does sum(1 for ...). I prefer this:
[sum(j < i for j in nums) for i in nums]
Instead of trying to advance an external counter, try adding ones to your list and then sum it:
for example:
nums = [1,2,3,4,5]
target = 3
print(sum(1 for n in nums if n < target))
Using counter inside the list comprehension creates the challenge of resetting it's value, each iteration of the first loop.
This can be avoided by filtering, and summing, in the second loop:
You use the first loop to iterate over the values of nums array.
return [SECOND_LOOP for i in nums]
You use the second loop, iterating over all elements of nums array. You filter in the elements that are smaller than i, the current element in the first loop, with if i < j, and evaluating 1 for each of them. Finally, you sum all the 1s generated:
sum(1 for j in nums if i > j)
You get the number of values that meet the requirements, by the list comprehension of the first loop:
return [sum(1 for j in nums if i > j) for i in nums]
This solution has been checked & validated in LeetCode.
You need a slightly different approach for the inner loop than a list comprehension. Instead of repeatedly appending a value to a list you need to repeatedly add a value to a variable.
This can be done in a functional way by using sum and a generator expression:
count = 0
# ...
for j in nums:
if i > j:
count = count + 1
can be replaced by
count = sum(1 for j in nums if i > j)
So that we now have this:
ans = []
for i in nums:
count = sum(1 for j in nums if i > j)
ans.append(count)
return ans
This pattern can in fact be replaced by a list comprehension:
return [sum(1 for j in nums if i > j) for i in nums]
Alternative Solution
We can also use the Counter from collections:
class Solution:
def smallerNumbersThanCurrent(self, nums):
count_map = collections.Counter(nums)
smallers = []
for index in range(len(nums)):
count = 0
for key, value in count_map.items():
if key < nums[index]:
count += value
smallers.append(count)
return smallers

How to retrieve subset in partitioning algorithm?

I have an array and I would like to split it two parts such that their sum is equal for example [10, 30, 20, 50] can be split into [10, 40] , [20, 30]. Both have a sum of 50. This is essentially partitioning algorithm but I'd like the retrieve the subsets not just identify whether it's partitionable. So, I went ahead and did the following:
Update: updated script to handle duplicates
from collections import Counter
def is_partitionable(a):
possible_sums = [a[0]]
corresponding_subsets = [[a[0]]]
target_value = sum(a)/2
if a[0] == target_value:
print("yes",[a[0]],a[1:])
return
for x in a[1:]:
temp_possible_sums = []
for (ind, t) in enumerate(possible_sums):
cursum = t + x
if cursum < target_value:
corresponding_subsets.append(corresponding_subsets[ind] + [x])
temp_possible_sums.append(cursum)
if cursum == target_value:
one_subset = corresponding_subsets[ind] + [x]
another_subset = list((Counter(a) - Counter(one_subset)).elements())
print("yes", one_subset,another_subset)
return
possible_sums.extend(temp_possible_sums)
print("no")
return
is_partitionable(list(map(int, input().split())))
Sample Input & Output:
>>> is_partitionable([10,30,20,40])
yes [10, 40] [30, 20]
>>> is_partitionable([10,30,20,20])
yes [10, 30] [20, 20]
>>> is_partitionable([10,30,20,10])
no
I'm essentially storing the corresponding values that were added to get a value in corresponding_subsets. But, as the size of a increases, it's obvious that the corresponding_subsets would have way too many sub-lists (equal to the number of elements in possible_sums). Is there a better/more efficient way to do this?
Though it is still a hard problem, you could try the following. I assume that there are n elements and they are stored in the array named arr ( I assume 1-based indexing ). Let us make two teams A and B, such that I want to partition the elements of arr among teams A and B such that sum of elements in both the teams is equal. Each element of arr has an option of either going to team A or team B. Say if an element ( say ith element ) goes to team A we denote it by -a[i] and if it goes to team B we let it be a[i]. Thus after assigning each element to a team, if the total sum is 0 our job is done. We will create n sets ( they do not store duplicates ). I will work with the example arr = {10,20,30,40}. Follow the following steps
set_1 = {10,-10} # -10 if it goes to Team A and 10 if goes to B
set_2 = {30,-10,10,-30} # four options as we add -20 and 20
set_3 = {60,0,20,-40,-20,-60} # note we don't need to store duplicates
set_4 = {100,20,40,-40,60,-20,-80,0,-60,-100} # see there is a zero means our task is possible
Now all you have to do is backtrack from the 0 in the last set to see if the ith element a[i] was added as a[i] or as -a[i], ie. whether it is added to Team A or B.
EDIT
The backtracking routine. So we have n sets from set_1 to set_n. Let us make two lists list_A to push the elements that belong to team A and similarly list_B. We start from set_n , thus using a variable current_set initially having value n. Also we are focusing at element 0 in the last list, thus using a variable current_element initially having value 0. Follow the approach in the code below ( I assume all sets 1 to n have been formed, for sake of ease I have stored them as list of list, but you should use set data structure ). Also the code below assumes a 0 is seen in the last list ie. our task is possible.
sets = [ [0], #see this dummy set it is important, this is set_0
#because initially we add -arr[0] or arr[0] to 0
[10,-10],
[30,-10,10,-30],
[60,0,20,-40,-20,-60],
[100,20,40,-40,60,-20,-80,0,-60,-100]]
# my array is 1 based so ignore the zero
arr = [0,10,20,30,40]
list_A = []
list_B = []
current_element = 0
current_set = 4 # Total number of sets in this case is n=4
while current_set >= 1:
print current_set,current_element
for element in sets[current_set-1]:
if element + arr[current_set] == current_element:
list_B.append(arr[current_set])
current_element = element
current_set -= 1
break
elif element - arr[current_set] == current_element:
list_A.append(arr[current_set])
current_element = element
current_set -= 1
break
print list_A,list_B
This is my implementation of #sasha's algo on the feasibility.
def my_part(my_list):
item = my_list.pop()
balance = []
temp = [item, -item]
while len(my_list) != 0:
new_player = my_list.pop()
for i, items in enumerate(temp):
balance.append(items + new_player)
balance.append(items - new_player)
temp = balance[:]
balance = set(balance)
if 0 in balance:
return 'YES'
else:
return 'NO'
I am working on the backtracking too.

count in python list comprehension

How do i write a list comprehension in python with an inclusion of a
count = count + 1?
print sum([info[count]["count"] for i in info])
# This is the working loop:
count = 0
lst = []
for i in info:
num = info[count]["count"]
# print num
count = count + 1
lst.append(num)
print sum(lst)
>>> a = ['a','b','c']
>>> v = ['a','e','i','o','u']
>>> len(a)
3
>>> sum([1 for x in a if x in v])
2
I do not follow why you use i in i in info together with count. If info is enumerable and enumerating has the same effect as accessing with a zero offset index (like you seem to do with count, you can rewrite your loop like:
lst = []
for infoi in info:
num = infoi["count"]
# print num
lst.append(num)
print sum(lst)
Now you can convert this to the following list comprehension:
sum([infoi["count"] for infoi in info])
Finally you do not need to materialize the comprehension to a list, sum can work on a generator:
sum(infoi["count"] for infoi in info)
This can be more efficient, since you will not construct a list first with all the values: sum will enumerate over all items and thus will result in constant memory usage.

Counting number of list entries that occur 1 time

I'm trying to write a Python function that counts the number of entries in a list that occur exactly once.
For example, given the list [17], this function would return 1. Or given [3,3,-22,1,-22,1,3,0], it would return 1.
** Restriction: I cannot import anything into my program.
The incorrect code that I've written so far: I'm going the double-loop route, but the index math is getting over-complicated.
def count_unique(x):
if len(x) == 1:
return 1
i = 0
j = 1
for i in range(len(x)):
for j in range(j,len(x)):
if x[i] == x[j]:
del x[j]
j+1
j = 0
return len(x)
Since you can't use collections.Counter or sorted/itertools.groupby apparently (one of which would usually be my go to solution, depending on whether the inputs are hashable or sortable), just simulate roughly the same behavior as a Counter, counting all elements and then counting the number of elements that appeared only once at the end:
def count_unique(x):
if len(x) <= 1:
return len(x)
counts = {}
for val in x:
counts[val] = counts.get(val, 0) + 1
return sum(1 for count in counts.values() if count == 1)
lst = [3,3,-22,1,-22,1,3,0]
len(filter(lambda z : z[0] == 1,
map(lambda x : (len(filter(lambda y : y == x, lst)), x), lst)))
sorry :)
Your solution doesn't work because you are doing something weird. Deleting things from a list while iterating through it, j+1 makes no sense etc. Try adding elements that are found to be unique to a new list and then counting the number of things in it. Then figure out what my solution does.
Here is the O(n) solution btw:
lst = [3,3,-22,1,-22,1,3,0,37]
cnts = {}
for n in lst:
if n in cnts:
cnts[n] = cnts[n] + 1
else:
cnts[n] = 1
count = 0
for k, v in cnts.iteritems():
if v == 1:
count += 1
print count
A more simple and understandable solution:
l = [3, 3, -22, 1, -22, 1, 3, 0]
counter = 0
for el in l:
if l.count(el) == 1:
counter += 1
It's pretty simple. You iterate over the items of the list. Then you look if the element is exactly one time in the list and then you add +1. You can improve the code (make liste comprehensions, use lambda expressions and so on), but this is the idea behind it all and the most understandable, imo.
you are making this overly complicated. try using a dictionary where the key is the element in your list. that way if it exists it will be unique
to add to this. it is probably the best method when looking at complexity. an in lookup on a dictionary is considered O(1), the for loop is O(n) so total your time complexity is O(n) which is desirable... using count() on a list element does a search on the whole list for every element which is basically O(n^2)... thats bad
from collections import defaultdict
count_hash_table = defaultdict(int) # i am making a regular dictionary but its data type is an integer
elements = [3,3,-22,1,-22,1,3,0]
for element in elements:
count_hash_table[element] += 1 # here i am using that default datatype to count + 1 for each type
print sum(c for c in count_hash_table.values() if c == 1):
There is method on lists called count.... from this you can go further i guess.
for example:
for el in l:
if l.count(el) > 1:
continue
else:
print("found {0}".format(el))

Categories

Resources