Find 3 maximum elements of list with python - python

How can I search for the three maximum elements of a list and replace it at same index with its result when divided by 2.
Please what am I doing wrong:
input is: 2 5 8 19 1 15 7 20 11
output should be : 2 5 8 9.5 1 7.5 7 10 11
Index out of range is the result displayed
def numberInput(line):
lineInput = [int(i) for i in line.split()]
min1 = min(lineInput)
for j in range(len(lineInput)):
if lineInput[j]>min1 and lineInput[j] > lineInput[j+1]:
max1 = lineInput[j]/float(2)
else:
max1 = lineInput[j]/float(2)
lineInput[j] = max1
lineInput[j] = max1
return(lineInput)
number = '2 5 8 19 1 15 7 20 11'
print(numberInput(number))

If the order of list isn't important, you can simply sort the list in descending order and then replace the first 3 elements as
a = [2, 5, 8, 19, 1, 15, 7, 20, 11]
a.sort(reverse = True)
a[0] = a[0] / 2
a[1] = a[1] / 2
a[2] = a[2] / 2
Output
[10.0, 9.5, 7.5, 11, 8, 7, 5, 2, 1]
If the order is important,
import heapq
largest = heapq.nlargest(3, a)
for i in range(len(a)):
if a[i] in largest:
a[i] = a[i] / 2
Output
[2, 5, 8, 9.5, 1, 7.5, 7, 10.0, 11]
heapq.nlargest() is a function of heapq which can give you n largest numbers from a list. Since lists in Python do not have a replace function, the list had to be traversed once, and then replaced manually. Hope this resolves your issue.

Why wouldn't you just walk through the list once (it maybe a bit longer code, but definitely efficient)
def numberInput(line):
lineInput = [int(i) for i in line.split()]
n = len(lineInput)
if n <= 3:
return [i / 2 for i in lineInput]
max_pairs = [(lineInput[i], i) for i in range(3)] # keep track of 3 max's and their indices
max_pairs.sort(key = lambda x: -x) # sort in descending order
for i in range(3, n):
if lineInput[i] >= max_pairs[0][0]: # greater than the largest element
max_pairs = [(lineInput[i], i)] + max_pairs[:2]
elif lineInput[i] >= max_pairs[1][0]: # greater than second element
max_pairs = [max_pairs[0], (lineInput[i], i), max_pairs[1]]
elif lineInput[i] >= max_pairs[2][0]: # greater than third element
max_pairs = max_pairs[:2] + [(lineInput[i], i)]
for pair in max_pairs:
lineInput[pair[1]] = lineInput[pair[0]] / 2
return lineInput
Explanation: max_pairs is a set of three tuples, containing the maximum three elements and their indices
Note: I think the above is easiest to understand, but you can do it in a loop if you don't like all those ifs

Related

Iterative summation

