Smallest Positive Number - Python - Codility - python

I am using Codelite to train myself as I just started learning Python. In the excercise "Smallest Positive Number", I used the code below and some test cases passed, but most of them not. How can I improve my code? (test cases like [1,2,3,4] and repetitive numbers like [5,6,2,4,6].
A = [2,1,-2,5,-6]
m = max(A)
temp = []
index = []
result = []
if m < 1:
print ("1")
elif len(A) == 1:
if A[0] == 1:
print("2")
else:
print("1")
else:
for i in range(len(A)):
if A[i] > 0:
temp.append(A[i])
temp.sort()
print(temp)
for j in range(len(temp)):
index.append(j+1)
if temp[j] != index[j]:
result.append(index[j])
print(min(result))
Thanks!

First you would want to store the smallest positive number. Start it at the first element of the list.
small_positive = A[0]
You would then want to iterate through the input
for number in A:
Then check if the number is positive. The easiest way is to see if it is greater then zero.
if number > 0:
After, check if the number is smaller then the current smallest. If so, overwrite it.
if number < small_positive:
small_positive = number
Lastly, you need to check if there were all negatives in the list. If small_positive is negative after the loop, there were no positives in the list.
if small_positive < 0:
return None
This is the easiest way to understand the solution, but there are many built in functions which are much simpler then this.

A = [2,-2,5,-6]
def small(a,b):
if a >= b: return b
else: return a
positive_list = [ele for ele in A if ele > 0]
res = min(positive_list)
if len(positive_list) == 0:
print("no positive number.")
else:
for i in range(len(positive_list)):
res = small(res, positive_list[i])
print(f"Smallest Positive number : {res}")
by this, yon can easily get a result.

For a start, this wouldn't work for the list [-42]:
m = max(A)
if m < 1:
print ("1")
There's no 1 anywhere in that given list so it should not be reported as the solution. Python would usually give you None in that scenario but the puzzle you've been set may have other ideas. It also wouldn't work with the list [] as max() will complain bitterly about the empty list.
You may think that one or both of those cases are unlikely but that's one of the things that makes the difference between a good coder and a not-so-good one. The former assume their users and testers are psychotic geniuses who know how to break code with near-zero effort :-)
To code it up by yourself, you can use the following pseudo-code(1) as a basis:
num_list = (some list of numbers)
min_pos = nothing
for num in num_list:
if num > 0 and (min_pos is nothing or num < min_pos): # >= ?
min_pos = num
print(min_pos)
Of course, if you're learning Python rather than learning programming, you should probably just use the facilities of the language, things will be much easier:
pos_list = [item for item in the_list if item > 0] # >= ?
min_pos = min(pos_list) if len(pos_list) > 0 else None
print(min_pos)
And, if you consider zero to be positive, make sure you use >= 0 instead of > 0, in both code blocks above where indicated.
Both of these snippets will successfully handle lists with no positive numbers, giving you a sentinel value (Python None or pseudo-code nothing).
(1) Yes, it looks a bit like Python but that's only because Python is the ultimate pseudo-code language :-)

Related

Drawbacks of using <= operator instead of == plus an assumption

I have been part of a code review in which I have a condition such as this one (Python code):
my_list = <whatever list>
if len(my_list) <= 0:
do_something()
I have been pointed out that the condition should be == instead of <= as a list cannot have a negative length. I have no issues with it in this case, but I wanted to know if there is really any drawbacks on using <=.
Here is my rationale on why to use <=:
The actual condition that I want to check is not len(my_list) > 0 which is equivalent to len(my_list) <= 0, so that is correct for sure.
As explained in point 1, len(my_list) == 0 alone is not equivalent to the original condition. It is in this case, but it is not trivial why it is this way. len() returns the value returned by __len__() which may be an arbitrary. It only happens to be equivalent because the len() implementation performs some validations on the value. One could argue that len() implementation may change in the future, so the equivalence would not hold anymore.
I feel len(my_list) <= 0 would be safer in general than using len(my_list) == 0 plus an assumption that might not hold in some edge or unexpected cases.
So, are my arguments correct? Do you know any drawbacks of using len(my_list) <= 0?
Sorry if this seems like a non-relevant question, but this have been raised to me more than once on code reviews so wanted to understand if there is something I am missing.
So, after some more digging I found this in the python PEP 8 programming recommendations (https://peps.python.org/pep-0008/#programming-recommendations)
# Not recommended
my_list = []
if not len(my_list):
print('List is empty!')
# Recommended
my_list = []
if not my_list:
print('List is empty!')
so now you know the most pythonic way to check if a list is empty!
After testing a bit, I found this works even for custom classes! But my test might be incomplete.
class Test:
def __init__(self, len):
self.len = len
def __len__(self):
return self.len
t1 = Test(3)
if len(t1):
print("len1 > 0")
if t1:
print("len2 > 0")
t1 = Test(0)
if len(t1):
print("len3 > 0")
if t1:
print("len4 > 0")
t1 = Test(-1)
if len(t1):
print("len5 > 0")
prints
len1 > 0
len2 > 0
Exception has occurred: ValueError
__len__() should return >= 0

In a function ,How can I assign the value to a set?

I'm trying to make a program that finds the perfect number.(Perfect number=The sum of its divisors except itself is a number equal to itself.)And I want to add one more thing. Firstly I want to define an empty list and put the perfect numbers in it.But When I run the programm I didn't take the right throughput.How can I solve this problem.
My cods
def perfect(number):
total = 0
for i in range(1,number):
if number % i == 0:
total += i
return total
perfect_number_set = []
for i in range(1,1001)
if perfect(i):
perfect_number_set += [perfect(i)]
print(perfect_number_set)
print(i)
The output of the codes I wrote
[True]
6
[True,True]
28
[True,True,True]
You have following issues in your code:
Your implementation of perfect method is incorrect. You need to return True/False if a number is perfect. You are returning the total which is not correct.
Missing colon : in the for loop
def perfect(number):
total = 0
for i in range(1,number):
if number % i == 0:
total += i
return total == number
perfect_number_list = []
for i in range(1,1001):
if perfect(i):
print(i)
perfect_number_list.append(i)
print(perfect_number_list)
There is a difference between set and a list in python. You have used a list.
Set contains only unique entries.
Best practice tip:
defining a list use :
list_name = list()
Better way of adding items to as list by using it's method
list_name.append(list_item_to_add)
Another thing is that returning a number and checking it in if without any condition is not readable. Change it to:
if perfect(i) != 0:
(Altho Yours implementation work, because python if treats 0 like False and any other value as True)

How to find the positions that a number falls in-between in a set of sequential integers

I have an number variable and a sequential set of integers. As the number variable changes, I need to record what positions in the sequential set of integers the number variable falls in-between.
For example, if my set of integers is:
li = [20,21,22,23,24,25,26,27,28,29,30]
And the number variable is:
num = 22.74
My desired output would be the positions in the list that num falls in-between:
2,3
I know that li.index(22) would return the position but only if that exact item is in the list. Haven't been able to come across how to find positions if the number falls in-between items.
I imagine a for loop that compares num to each of the neighboring position items in the list by checking if num falls in between those two integers could solve it. Something like:
for x and the following item in li:
if x < num < the following item:
positionone = x
positiontwo = the following item
else:
continue
Just haven't been able to complete how this works, mainly by getting the next position of the list in replace of "the following item". Maybe there's a better way of figuring this? Any help is much appreciated! Thanks!
You are close with your basic logic. You need to iterate through the list. If you do it using the index, rather than the value, you'll be able to retrieve the positions you want.
Your basic logic is fine.
for idx in range(len(li)-1):
if li[idx] < num < li[idx+1]:
pos1, pos2 = idx, idx+1
Given that, I expect that you can solve the problem of equality as a special case, right?
I'm not going to touch the ambiguities regarding num being integer or not. But I will give you the tool needed to answer your question.
Let lo and hi respectively be the lower and upper limit of your interest. Then, to get the indices of the number between these two is done as such:
indices = [i for i, x in enumerate(li)
if lo < x < hi]
Please note that I use < twice there. If you want the lower bound to be inclusive, replace the first < with <=. Similarly, if you want the upper bound to be inclusive replace the second < with <=. Or very generally:
def indices_between(iterable, lo, hi, include_lo=False, include_hi=False):
lo_req = (lambda x: lo <= x) if include_lo else (lambda x: lo < x)
hi_req = (lambda x: x <= hi) if include_hi else (lambda x: x < hi)
return [i for i, x in enumerate(iterable)
if lo_req(x) and hi_req(x)]
You can compare your number with each element and break loop if current element greater than your number:
for index, current_element in enumerate(li):
if current_element > num:
position_one, position_two = index - 1 if index else index, index
break
else:
position_one, position_two = index, index
A mathematical way of doing this is to subtract the number from the list and check for the two minimum absolute value. If li is a numpy array you can do this in one line:
li = np.array(li)
interval = sorted(np.abs(li-num).argsort()[:2])

Optimizing Python Function to Submit on Codewars

I have a solution for this problem on codewars.com that works when I run it in Sublime, but when I try to submit, I get this error:
Process was terminated. It took longer than 12000ms to complete
Why did my code time out?
Our servers are configured to only allow a certain amount of time for your code to execute. In rare cases the server may be taking on too much work and simply wasn't able to run your code efficiently enough. Most of the time though this issue is caused by inefficient algorithms. If you see this error multiple times you should try to optimize your code further.
The goal of the function is to find the next biggest number after a given number that you can make by rearranging the digits of a given number. For example, if I was given 216, I would need to return 261.
This is the code I have now:
import itertools
def next_bigger(n):
# takes a number like 472 and puts it in a list like so: [4, 7, 2]
num_arr = [int(x) for x in str(n)]
perms = []
total = ''
# x would be a permutation of num_arr, like [7, 2, 4]
for x in itertools.permutations(num_arr):
for y in x:
total += str(y)
perms.append(int(total))
total = ''
# bigger is all permutations that are bigger than n,
# so bigger[0] is the next biggest number.
# if there are no bigger permutations, the function returns -1
bigger = sorted([x for x in perms if x > n])
return bigger[0] if bigger else -1
I'm new to coding in Python, so is there some mistake I am making which causes my code to be extremely inefficient? Any suggestions are welcome.
Thanks for all the help you guys gave me. I ended up finding a solution from here using the Next Lexicographical Permutation Algorithm
This is my tidied up version of the solution provided here:
def next_bigger(n):
# https://www.nayuki.io/res/next-lexicographical-permutation-algorithm/nextperm.py
# https://www.nayuki.io/page/next-lexicographical-permutation-algorithm
# Find non-increasing suffix
arr = [int(x) for x in str(n)]
i = len(arr) - 1
while i > 0 and arr[i - 1] >= arr[i]:
i -= 1
if i <= 0:
return -1
# Find successor to pivot
j = len(arr) - 1
while arr[j] <= arr[i - 1]:
j -= 1
arr[i - 1], arr[j] = arr[j], arr[i - 1]
# Reverse suffix
arr[i : ] = arr[len(arr) - 1 : i - 1 : -1]
return int(''.join(str(x) for x in arr))
Why are you getting TLE (time limit exceeded)?
Because your algorithm has wrong complexity. How much permutations you will find for list with 3 elements? Only 6. But what if we use list with 23 elements? 25852016738884976640000.
This is too much for time limit.
So, if you want to have solve this problem you have to find solution without permutations. Please rethink how the numbers are written. The number 271 is bigger then 216 because the number on the second position has bigger value 7>1.
So, your solution has to find two numbers and swap them position. The number on the left have to smaller then the second one.
For example - for 111115444474444 you should find 5 and 7.
Then you swap them - and now you should sort sublist on right from the first position.
For example after swapped the values (111117444454444) you have to sort (444454444) -> (444444445). Now merge all, and you have solution.
import functools
def next_bigger(a):
a = map(int, str(a))
tmp = list(reversed(a))
for i, item_a in enumerate(reversed(a)):
for j in (range(i)):
if item_a < tmp[j]:
#you find index of number to swap
tmp[i]=tmp[j]
print(list(reversed(tmp[i:])))
tmp[j]=item_a
fin = list(reversed(tmp[i:])) + sorted(tmp[:i])
return functools.reduce(lambda x,y: x*10+y, fin)
return -1
A simple backtracking approach is to consider the digits one at a time. Starting from the most significant digit, pick the smallest number you have left that doesn't prevent the new number from exceeding the input. This will always start by reproducing the input, then will have to backtrack to the next-to-last digit (because there aren't any other choices for the last digit). For inputs like 897654321, the backtracking will immediately cascade to the beginning because there are no larger digits left to try in any of the intermediate slots.
You should sorting the num_arr in desc order and creating a number by combining the result.
Since OP required, next largest, OP needs to check starting from right, which right digit is larger then its very left digit and rotate their position.
Here is the final code:
def next_bigger(n):
num_arr = [int(x) for x in str(n)]
i = 0
i = len(num_arr) - 1
while(i > 0):
if num_arr[i] > num_arr[i-1]:
a = num_arr[i]
num_arr[i] = num_arr[i-1]
num_arr[i-1] = a
break
else:
i = i-1
newbig = "".join(str(e) for e in num_arr)
return int(newbig)
Now I edit to calculate next bigger element.
def perms(s):
if(len(s)==1):
return [s]
result=[]
for i,v in enumerate(s):
result += [v+p for p in perms(s[:i]+s[i+1:])]
return result
a=input()
b=perms(str(a))
if len(b)!=1:
for i in range(0,len(b)):
if b[i]==a:
print (b[i+1])
break
else:
print ("-1")

Find the Duplicate Number

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
My solution:
def findDuplicate(nums):
slow = fast = finder = 0
while fast is not None:
slow = nums[slow]
fast = nums[nums[fast]]
if fast is slow:
return slow
return False
nums = [1,2,2,3,4]
print findDuplicate(nums)
My above solution works and gives me o/p 2 but it doesn't work for every input for example it doesn't work for [11,15,17,17,14] or [3,1,2,6,2,3] and gives me error IndexError: list index out of range. I am not able to find patterns and am not able to track down the exact problem. Also tried to change my while condition:
while fast is not None and nums[nums[fast]] is not None:
your help will be greatly appreciated! Thank you.
Since the numbers are between 1 and n and you have been told there is only one duplicate, you can use difference between the sum of the numbers in the array and the sum of numbers from 1 to n to get the duplicate.
def findDuplicate(l):
n = len(l) - 1 # Get n as length of list - 1
return sum(l) - (n * (n + 1) / 2) # n*(n+1)/2 is the sum of integers from 1 to n
So the duplicate is the sum of the list - n*(n+1)/2
Of course, this doesn't generalize to finding duplicates for any list. For that case, you need to use #Jalepeno112 's answer.
The fact that the first one works is a fluke. Let's look at what it does on the first pass.
nums = [1,2,2,3,4]
# slow starts as index 0. So now, you've reassigned slow to be nums[0] which is 1.
# so slow equals 1
slow = nums[slow]
# now you are saying that fast equals nums[nums[0]].
# nums[0] is 1. nums[1] is 2
# so fast = 2
fast = nums[nums[fast]]
On the next pass, slow will be nums[1] which is 2. fast will be nums[nums[2]] which is nums[2] which is 2. At this point slow and fast are equal.
In your second example, you are getting an IndexError because of fast = nums[nums[fast]] If the value at nums[fast] is not a valid index, then this code will fail. Specifically in the second example, nums[0] is 11. nums doesn't have an element at index 11, so you get an error.
What you really want to be doing is performing a nested for loop on the array:
# range(0,len(nums)-1) will give a list of numbers from [0, to the length of nums-1)
# range(1, len(nums)) does the same,
# except it will start at 1 more than i is currently at (the next element in the array).
# So it's range is recomputed on each outer loop to be [i+1, length of nums)
for i in range(0,len(nums)-1):
for j in range(i+1,len(nums)):
# if we find a matching element, return it
if nums[i] == nums[j]:
return nums[i]
# if we don't find anything return False
return False
There are likely other more Pythonic ways to achieve this, but that wasn't your original question.
first you must ensure all numbers in list satisfy your constrains.
to find duplicated numbers in a list Use Counter in collections it will return each number and number of occurrence example :
>>> from collections import Counter
>>> l=Counter([11,15,17,17,14])
>>> l
Counter({17: 2, 11: 1, 14: 1, 15: 1})
to get the most common one use :
>>> l.most_common(n=1)
[(17, 2)]
where n is the number most common numbers you want to get
def duplicates(num_list):
if type(num_list) is not list:
print('No list provided')
return
if len(num_list) is 0 or len(num_list) is 1:
print('No duplicates')
return
for index,numA in enumerate(num_list):
num_len = len(num_list)
for indexB in range(index+1, num_len):
if numA == num_list[indexB]:
print('Duplicate Number:'+str(numA))
return
duplicates([11,15,17,17,14])
duplicates([3,1,2,6,2,3])
duplicates([])
duplicates([5])
l=[]
n= int(input("the number of digit is :"))
l=[0 for k in range(n)]
for j in range(0,n):
l[j]=int(input("the component is"))
print(l)
b=0; c=0
for i in range(n):
if l[i]== l[n-1-i]:
b=1;c=i
if b==1:
print("duplicate found! it is",l[c])
elif b==0:
print("no duplicate")
The answer is unfinished. It tries to convert the array to a linked list. So far it found where the slow pointer and fast pointer met, but this is the halfway solution. To get the solution, we need to initialize another pointer from the beginning of the linked list and walk towards each other. When they meet, that point is the where cycle is detected, in our question it is where the single point is:
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
slow,fast=0,0
while True:
slow=nums[slow]
fast=nums[nums[fast]]
if slow==fast:
break
slow2=0
while True:
slow2=nums[slow2]
slow=nums[slow]
if slow==slow2:
return slow2

Categories