during the Kata on Codewars called 'Find The Parity Outlier' I faced a problem, and have been trying to solve it using dictionary. I pass almost all tests except 4.
Instruction for the Kata is:
You are given an array (which will have a length of at least 3, but could be very large) containing integers. The array is either entirely comprised of odd integers or entirely comprised of even integers except for a single integer N. Write a method that takes the array as an argument and returns this "outlier" N.
The function is:
def find_outlier(integers):
d = dict()
count = 0
count1 = 0
for i in range(len(integers)):
if integers[i] % 2 != 0 :
d['odd'] = integers[i]
else:
d['even'] = integers[i]
for j in range(len(integers)):
if integers[j] % 2 == 0:
count += 1
else:
count1 += 1
if count > count1:
return d['odd']
return d['even']
Test Results:
2 should equal 1
36 should equal 17
36 should equal -123456789
0 should equal 1
So the question is? Why is it so? Can you help me to sort the problem out? Thanks a lot!
I'm not sure what exactly you're referring to with that list of test results. In general though, your method with the dictionary seems like it might be overcomplicating things a bit as well. You shouldn't need to use a dict, and you shouldn't need two for loops either. Here's an alternative solution to this problem using only list comprehension.
def find_outlier(arr):
# input should be an array-like of integers (and length >= 3) with either only one odd element OR only one even element
odd_mask = [n%2 != 0 for n in arr] # boolean array with True in the location(s) where the elements are odd
even_mask = [n%2 == 0 for n in arr] # boolean array with True in the location(s) where the elements are even
N_odd = sum(odd_mask) # number of odd elements in the input
N_even = sum(even_mask) # number of even elements in the input
if N_even == 1: # if even is the 'outlier'...
return arr[even_mask.index(True)] # return the element of the input array at the index we determined we had an even
elif N_odd == 1: # if odd is the 'outlier'...
return arr[odd_mask.index(True)] # return the element of the input array at the index we determined we had an odd
else: # something has gone wrong or the input did not adhere to the standards set by the problem
return None
And even this is technically not as efficient as it could be. Let me know if you try this and whether it solves whatever issue you were experiencing with expected results.
In your code the final part should not be in the else block, nor even in the for loop:
if count > count1:
return d['odd']
return d['even']
Like this is may give a wrong result. For instance, if the first number in the input is odd, and is the only odd one, then this code will return d['even'] which is obviously wrong.
Place these lines after the loop (with correct indentation) and it should work.
However, this problem can be solved without dictionary or extra lists. Have a go at it.
def find_outlier(integers):
parity = integers[-2] % 2
if integers[-1] % 2 != parity:
if integers[0] % 2 != parity:
return integers[-2]
else:
return integers[-1]
for i in integers:
if i % 2 != parity:
return i
Related
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)
The function I am writing is meant to add every other integer from 0 to n. For example, if you inputted 6, you would get 12 because 6 + 4 + 2 + 0 = 12.
I was working with a list at first but realized I have to be working with integers instead. Below is my attempt, but I get TypeError: object of type 'int' has no len().
I'm confused as to how to fix this problem and make it a proper recursive function.
def addNum(n):
if len(n) == 0:
return 0
else:
return n[0] + addNum(n[::2])
print(addNum(6)) #example of how you would call the function
If you are looking to add just the alternating integers, it should be like
def addNum(n):
if n < 1 :
return 0
else:
return n + addNum(n-2)
print(addNum(6))
You pass an Integer to your function addnum, but you need a list. So you have to write addnum([6]) to create a list with the 6 as a single entry.
You have to call the function with the argument, reduced by 2 if you want to work without a list.
def addnum(n):
if n == 0:
return 0
else:
return n + addnum(n - 2)
print(addnum(6))
This gives the output 12.
so first of all the func len() is for lengths and you actually don't need that, what you need is a list comprehension. I assume that you don't know them so go and learn about them because they can help with these kinds of functions because they aren't that hard to write and are highly flexible.
The code below here does exactly what you want!
def addnum(n):
if n == 0:
return 0
else:
return sum([list for list in range(0,n+1,2)])
print(addnum(10))
first I create a function named addnum which has one parameter called n.
secondly I created the if else clauses, if the number is equal to 0 it returns 0. Else it returns the sum of every odd integer from 0 the the range. n+1 is here just to get the number you put in because the func range doesn't include the last value.
Write a func/on first_neg that takes a (possibly empty) list of
numbers as input parameter, finds the first occurrence of a
nega/ve number, and returns the index (i.e. the posi/on in the
list) of that number. If the list contains no nega/ve numbers or it
is empty, the program should return None. Use while loop (and
not for loop) and your while loop should stop looping once the
first nega/ve number is found.
This is the question my teacher asked me any ideas this what i did:
def first_neg(list):
count = 0
for number in list:
if number < 0:
count += 1
return count
Dosent seem to work properly i just joined 1st post hope can get some help
x = [1,2,3,-5]
def first_neg(list):
count = 0
for number in list:
count += 1 #moved it outside of the if
if number < 0:
return count
print(first_neg(x)) #prints 4
You want to increment count not when you've found the answer but everytime the forloops loops. Note that this method returns 4 which is the fourth item in the list, not the index, Index of the list starts from 0 so to access it would be 3. Take our list x = [1,2,3,-5], -5 is in the fourth slot of the list, but to access it we have to call x[3] since lists starts at 0 indexing.
If you want to return the index of the list where the first negative number is found try this:
x = [1,2,3,-5]
def first_neg(list):
for count, number in enumerate(list):
if number < 0:
return count
print(first_neg(x)) # prints 3
This is because enumerate creates a "pairing" of the item in the list and it's the current count. Enumerate just counts from 0 everytime it gets an item out of the list.
Also as a side note ( I didn't change it in my answer since I wanted you to understand what's going on ). Don't name your variables keywords like list, tuple, int, str... Just a bad idea and habit, it works as you can see but it can cause issues.
Return the index immediately once you encounter the negative element. Increment the index otherwise:
def first_neg(lst):
count = 0
while count < len(lst):
if lst[count] < 0:
return count
count = count + 1
return None
Note : Better if you use enumerate() rather than using extra count variable. The code you mentioned is not written in pythonic way.
You may try this as well:
def first_neg(lst):
res = [i for i,x in enumerate(lst) if x<0]
return None if res == [] else res[0]
The code above can be improved using generators as suggested by #Chris_Rands.
I am trying to better understand list comprehension in Python. I completed an online challenge on codewars with a rather inelegant solution, given below.
The challenge was:
Given a list of even numbers and one odd, return the odd
Given a list of odd numbers and one even, return the even
My (inelegant) solution to this was:
def find_outlier(integers):
o = []
e = []
for i in integers:
if i % 2 == 0:
e.append(i)
else:
o.append(i)
# use sums to return int type
if len(o) == 1:
return sum(o)
else:
return sum(e)
Which works fine, but seems to be pretty brute force. Am I wrong in thinking that starting (most) functions with placeholder lists like o and e is pretty "noob-like"?
I would love to better understand why this solution works for the odd list, but fails on the even list, in an effort to better understand list comprehension:
def find_outlier(integers):
if [x for x in integers if x % 2 == 0]:
return [x for x in integers if x % 2 == 0]
elif [x for x in integers if x % 2 != 0]:
return [x for x in integers if x % 2 != 0]
else:
print "wtf!"
o = [1,3,4,5]
e = [2,4,6,7]
In[1]: find_outlier(o)
Out[1]: [4]
In[2]: find_outlier(e)
Out[2]: [2, 4, 6]
Where Out[2] should be returning 7.
Thanks in advance for any insights.
Your attempt fails because the first if is always going to be true. You'll always have a list with at least 1 element; either the odd one out is odd and you tested a list with all even numbers, otherwise you have a list with the one even number in it. Only an empty list would be false.
List comprehensions are not the best solution here, no. Try to solve it instead with the minimum number of elements checked (the first 2 elements, if they differ in type get a 3rd to break the tie, otherwise iterate until you find the one that doesn't fit in the tail):
def find_outlier(iterable):
it = iter(iterable)
first = next(it)
second = next(it)
parity = first % 2
if second % 2 != parity:
# odd one out is first or second, 3rd will tell which
return first if next(it) % 2 != parity else second
else:
# the odd one out is later on; iterate until we find the exception
return next(i for i in it if i % 2 != parity)
The above will throw a StopIteration exception if there are either fewer than 3 elements in the input iterable, or there is no exception to be found. It also won't handle the case where there is more than one exception (e.g. 2 even followed by 2 odd; the first odd value would be returned in that case).
What are the shortcomings of this response (which is at the top of the solution stack on this particular challenge)?
def find_outlier(int):
odds = [x for x in int if x%2!=0]
evens= [x for x in int if x%2==0]
return odds[0] if len(odds)<len(evens) else evens[0]
The most efficient answer is going to get a little ugly.
def f(in_list):
g = (i for i in in_list)
first = next(g)
second = next(g) #The problem as described doesn't make sense for fewer than 3 elements. Let them handle the exceptions.
if first%2 == second%2:
a = first%2
for el in g:
if el%2 != a:
return el
else:
third = next(g)
if third%2 == first%2:
return second
else:
return first
except ValueError('Got a bad list, all evens or all odds')
I need to sum the elements of a list, containing all zeros or ones, so that the result is 1 if there is a 1 in the list, but 0 otherwise.
def binary_search(l, low=0,high=-1):
if not l: return -1
if(high == -1): high = len(l)-1
if low == high:
if l[low] == 1: return low
else: return -1
mid = (low + high)//2
upper = [l[mid:high]]
lower = [l[0:mid-1]]
u = sum(int(x) for x in upper)
lo = sum(int(x) for x in lower)
if u == 1: return binary_search(upper, mid, high)
elif lo == 1: return binary_search(lower, low, mid-1)
return -1
l = [0 for x in range(255)]
l[123] = 1
binary_search(l)
The code I'm using to test
u = sum(int(x) for x in upper)
works fine in the interpreter, but gives me the error
TypeError: int() argument must be a string or a number, not 'list'
I've just started to use python, and can't figure out what's going wrong (the version I've written in c++ doesn't work either).
Does anyone have any pointers?
Also, how would I do the sum so that it is a binary xor, not simply decimal addition?
You don't actually want a sum; you want to know whether upper or lower contains a 1 value. Just take advantage of Python's basic container-type syntax:
if 1 in upper:
# etc
if 1 in lower:
# etc
The reason you're getting the error, by the way, is because you're wrapping upper and lower with an extra nested list when you're trying to split l (rename this variable, by the way!!). You just want to split it like this:
upper = the_list[mid:high]
lower = the_list[:mid-1]
Finally, it's worth noting that your logic is pretty weird. This is not a binary search in the classic sense of the term. It looks like you're implementing "find the index of the first occurrence of 1 in this list". Even ignoring the fact that there's a built-in function to do this already, you would be much better served by just iterating through the whole list until you find a 1. Right now, you've got O(nlogn) time complexity (plus a bunch of extra one-off loops), which is pretty silly considering the output can be replicated in O(n) time by:
def first_one(the_list):
for i in range(len(the_list)):
if the_list[i] == 1:
return i
return -1
Or of course even more simply by using the built-in function index:
def first_one(the_list):
try:
return the_list.index(1)
except ValueError:
return -1
I need to sum the elements of a list, containing all zeros or ones, so that the result is 1 if there is a 1 in the list, but 0 otherwise.
What's wrong with
int(1 in l)
I need to sum the elements of a list, containing all zeros or ones, so that the result is 1 if there is a 1 in the list, but 0 otherwise.
No need to sum the whole list; you can stop at the first 1. Simply use any(). It will return True if there is at least one truthy value in the container and False otherwise, and it short-circuits (i.e. if a truthy value is found early in the list, it doesn't scan the rest). Conveniently, 1 is truthy and 0 is not.
True and False work as 1 and 0 in an arithmetic context (Booleans are a subclass of integers), but if you want specifically 1 and 0, just wrap any() in int().
Stop making nested lists.
upper = l[mid:high]
lower = l[0:mid-1]