I'm trying to write a python code that allows me to iteratively sum up the average values of three elements of a list, starting with the third element and its two predecessors. Let me give you an example:
list = [1, 2, 3, 4, 5, 6, 7]
I want to calculate the following:
sum_of_average_values = sum(1, 2, 3)/3 + sum(2, 3, 4)/3 + sum(3, 4, 5)/3 + sum(4, 5, 6)/3 + sum(5, 6, 7)/3
Since I'm quite new to programming I couldn't find an effective way of putting this into a function.
You can do in this way:
a = [1,2,3,4,5,6,7]
sum_of_average_values = 0
for i in range(0,len(a)-2):
sum_of_average_values += sum(a[i:i+2])/3
print(sum_of_average_values)
You could use rolling from pandas.
import pandas as pd
num_list = [1, 2, 3, 4, 5, 6, 7]
average_sum = sum(pd.Series(num_list).rolling(3).mean().dropna())
print(average_sum)
Many ways to achieve this, one way is recursively.
The function averages the last three elements of a list and adds the result to the result generated by the function with a list lacking the last element. Continues like this until the list is shorter than 3.
def fn(l):
if len(l) < 3:
return 0
return sum(l[-3:])/3 + fn(l[:-1])
print(fn([1, 2, 3, 4, 5, 6, 7]))
Another solution where you can specify the amount of elements you want to sum up and average:
l = [1, 2, 3, 4, 5, 6, 7]
def sum_avg(l, n):
res = 0
for i in range(n-1, len(l)):
res += sum([l[j] for j in range(i, i-n, -1)])/n
return res
print(sum_avg(l, 3))
--> 20.0
Mathematically, this would could be obtain by averaging the sums of 3 sublists:
L = [1, 2, 3, 4, 5, 6, 7]
r = (sum(L) + sum(L[1:-1]) + sum(L[2:-2]))/3 # 20.0
and can be generalized to a window size of w:
w = 3
r = sum(sum(L[p:-p or None]) for p in range(w)) / w
It can also be implemented without the overhead of generating sublists by using item positions to determine the number of times they are added to the total:
r = sum(n*min(i+1,len(L)-i,w) for i,n in enumerate(L)) / w
This would be the most memory-efficient of the 3 methods because it use an iterator to feed data to the sum function and only goes through the data once.
Detailed explanation:
Since all the averages that are added together are a division by 3, we can produce the total sum and divide by 3 at the end
the number at the first and last positions are added once
the number at the second and penultimate positions are added twice
The numbers from the third position up to the antepenultimate will be added 3 times
visually:
(1 + 2 + 3) / 3
(2 + 3 + 4) / 3
(3 + 4 + 5) / 3
(4 + 5 + 6) / 3
(5 + 6 + 7) / 3
(1x1 + 2x2 + 3x3 + 4x3 + 5x3 + 6x2 + 7x1) / 3 = 20.0
n = 1 2 3 4 5 6 7 # value
* = 1 2 3 3 3 2 1 # multiplier (times added)
-------------------------
(2, 4, 9, 12, 15, 12, 7) / 3 = 20.0
i = 0 1 2 3 4 5 6 # index
1 2 3 3 3 2 1 # min(i+1,len(L)-i,w) = multiplier
You can do in one line using list comprehension as:
n = 3
avg = sum( [ sum(lst[i:i+n])/n for i in range(0, len(lst) - (n - 1)) ] )
print(avg) # 20.0

Sum digits of each integer in a list

I was trying to sum the digits for every element in the list and print the sum of every element at once but my code below only gives me 6 6 6. My desired output is 6 1 2.
#pythonCode#
my_list = [15, 10, 20]
sum = 0
m = ""
for i in range(0, 3):
while m != 0:
rem= my_list[i] % 10
m = my_list[i] //10
my_list[i] = m
sum = sum + rem
print(sum)
You could do this using map to apply a lambda function - if I understand the desired output correctly:
>>> my_list = [15, 10, 20]
>>> list(map(lambda x: sum(int(s) for s in str(x)), my_list))
[6, 1, 2]
Written out in full, this is roughly equivalent to:
my_list = [15, 10, 20]
for integer in my_list:
total = 0
for digit in str(integer):
total += int(digit)
print(f"The sum of {integer} is {total}")
Output:
The sum of 15 is 6
The sum of 10 is 1
The sum of 20 is 2

Remove smallest value from array after append to New array

