Find if valley exists in integer list - python

A list of integers is said to be a valley if it consists of a sequence of strictly decreasing values followed by a sequence of strictly increasing values. The decreasing and increasing sequences must be of length at least 2. The last value of the decreasing sequence is the first value of the increasing sequence.
Write a Python function valley(l) that takes a list of integers and returns True if l is a valley and False otherwise.
Here are some examples to show how your function should work.
>>> valley([3,2,1,2,3])
True
>>> valley([3,2,1])
False
>>> valley([3,3,2,1,2])
False
I have been sleepless for 2 days and the best i could write is this code
def valley(list):
first =False
second=False
midway=0
if(len(list)<2):
return False
else:
for i in range(0,len(list)):
if(list[i]<list[i+1]):
first=True
midway=i
break
for j in range(midway,len(list)-1):
if(list[j]<list[j+1] and j+1==len(list)):
Second=True
break
if(list[j]>=list[j+1]):
second=False
break
if(first==True and second==True):
return True
else:
return False

The solution i found that also works if the numbers are not in perfect sequence and it is not necessary that the lowest value must be equal to 1, what i'm trying to say is if the list is suppose [14,12,10,5,3,6,7,32,41], here also a valley is formed, as the values are decreasing up to 3(lowest) and then it's again increasing. List's such as [4,3,2,1,2,3,4] is a perfect valley.
Solution:
def valley(lst):
if len(lst)<2:
return False
else:
p = lst.index(min(lst))
for i in range (0,p):
x = (lst[i] > lst[i+1])
for q in range (p,len(lst)-1):
y = (lst[q]< lst[q+1])
return (x==y)
Don't forget to accept it if this solves the problem and is most helpful, thank you.

It seems saurav beat me to the punch, but if you'll allow for some NumPy magic:
import numpy as np
def valley(arr):
diff = arr[:-1] - arr[1:]
gt = np.where(diff > 0)[0]
lt = np.where(diff < 0)[0]
d = np.sum(diff == 0)
if gt.size == 0 or lt.size == 0:
# Doesn't have ascendings or decendings
return False
elif d > 0:
# Has a flat
return False
elif gt[-1] > lt[0]:
# Not strictly one descent into one ascent
return False
else:
return True
a = np.array([3, 2, 1, 2, 3])
b = np.array([3, 3, 2, 1, 2])
c = np.array([3, 2, 1])
d = np.array([1, 2, 3, 2, 1])
print(valley(a), valley(b), valley(c), valley(d))
>>> True False False False
You can also use plain old Python builtins to do it:
def valley(arr):
diff = [i1-i2 for i1, i2 in zip(arr, arr[1:])]
gt = [i for i, item in enumerate(diff) if item > 0]
lt = [i for i, item in enumerate(diff) if item < 0]
d = sum([True for d in diff if d == 0])
if len(gt) == 0 or len(lt) == 0:
# Doesn't have ascendings or decendings
return False
elif d > 0:
# Has a flat
return False
elif gt[-1] > lt[0]:
# Not strictly one descent into one ascent
return False
else:
return True
a = [3, 2, 1, 2, 3]
print(valley(a), ...)
>>> True False False False

Actually I did not want to send a complete solution but I just wanted to solve and for the first, and hopefully last, time I'm posting a solution for a task.
Here is my solution, of course there may be other solutions this is the first one my fingers typed.
def valley(heights):
directions = []
# Check input
if not heights:
return False
# Traverse array and compare current height with previous one
# if we are going down or up.
pre = heights[0]
for h in heights[1:]:
if h > pre:
# If we are going upward add 1
directions.append(1)
elif h < pre:
# If we are going downward add -1
directions.append(-1)
pre = h
# We have many -1s and 1s in out directions list.
# However, if it is a valley then it should first down and up
# which is [-1, 1]. Return the comparison result with [-1, 1]
return set(directions) == set([-1, 1])

The result of variable n in valley function, is the pairwise difference of the numbers in the input list, so if
input = [3,2,1,2,3]
n = [-1, -1, 1, 1]
Now the next variable h is, again pairwise difference of the n, so h will be
h = ['0', '2', '0']
So, every time you will have a valley, you just have to check the pattern "020". Use re module in python to do so,
import re
def valley(f):
n = [j-i for i, j in zip(f[:-1], f[1:])]
h = [str(j-i) for i, j in zip(n[:-1], n[1:])]
result = "".join(h)
m = re.search('020', result)
if m:
return True
else:
return False
Please let me know if its correct or not.

Related

Why is my code O(N^2) if there is only one for loop