I am running a selection sort function in Python that works with numpy arrays instead of lists (so I can't use .pop for this, I don't think).
The function is:
def selectionSort(arr):
newArr = []
for i in range(len(arr)):
smallest = findSmallest(arr)
newArr.append((smallest))
arr = arr[(arr > smallest)]
return newArr
I want that "arr = arr[(arr > smallest)] which obviously doesn't work, to remove the smallest value (or, the value appended to newArr i.e the same value) from the passed array in the same way that .pop would do with a list.
I've tried things along these lines:
a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
index = [2, 3, 6]
new_a = np.delete(a, index)
But couldn't get it to work. At the end of the day, I need to get something in the format of:
arr = randint(0,10,20)
to return an array sorted in ascending order. All I can manage is returning the smallest values repeated.
Thanks for any help
Try
arr = arr[np.where(arr > smallest)]
You may try:
arr = arr[ arr != np.min(a)]
This way you'll take from arr all the elements except the smallest one and reassign them to arr.
Your algorithm is almost correct. Indeed, it works if there are no duplicate values in arr:
import numpy as np
def selectionSort(arr):
newArr = []
for i in range(len(arr)):
smallest = findSmallest(arr)
newArr.append((smallest))
arr = arr[(arr > smallest)]
return newArr
findSmallest = np.min
# no duplicate values
auniq = np.random.choice(np.arange(20), (10,), replace=False)
print(auniq)
print(selectionSort(auniq))
Sample run:
[ 0 1 7 4 10 14 13 16 9 12]
[0, 1, 4, 7, 9, 10, 12, 13, 14, 16]
If there are duplicates it will crash because upon removing a minimum with duplicates the duplicates will be removed as well and that throws off the logic of the loop.
# duplicate values
adupl = np.random.randint(0, 9, (10,))
print(adupl)
# next line would crash
#print(selectionSort(adupl))
One fix is to only remove one copy of duplicates. This can for example be done using argmin which returns the index of the/one minimum, not its value.
def selectionSort2(arr):
arr = np.array(arr)
sorted = np.empty_like(arr)
for i in range(len(sorted)):
j = arr.argmin()
sorted[i] = arr[j]
arr = np.delete(arr, j)
return sorted
print(selectionSort2(adupl))
This works but is terribly inefficient because np.delete is more or less O(n). It is cheaper to swap the minimum element with a boundary element and then cut that off:
def selectionSort3(arr):
arr = np.array(arr)
sorted = np.empty_like(arr)
for i in range(len(sorted)):
j = arr[i:].argmin()
sorted[i] = arr[i + j]
arr[i], arr[i + j] = arr[i + j], arr[i]
return sorted
print(selectionSort3(adupl))
Looking at selectionSort3 we can observe that the separate output sorted is not actually needed, because arr has been sorted inplace already:
def selectionSort4(arr):
arr = np.array(arr)
for i in range(len(arr)):
j = arr[i:].argmin()
arr[i], arr[i + j] = arr[i + j], arr[i]
return arr
print(selectionSort4(adupl))
Sample output (adupl and output of selectionSort2-4):
[0 4 3 8 8 4 5 0 4 2]
[0 0 2 3 4 4 4 5 8 8]
[0 0 2 3 4 4 4 5 8 8]
[0 0 2 3 4 4 4 5 8 8]

Adding to a specific variable in a list

I'm trying to add two lists. If the last variable is greater than 10, it needs to carry over to the previous variable in the list. For example :
1 / 2 / 3 (List 1)
7 / 8 / 9 (List 2)
Should equal
9 / 1 / 2 not 8/10/12
So far, I have
list1 = [1, 2, 3]
list2 = [7, 8, 9]
SumOfLists = [x+y for x,y in zip(list1, list2)]
That adds the lists together, but I'm not sure how to make the number carry over.
You can try this code.
list1 = [1, 2, 3]
list2 = [7, 8, 9]
def add_list(a,b):
carry = 0
res_list = []
for i,j in zip(a[::-1],b[::-1]): # Iterate through the lists in reverse
val = (i+j+carry)%10 # Store the sum in val
carry = (i+j+carry)//10 # Store the carry
res_list.append(val) # Append to the returning list
return res_list[::-1] # Return the list
print add_list(list1,list2)
Wil print
[9, 1, 2]
Algorithm
Loop through each of the values in reverse. Add each corresponding values. If the values are above 10 then find the exceeding value and put it to carry. Finally return the reverse of the list.
list1 = [1, 2, 3]
list2 = [7, 8, 9]
cur = 0 # num to carry over
result = []
for x,y in zip(reversed(list2),reversed(list1)):
if x + y + cur > 10: # if sum greater than 10, remember to add 1 on
t = x+y + cur # the next loop
d = str(t)[1] # get the rightmost digit
result.append(int(d))
cur = 1
else: # nothing to curry over, but still add cur,
# it may be 1
result.append(x+y+cur)
cur = 0
print(list(reversed(result)) )
[9, 1, 2]
just subtract 10 if it's more then 10 and add 1 to it's previous element. Do this proccess for all element in sum list
if SumOfLists[2] >= 10:
SumOfLists[2] -= 10
SumOfLists[1] += 1
And at last check
if SumOfLists[0] >= 10:
for i in range(len(SumOfLists)-1,0,-1):
SumOfLists[i] = SumOfLists[i-1]
SumOfLists[0] = 1