I am trying to solve problem 4.1 on Codility.com. I need a solution which runs in O(N) time. My code solves the problem, but it runs in O(N^2) time, according to Codility's performance tests. Unfortunately I can't see why. There is only one for loop, which should scale linearly with n.
The challenge is to write a solution which tests whether an array ('A') contains all integers between 1 and X. It should return the index of the element which is the last element in 1 to X that appears in the array, or it should return -1 if not every integer between 1 and X is an element of the array. For example, the solution to X = 4 and A = [1, 3, 4, 2, 2] is 3 since 2 is the last element in 1 to 4 which appears in the array and it first appears in position 3. The solution to X = 5 and A = [1, 2, 4, 2, 3] is -1 because 5 never appears. My solution is below.
def Solution(X, A):
N = len(A)
count = [0] * (X + 1)
# Solution for single-element arrays
if N == 1:
if A[0] == 1:
return 0
elif A[0] != 1:
return - 1
# Solution for multi-element arrays
elif N != 1:
for i in range(0, N + 1, 1):
if count[A[i]] == 0:
count[A[i]] = count[A[i]] + 1
else:
pass
if count == [0] + [1] * (X):
return i
elif count != [0] + [1] * (X) and i == N - 1:
return -1
Would anyone know why it runs in O(N^2) time? Codility's performance tests confirm this, but as far as I can see this should run in O(kN) time since there is only one for loop. Any help is appreciated.
Something like this would work in O(n), you would have to adjust it to the exact question given:
def solution(x, a):
b = []
for v in a:
# the conditions
if (type(v) == int) and (v < x) and (v>1): b.append(v)
else: return -1
return b
# some examples
# this returns -1
a = [1,2,3,4,5.5,6]
x = solution(5,a)
print(x)
# this returns the the list [2, 3, 4]
a = [2,3,4]
x = solution(5,a)
print(x)
the reason why is because you can exit the list at the first fail with the return -1 statement.
This should be O(N): a single instantiation of a list of length X, one for-loop over A which does O(1) retrieval of single list items, and one single final search of the list for None values.
def solution(X, A):
pos = [None] * X
for i, n in enumerate(A):
if 1 <= n <= X:
if pos[n-1] is None:
pos[n-1] = i
return pos[n-1] if None not in pos else -1
print(solution(4, [1, 3, 4, 2, 2]))
print(solution(5, [1, 2, 4, 2, 3]))
print(solution(1000, reversed(range(1, 1001))))
prints:
3
-1
999

Python code for printing out the second largest number number given a list [duplicate]