Find second maximum number in a list [duplicate]

This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 12 months ago.
First line contains N. Second line contains list of N integers each separated by a space. I need to find the second largest number in list.
My code:
N = int(raw_input())
L = map(int, raw_input().split())
for i in L:
if i == max(L):
L.remove(i)
print L
print max(L)
If input is [2, 6, 9, 9, 5], this still prints maximum value: 9, as only one 9 is getting removed from the list.
So, how to remove all the 1st maximum values in the list?
The reason that it's still returning 9 is because you're mutating the list as you iterate over it. Essentially the steps are:
1. 2 6 9 9 5
^idx0
2. 2 6 9 9 5
^idx1
3. 2 6 9 9 5
^idx2
3a. 2 6 9 5
^idx2
4. 2 6 9 5
^
See how it skips evaluating the second 9 when it deletes the first 9? You'll notice if your list is [2, 6, 9, 5, 9], it works appropriately.
The minimal change to your code that will make it function is to iterate over a copy of the list, rather than the list itself.
L = [2, 6, 9, 9, 5]
maxL = max(L) # gotta move this out!!
for i in L[:]: # the slice creates a copy
if i == maxL:
L.remove(i)
print(max(L))
However it's probably easier to make a set (ensuring uniqueness), sort it, and return the second-to-last entry.
second_max = sorted(set(L))[-2]
try
N = 5
L = map(int, "2 6 9 9 5".split())
maxL = max(L)
#list comprehension for remove all occurrences of max(L)
L_filter = [e for e in L if e!=maxL]
print L
#print max of L_filter, second maximum of L
print max(L_filter)
you get:
[2, 6, 9, 9, 5]
6
Remove duplicated elements by converting to a set:
values = set(L)
Then remove the maximum:
values.discard(max(values))
you could also do this
L = [2, 6, 9, 9, 5]
L_tuples = zip(L, range(len(L))) #need tuples to make a dict
L_map = dict(L_tuples) #use the dict to dedupe
L_uniq = L_map.keys() #get back deduped values
L_sorted = sorted(L_uniq) #sort them ascending
second_largest = L_sorted[-2] #second from last is second largest
#or, rolling all that up...
second_largest = sorted(dict(zip(L, range(len(L)))).keys())[-2]
>>> import heapq
>>> values = [2, 6, 9, 9, 5]
>>> heapq.heapify(values)
>>> heapq._heapify_max(values)
>>> value = top = heapq.heappop()
>>> while value == top:
... value = heapq.heappop()
>>> print value
Here is another way to calculate the second maximum in a list. The code also considers the scenario that includes duplicate elements in the list.
number_of_elements=int(input('The number of elements in list\n'))
a=[]
for i in range(number_of_elements):
a.append(int(input('enter the list elements')))
#Use built-in function to calculate the maximum
max_list=max(a)
print("The maximum element is ",max_list)
#Calculate the number of times the number occur
count_of_max_num=a.count(max_list)
b=a
if (count_of_max_num == 1):
b.remove(max_list)
second_max=max(b)
print("The second largest number is", second_max, "The new list is" ,b)
else:
for i in range(count_of_max_num):
b.remove(max_list)
print ("The second largest is" , max(b))

Categories

Resources