I'm learning Python and the simple ways to handle lists is presented as an advantage. Sometimes it is, but look at this:
>>> numbers = [20,67,3,2.6,7,74,2.8,90.8,52.8,4,3,2,5,7]
>>> numbers.remove(max(numbers))
>>> max(numbers)
74
A very easy, quick way of obtaining the second largest number from a list. Except that the easy list processing helps write a program that runs through the list twice over, to find the largest and then the 2nd largest. It's also destructive - I need two copies of the data if I wanted to keep the original. We need:
>>> numbers = [20,67,3,2.6,7,74,2.8,90.8,52.8,4,3,2,5,7]
>>> if numbers[0]>numbers[1]):
... m, m2 = numbers[0], numbers[1]
... else:
... m, m2 = numbers[1], numbers[0]
...
>>> for x in numbers[2:]:
... if x>m2:
... if x>m:
... m2, m = m, x
... else:
... m2 = x
...
>>> m2
74
Which runs through the list just once, but isn't terse and clear like the previous solution.
So: is there a way, in cases like this, to have both? The clarity of the first version, but the single run through of the second?
You could use the heapq module:
>>> el = [20,67,3,2.6,7,74,2.8,90.8,52.8,4,3,2,5,7]
>>> import heapq
>>> heapq.nlargest(2, el)
[90.8, 74]
And go from there...
Since #OscarLopez and I have different opinions on what the second largest means, I'll post the code according to my interpretation and in line with the first algorithm provided by the questioner.
def second_largest(numbers):
count = 0
m1 = m2 = float('-inf')
for x in numbers:
count += 1
if x > m2:
if x >= m1:
m1, m2 = x, m1
else:
m2 = x
return m2 if count >= 2 else None
(Note: Negative infinity is used here instead of None since None has different sorting behavior in Python 2 and 3 – see Python - Find second smallest number; a check for the number of elements in numbers makes sure that negative infinity won't be returned when the actual answer is undefined.)
If the maximum occurs multiple times, it may be the second largest as well. Another thing about this approach is that it works correctly if there are less than two elements; then there is no second largest.
Running the same tests:
second_largest([20,67,3,2.6,7,74,2.8,90.8,52.8,4,3,2,5,7])
=> 74
second_largest([1,1,1,1,1,2])
=> 1
second_largest([2,2,2,2,2,1])
=> 2
second_largest([10,7,10])
=> 10
second_largest([1,1,1,1,1,1])
=> 1
second_largest([1])
=> None
second_largest([])
=> None
Update
I restructured the conditionals to drastically improve performance; almost by a 100% in my testing on random numbers. The reason for this is that in the original version, the elif was always evaluated in the likely event that the next number is not the largest in the list. In other words, for practically every number in the list, two comparisons were made, whereas one comparison mostly suffices – if the number is not larger than the second largest, it's not larger than the largest either.
You could always use sorted
>>> sorted(numbers)[-2]
74
Try the solution below, it's O(n) and it will store and return the second greatest number in the second variable. UPDATE: I've adjusted the code to work with Python 3, because now arithmetic comparisons against None are invalid.
Notice that if all elements in numbers are equal, or if numbers is empty or if it contains a single element, the variable second will end up with a value of None - this is correct, as in those cases there isn't a "second greatest" element.
Beware: this finds the "second maximum" value, if there's more than one value that is "first maximum", they will all be treated as the same maximum - in my definition, in a list such as this: [10, 7, 10] the correct answer is 7.
def second_largest(numbers):
minimum = float('-inf')
first, second = minimum, minimum
for n in numbers:
if n > first:
first, second = n, first
elif first > n > second:
second = n
return second if second != minimum else None
Here are some tests:
second_largest([20, 67, 3, 2.6, 7, 74, 2.8, 90.8, 52.8, 4, 3, 2, 5, 7])
=> 74
second_largest([1, 1, 1, 1, 1, 2])
=> 1
second_largest([2, 2, 2, 2, 2, 1])
=> 1
second_largest([10, 7, 10])
=> 7
second_largest( [1, 3, 10, 16])
=> 10
second_largest([1, 1, 1, 1, 1, 1])
=> None
second_largest([1])
=> None
second_largest([])
=> None
Why to complicate the scenario? Its very simple and straight forward
Convert list to set - removes duplicates
Convert set to list again - which gives list in ascending order
Here is a code
mlist = [2, 3, 6, 6, 5]
mlist = list(set(mlist))
print mlist[-2]
You can find the 2nd largest by any of the following ways:
Option 1:
numbers = set(numbers)
numbers.remove(max(numbers))
max(numbers)
Option 2:
sorted(set(numbers))[-2]
The quickselect algorithm, O(n) cousin to quicksort, will do what you want. Quickselect has average performance O(n). Worst case performance is O(n^2) just like quicksort but that's rare, and modifications to quickselect reduce the worst case performance to O(n).
The idea of quickselect is to use the same pivot, lower, higher idea of quicksort, but to then ignore the lower part and to further order just the higher part.
This is one of the Simple Way
def find_second_largest(arr):
first, second = 0, 0
for number in arr:
if number > first:
second = first
first = number
elif number > second and number < first:
second = number
return second
If you do not mind using numpy (import numpy as np):
np.partition(numbers, -2)[-2]
gives you the 2nd largest element of the list with a guaranteed worst-case O(n) running time.
The partition(a, kth) methods returns an array where the kth element is the same it would be in a sorted array, all elements before are smaller, and all behind are larger.
there are some good answers here for type([]), in case someone needed the same thing on a type({}) here it is,
def secondLargest(D):
def second_largest(L):
if(len(L)<2):
raise Exception("Second_Of_One")
KFL=None #KeyForLargest
KFS=None #KeyForSecondLargest
n = 0
for k in L:
if(KFL == None or k>=L[KFL]):
KFS = KFL
KFL = n
elif(KFS == None or k>=L[KFS]):
KFS = n
n+=1
return (KFS)
KFL=None #KeyForLargest
KFS=None #KeyForSecondLargest
if(len(D)<2):
raise Exception("Second_Of_One")
if(type(D)!=type({})):
if(type(D)==type([])):
return(second_largest(D))
else:
raise Exception("TypeError")
else:
for k in D:
if(KFL == None or D[k]>=D[KFL]):
KFS = KFL
KFL = k
elif(KFS == None or D[k] >= D[KFS]):
KFS = k
return(KFS)
a = {'one':1 , 'two': 2 , 'thirty':30}
b = [30,1,2]
print(a[secondLargest(a)])
print(b[secondLargest(b)])
Just for fun I tried to make it user friendly xD
>>> l = [19, 1, 2, 3, 4, 20, 20]
>>> sorted(set(l))[-2]
19
O(n): Time Complexity of a loop is considered as O(n) if the loop variables is incremented / decremented by a constant amount. For example following functions have O(n) time complexity.
// Here c is a positive integer constant
for (int i = 1; i <= n; i += c) {
// some O(1) expressions
}
To find the second largest number i used the below method to find the largest number first and then search the list if thats in there or not
x = [1,2,3]
A = list(map(int, x))
y = max(A)
k1 = list()
for values in range(len(A)):
if y !=A[values]:
k.append(A[values])
z = max(k1)
print z
Objective: To find the second largest number from input.
Input : 5
2 3 6 6 5
Output: 5
*n = int(raw_input())
arr = map(int, raw_input().split())
print sorted(list(set(arr)))[-2]*
def SecondLargest(x):
largest = max(x[0],x[1])
largest2 = min(x[0],x[1])
for item in x:
if item > largest:
largest2 = largest
largest = item
elif largest2 < item and item < largest:
largest2 = item
return largest2
SecondLargest([20,67,3,2.6,7,74,2.8,90.8,52.8,4,3,2,5,7])
list_nums = [1, 2, 6, 6, 5]
minimum = float('-inf')
max, min = minimum, minimum
for num in list_nums:
if num > max:
max, min = num, max
elif max > num > min:
min = num
print(min if min != minimum else None)
Output
5
Initialize with -inf. This code generalizes for all cases to find the second largest element.
max1= float("-inf")
max2=max1
for item in arr:
if max1<item:
max2,max1=max1,item
elif item>max2 and item!=max1:
max2=item
print(max2)
Using reduce from functools should be a linear-time functional-style alternative:
from functools import reduce
def update_largest_two(largest_two, x):
m1, m2 = largest_two
return (m1, m2) if m2 >= x else (m1, x) if m1 >= x else (x, m1)
def second_largest(numbers):
if len(numbers) < 2:
return None
largest_two = sorted(numbers[:2], reverse=True)
rest = numbers[2:]
m1, m2 = reduce(update_largest_two, rest, largest_two)
return m2
... or in a very concise style:
from functools import reduce
def second_largest(n):
update_largest_two = lambda a, x: a if a[1] >= x else (a[0], x) if a[0] >= x else (x, a[0])
return None if len(n) < 2 else (reduce(update_largest_two, n[2:], sorted(n[:2], reverse=True)))[1]
This can be done in [N + log(N) - 2] time, which is slightly better than the loose upper bound of 2N (which can be thought of O(N) too).
The trick is to use binary recursive calls and "tennis tournament" algorithm. The winner (the largest number) will emerge after all the 'matches' (takes N-1 time), but if we record the 'players' of all the matches, and among them, group all the players that the winner has beaten, the second largest number will be the largest number in this group, i.e. the 'losers' group.
The size of this 'losers' group is log(N), and again, we can revoke the binary recursive calls to find the largest among the losers, which will take [log(N) - 1] time. Actually, we can just linearly scan the losers group to get the answer too, the time budget is the same.
Below is a sample python code:
def largest(L):
global paris
if len(L) == 1:
return L[0]
else:
left = largest(L[:len(L)//2])
right = largest(L[len(L)//2:])
pairs.append((left, right))
return max(left, right)
def second_largest(L):
global pairs
biggest = largest(L)
second_L = [min(item) for item in pairs if biggest in item]
return biggest, largest(second_L)
if __name__ == "__main__":
pairs = []
# test array
L = [2,-2,10,5,4,3,1,2,90,-98,53,45,23,56,432]
if len(L) == 0:
first, second = None, None
elif len(L) == 1:
first, second = L[0], None
else:
first, second = second_largest(L)
print('The largest number is: ' + str(first))
print('The 2nd largest number is: ' + str(second))
You can also try this:
>>> list=[20, 20, 19, 4, 3, 2, 1,100,200,100]
>>> sorted(set(list), key=int, reverse=True)[1]
100
A simple way :
n=int(input())
arr = set(map(int, input().split()))
arr.remove(max(arr))
print (max(arr))
use defalut sort() method to get second largest number in the list.
sort is in built method you do not need to import module for this.
lis = [11,52,63,85,14]
lis.sort()
print(lis[len(lis)-2])
Just to make the accepted answer more general, the following is the extension to get the kth largest value:
def kth_largest(numbers, k):
largest_ladder = [float('-inf')] * k
count = 0
for x in numbers:
count += 1
ladder_pos = 1
for v in largest_ladder:
if x > v:
ladder_pos += 1
else:
break
if ladder_pos > 1:
largest_ladder = largest_ladder[1:ladder_pos] + [x] + largest_ladder[ladder_pos:]
return largest_ladder[0] if count >= k else None
def secondlarget(passinput):
passinputMax = max(passinput) #find the maximum element from the array
newpassinput = [i for i in passinput if i != passinputMax] #Find the second largest element in the array
#print (newpassinput)
if len(newpassinput) > 0:
return max(newpassinput) #return the second largest
return 0
if __name__ == '__main__':
n = int(input().strip()) # lets say 5
passinput = list(map(int, input().rstrip().split())) # 1 2 2 3 3
result = secondlarget(passinput) #2
print (result) #2
if __name__ == '__main__':
n = int(input())
arr = list(map(float, input().split()))
high = max(arr)
secondhigh = min(arr)
for x in arr:
if x < high and x > secondhigh:
secondhigh = x
print(secondhigh)
The above code is when we are setting the elements value in the list
as per user requirements. And below code is as per the question asked
#list
numbers = [20, 67, 3 ,2.6, 7, 74, 2.8, 90.8, 52.8, 4, 3, 2, 5, 7]
#find the highest element in the list
high = max(numbers)
#find the lowest element in the list
secondhigh = min(numbers)
for x in numbers:
'''
find the second highest element in the list,
it works even when there are duplicates highest element in the list.
It runs through the entire list finding the next lowest element
which is less then highest element but greater than lowest element in
the list set initially. And assign that value to secondhigh variable, so
now this variable will have next lowest element in the list. And by end
of loop it will have the second highest element in the list
'''
if (x<high and x>secondhigh):
secondhigh=x
print(secondhigh)
Max out the value by comparing each one to the max_item. In the first if, every time the value of max_item changes it gives its previous value to second_max. To tightly couple the two second if ensures the boundary
def secondmax(self, list):
max_item = list[0]
second_max = list[1]
for item in list:
if item > max_item:
second_max = max_item
max_item = item
if max_item < second_max:
max_item = second_max
return second_max
you have to compare in between new values, that's the trick, think always in the previous (the 2nd largest) should be between the max and the previous max before, that's the one!!!!
def secondLargest(lista):
max_number = 0
prev_number = 0
for i in range(0, len(lista)):
if lista[i] > max_number:
prev_number = max_number
max_number = lista[i]
elif lista[i] > prev_number and lista[i] < max_number:
prev_number = lista[i]
return prev_number
Most of previous answers are correct but here is another way !
Our strategy is to create a loop with two variables first_highest and second_highest. We loop through the numbers and if our current_value is greater than the first_highest then we set second_highest to be the same as first_highest and then the second_highest to be the current number. If our current number is greater than second_highest then we set second_highest to the same as current number
#!/usr/bin/env python3
import sys
def find_second_highest(numbers):
min_integer = -sys.maxsize -1
first_highest= second_highest = min_integer
for current_number in numbers:
if current_number == first_highest and min_integer != second_highest:
first_highest=current_number
elif current_number > first_highest:
second_highest = first_highest
first_highest = current_number
elif current_number > second_highest:
second_highest = current_number
return second_highest
print(find_second_highest([80,90,100]))
print(find_second_highest([80,80]))
print(find_second_highest([2,3,6,6,5]))
Best solution that my friend Dhanush Kumar came up with:
def second_max(loop):
glo_max = loop[0]
sec_max = float("-inf")
for i in loop:
if i > glo_max:
sec_max = glo_max
glo_max=i
elif sec_max < i < glo_max:
sec_max = i
return sec_max
#print(second_max([-1,-3,-4,-5,-7]))
assert second_max([-1,-3,-4,-5,-7])==-3
assert second_max([5,3,5,1,2]) == 3
assert second_max([1,2,3,4,5,7]) ==5
assert second_max([-3,1,2,5,-2,3,4]) == 4
assert second_max([-3,-2,5,-1,0]) == 0
assert second_max([0,0,0,1,0]) == 0
Below code will find the max and the second max numbers without the use of max function. I assume that the input will be numeric and the numbers are separated by single space.
myList = input().split()
myList = list(map(eval,myList))
m1 = myList[0]
m2 = myList[0]
for x in myList:
if x > m1:
m2 = m1
m1 = x
elif x > m2:
m2 = x
print ('Max Number: ',m1)
print ('2nd Max Number: ',m2)
Here I tried to come up with an answer.
2nd(Second) maximum element in a list using single loop and without using any inbuilt function.
def secondLargest(lst):
mx = 0
num = 0
sec = 0
for i in lst:
if i > mx:
sec = mx
mx = i
else:
if i > num and num >= sec:
sec = i
num = i
return sec

Why doesn't this code give the right results for numeric comparisons?

I am currently doing Udemy course for Python by Jose Portilla. I am still a complete beginner. The excercise is the following:
"Given a list of ints, return True if the array contains a 3 next to a 3 somewhere."
has_33([1, 3, 3]) → True
has_33([1, 3, 1, 3]) → False
has_33([3, 1, 3]) → False
My code is the following. For my logic it should give me the above results, but it gives False, False, True, which is completely off:
def has_33(nums):
for n in nums:
a = nums.index(n)
x = nums[a + 1]
y = nums[a - 1]
if n == 3 and (x == n or y == n):
return True
else:
return False
I try to explain the working of your function for has_33([1, 3, 3]). This way it gets obvious why your function is not doing what you expect it to do.
def has_33(nums):
# Call function and nums = [1, 3, 3]
for n in nums:
# Iterate over nums. n = 1
a = nums.index(n)
# Get the index of the list a where element = 1; a = 0
x = nums[a + 1]
# 0 + 1 = 1 -> x = nums[1] = 3
y = nums[a - 1]
# 0 - 1 = -1 -> y = nums[-1] = 3 (index -1 points to the last element of the list)
if n == 3 and (x == n or y == n):
# False, since n != 3 (even though x = 3 and y = 3)
return True
else:
return False
# When returning, the functin stopts executing, hence n never reaches another element.
As pointed out in the comments, the main reason why this doesn't work is that you return false on the first iteration. Concerning the implementation, you can simplify things a bit to understand a bit more clearly what's going on:
def has_33(nums):
for a in range(len(nums)-1):
x = nums[a]
y = nums[a + 1]
if x == 3 and y == 3:
return True
return False
In your original solution, you were iterating over items by group of 3, adding print(y, n, x) right before your test would have output the following (for a call to has_33([1, 2, 3, 3, 4])):
item at: i-1 i i+1
---------------
i = 0 4 1 2
i = 1 1 2 3
i = 2 2 3 3
In the first line, 4 1 2, the 4 is the item at position 0-1 = -1, in python, negative indexes correspond to positions relative to the end of the list. For example, nums[-1] is the last item from nums and nums[-2] is the one before the last, etc.
In this second code we just iterate through nums's indexes to get every x item together with the following item y, printing print(x, y) would give:
item at: i i+1
---------
i = 0 1 2
i = 1 2 3
i = 2 3 3
Remark that, in the end we just used the indexes to get items, when the index is not used for anything else, you can often use the zip function instead:
def has_33(nums):
for x, y in zip(nums, nums[1:]):
if x == 3 and y == x:
return True
return False
has_33([1, 2, 3, 3, 1])
This outputs True, and for the record:
>>> nums
[1, 2, 3, 3, 1]
>>> nums[1:]
[2, 3, 3, 1]
>>> list(zip(nums, nums[1:]))
[(1, 2), (2, 3), (3, 3), (3, 1)]
The zip function will pair item at index i from the first list with item at index i from the second list. Here we just removed the first item from nums to form our second list, using this strategy we managed to pair item i with item i+1.
As mentioned in the comments, you can even use the function any that does exactly what our loop does
def has_33(nums):
return any(p == (3, 3) for p in zip(nums, nums[1:]))
There is a logic error in your code at line 3. You can't use:
a = nums.index(n)
To get the index but you need to use this way:
for a, n in enumerate(nums):
Finally there is your corrected code:
def has_33(nums):
for a, n in enumerate(nums):
x = nums[a + 1]
y = nums[a - 1]
if n == 3 and (x == n or y == n):
return True
else:
return False
WHY? nums.index(n) return the first position of n not the current position.
So, you can try this.
def has_33(numbers):
str_numbers = ''.join(map(str, numbers))
return '33' in str_numbers
The function accepts as an argument a list, the variable str_numbers contains the list converted to a string by the map function and join method.
For example, if my list is
my_numbers = [1,3,1,3]
The variable stores it as
'1313'
Last but not least, return an evaluation of an expression to see if '33' is in the new created variable, which returns True or False.
Run this
my_numbers = [1, 1, 3, 1, 3, 3]
def has_33(numbers):
str_numbers = ''.join(map(str, numbers))
return '33' in str_numbers
result = has_33(my_numbers)
print(result)

Python given an array A of N integers, returns the smallest positive integer (greater than 0) that does not occur in A in O(n) time complexity

For example:
input: A = [ 6 4 3 -5 0 2 -7 1 ]
output: 5
Since 5 is the smallest positive integer that does not occur in the array.
I have written two solutions to that problem. The first one is good but I don't want to use any external libraries + its O(n)*log(n) complexity. The second solution "In which I need your help to optimize it" gives an error when the input is chaotic sequences length=10005 (with minus).
Solution 1:
from itertools import count, filterfalse
def minpositive(a):
return(next(filterfalse(set(a).__contains__, count(1))))
Solution 2:
def minpositive(a):
count = 0
b = list(set([i for i in a if i>0]))
if min(b, default = 0) > 1 or min(b, default = 0) == 0 :
min_val = 1
else:
min_val = min([b[i-1]+1 for i, x in enumerate(b) if x - b[i - 1] >1], default=b[-1]+1)
return min_val
Note: This was a demo test in codility, solution 1 got 100% and
solution 2 got 77 %.
Error in "solution2" was due to:
Performance tests ->
medium chaotic sequences length=10005 (with minus) got 3 expected
10000
Performance tests -> large chaotic + many -1, 1, 2, 3 (with
minus) got 5 expected 10000
Testing for the presence of a number in a set is fast in Python so you could try something like this:
def minpositive(a):
A = set(a)
ans = 1
while ans in A:
ans += 1
return ans
Fast for large arrays.
def minpositive(arr):
if 1 not in arr: # protection from error if ( max(arr) < 0 )
return 1
else:
maxArr = max(arr) # find max element in 'arr'
c1 = set(range(2, maxArr+2)) # create array from 2 to max
c2 = c1 - set(arr) # find all positive elements outside the array
return min(c2)
I have an easy solution. No need to sort.
def solution(A):
s = set(A)
m = max(A) + 2
for N in range(1, m):
if N not in s:
return N
return 1
Note: It is 100% total score (Correctness & Performance)
def minpositive(A):
"""Given an list A of N integers,
returns the smallest positive integer (greater than 0)
that does not occur in A in O(n) time complexity
Args:
A: list of integers
Returns:
integer: smallest positive integer
e.g:
A = [1,2,3]
smallest_positive_int = 4
"""
len_nrs_list = len(A)
N = set(range(1, len_nrs_list+2))
return min(N-set(A)) #gets the min value using the N integers
This solution passes the performance test with a score of 100%
def solution(A):
n = sorted(i for i in set(A) if i > 0) # Remove duplicates and negative numbers
if not n:
return 1
ln = len(n)
for i in range(1, ln + 1):
if i != n[i - 1]:
return i
return ln + 1
def solution(A):
B = set(sorted(A))
m = 1
for x in B:
if x == m:
m+=1
return m
Continuing on from Niroj Shrestha and najeeb-jebreel, added an initial portion to avoid iteration in case of a complete set. Especially important if the array is very large.
def smallest_positive_int(A):
sorted_A = sorted(A)
last_in_sorted_A = sorted_A[-1]
#check if straight continuous list
if len(sorted_A) == last_in_sorted_A:
return last_in_sorted_A + 1
else:
#incomplete list, iterate to find the smallest missing number
sol=1
for x in sorted_A:
if x == sol:
sol += 1
else:
break
return sol
A = [1,2,7,4,5,6]
print(smallest_positive_int(A))
This question doesn't really need another answer, but there is a solution that has not been proposed yet, that I believe to be faster than what's been presented so far.
As others have pointed out, we know the answer lies in the range [1, len(A)+1], inclusively. We can turn that into a set and take the minimum element in the set difference with A. That's a good O(N) solution since set operations are O(1).
However, we don't need to use a Python set to store [1, len(A)+1], because we're starting with a dense set. We can use an array instead, which will replace set hashing by list indexing and give us another O(N) solution with a lower constant.
def minpositive(a):
# the "set" of possible answer - values_found[i-1] will tell us whether i is in a
values_found = [False] * (len(a)+1)
# note any values in a in the range [1, len(a)+1] as found
for i in a:
if i > 0 and i <= len(a)+1:
values_found[i-1] = True
# extract the smallest value not found
for i, found in enumerate(values_found):
if not found:
return i+1
We know the final for loop always finds a value that was not marked, because it has one more element than a, so at least one of its cells was not set to True.
def check_min(a):
x= max(a)
if x-1 in a:
return x+1
elif x <= 0:
return 1
else:
return x-1
Correct me if i'm wrong but this works for me.
def solution(A):
clone = 1
A.sort()
for itr in range(max(A) + 2):
if itr not in A and itr >= 1:
clone = itr
break
return clone
print(solution([2,1,4,7]))
#returns 3
def solution(A):
n = 1
for i in A:
if n in A:
n = n+1
else:
return n
return n
def not_in_A(a):
a=sorted(a)
if max(a)<1:
return(1)
for i in range(0,len(a)-1):
if a[i+1]-a[i]>1:
out=a[i]+1
if out==0 or out<1:
continue
return(out)
return(max(a)+1)
mark and then find the first one that didn't find
nums = [ 6, 4, 3, -5, 0, 2, -7, 1 ]
def check_min(nums):
marks = [-1] * len(nums)
for idx, num in enumerate(nums):
if num >= 0:
marks[num] = idx
for idx, mark in enumerate(marks):
if mark == -1:
return idx
return idx + 1
I just modified the answer by #najeeb-jebreel and now the function gives an optimal solution.
def solution(A):
sorted_set = set(sorted(A))
sol = 1
for x in sorted_set:
if x == sol:
sol += 1
else:
break
return sol
I reduced the length of set before comparing
a=[1,222,3,4,24,5,6,7,8,9,10,15,2,3,3,11,-1]
#a=[1,2,3,6,3]
def sol(a_array):
a_set=set()
b_set=set()
cnt=1
for i in a_array:
#In order to get the greater performance
#Checking if element is greater than length+1
#then it can't be output( our result in solution)
if i<=len(a) and i >=1:
a_set.add(i) # Adding array element in set
b_set.add(cnt) # Adding iterator in set
cnt=cnt+1
b_set=b_set.difference(a_set)
if((len(b_set)) > 1):
return(min(b_set))
else:
return max(a_set)+1
sol(a)
def solution(A):
nw_A = sorted(set(A))
if all(i < 0 for i in nw_A):
return 1
else:
ans = 1
while ans in nw_A:
ans += 1
if ans not in nw_A:
return ans
For better performance if there is a possibility to import numpy package.
def solution(A):
import numpy as np
nw_A = np.unique(np.array(A))
if np.all((nw_A < 0)):
return 1
else:
ans = 1
while ans in nw_A:
ans += 1
if ans not in nw_A:
return ans
def solution(A):
# write your code in Python 3.6
min_num = float("inf")
set_A = set(A)
# finding the smallest number
for num in set_A:
if num < min_num:
min_num = num
# print(min_num)
#if negative make positive
if min_num < 0 or min_num == 0:
min_num = 1
# print(min_num)
# if in set add 1 until not
while min_num in set_A:
min_num += 1
return min_num
Not sure why this is not 100% in correctness. It is 100% performance
def solution(A):
arr = set(A)
N = set(range(1, 100001))
while N in arr:
N += 1
return min(N - arr)
solution([1, 2, 6, 4])
#returns 3

Find the subset of a set of integers that has the maximum product

Let A be a non-empty set of integers. Write a function find that outputs a non-empty subset of A that has the maximum product. For example, find([-1, -2, -3, 0, 2]) = 12 = (-2)*(-3)*2
Here's what I think: divide the list into a list of positive integers and a list of negative integers:
If we have an even number of negative integers, multiply everything in both list and we have the answer.
If we have an odd number of negative integers, find the largest and remove it from the list. Then multiply everything in both lists.
If the list has only one element, return this element.
Here's my code in Python:
def find(xs):
neg_int = []
pos_int = []
if len(xs) == 1:
return str(xs[0])
for i in xs:
if i < 0:
neg_int.append(i)
elif i > 0:
pos_int.append(i)
if len(neg_int) == 1 and len(pos_int) == 0 and 0 in xs:
return str(0)
if len(neg_int) == len(pos_int) == 0:
return str(0)
max = 1
if len(pos_int) > 0:
for x in pos_int:
max=x*max
if len(neg_int) % 2 == 1:
max_neg = neg_int[0]
for j in neg_int:
if j > max_neg:
max_neg = j
neg_int.remove(max_neg)
for k in neg_int:
max = k*max
return str(max)
Am I missing anything? P.S. This is a problem from Google's foobar challenge, I am apparently missing one case but I don't know which.
Now here's actual problem:
from functools import reduce
from operator import mul
def find(array):
negative = []
positive = []
zero = None
removed = None
def string_product(iterable):
return str(reduce(mul, iterable, 1))
for number in array:
if number < 0:
negative.append(number)
elif number > 0:
positive.append(number)
else:
zero = str(number)
if negative:
if len(negative) % 2 == 0:
return string_product(negative + positive)
removed = max(negative)
negative.remove(removed)
if negative:
return string_product(negative + positive)
if positive:
return string_product(positive)
return zero or str(removed)
You can simplify this problem with reduce (in functools in Py3)
import functools as ft
from operator import mul
def find(ns):
if len(ns) == 1 or len(ns) == 2 and 0 in ns:
return str(max(ns))
pos = filter(lambda x: x > 0, ns)
negs = sorted(filter(lambda x: x < 0, ns))
return str(ft.reduce(mul, negs[:-1 if len(negs)%2 else None], 1) * ft.reduce(mul, pos, 1))
>>> find([-1, -2, -3, 0, 2])
'12'
>>> find([-3, 0])
'0'
>>> find([-1])
'-1'
>>> find([])
'1'
Here's a solution in one loop:
def max_product(A):
"""Calculate maximal product of elements of A"""
product = 1
greatest_negative = float("-inf") # greatest negative multiplicand so far
for x in A:
product = max(product, product*x, key=abs)
if x <= -1:
greatest_negative = max(x, greatest_negative)
return max(product, product // greatest_negative)
assert max_product([2,3]) == 6
assert max_product([-2,-3]) == 6
assert max_product([-1, -2, -3, 0, 2]) == 12
assert max_product([]) == 1
assert max_product([-5]) == 1
Extra credit: what if the integer constraint were relaxed? What extra information do you need to collect during the loop?
Here is another solution that doesn't require libraries :
def find(l):
if len(l) <= 2 and 0 in l: # This is the missing case, try [-3,0], it should return 0
return max(l)
l = [e for e in l if e != 0] # remove 0s
r = 1
for e in l: # multiply all
r *= e
if r < 0: # if the result is negative, remove biggest negative number and retry
l.remove(max([e for e in l if e < 0]))
r = find(l)
return r
print(find([-1, -2, -3, 0, 2])) # 12
print(find([-3, 0])) # 0
EDIT :
I think I've found the missing case which is when there are only two elements in the list, and the highest is 0.

Categories

Resources