3

I am trying to solve this problem but it doesn't give me the correct answer. Here is the problem:

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.

Here is my code:

a = [2, 4, 6, 8, 10, 3]
b = [2, 4, 0, 100, 4, 11, 2602, 36]
c = [160, 3, 1719, 19, 11, 13, -21]

def find_outlier(list_integers):
    for num in list_integers:
       if num % 2 != 0:
         odd = num
       elif num % 2 == 0:
         even = num
    for num in list_integers:
      if len([odd]) < len([even]):
        return odd
      else:
        return even


print(find_outlier(a))

print(find_outlier(b))

print(find_outlier(c))

It spits out 10, 36, 160 and obviously only the last one is correct. Can anyone help me out with it? Thanks!

  • 1
    `len([odd])` cannot possibly return any value other than 1 - you're explicitly passing a one-element list to the function! – jasonharper Jul 07 '20 at 21:41
  • 1
    Does this answer your question? [Finding the Parity Outlier in a list of odd/even integers](https://stackoverflow.com/questions/39089459/finding-the-parity-outlier-in-a-list-of-odd-even-integers) – Georgy Jul 13 '20 at 12:34
  • 1
    Also: [Find The Parity Outlier Python](https://stackoverflow.com/q/59538186/7851470) – Georgy Jul 13 '20 at 12:35

5 Answers5

4

You could analyze the first three and find the outlier if it is there. If it is there, you are done. If not, you know the expected parity and can test each subsequent element accordingly.

Creating lists for odd/even numbers, while in principle leading to a result, is unnecessarily memory inefficient.


In code this could look something like:

def find_outlier(seq):
    par0 = seq[0] % 2
    par1 = seq[1] % 2
    if par0 != par1:
        return seq[1] if seq[2] % 2 == par0 else seq[0]
    # the checks on the first 2 elements are redundant, but avoids copying
    # for x in seq[2:]: would do less iteration but will copy the input
    for x in seq:  
        if x % 2 != par0:
            return x


a = [2, 4, 6, 8, 10, 3]
b = [2, 4, 0, 100, 4, 11, 2602, 36]
c = [160, 3, 1719, 19, 11, 13, -21]

print(find_outlier(a))
# 3
print(find_outlier(b))
# 11
print(find_outlier(c))
# 160

Your code could not work in its current form:

  • this block:
    for num in list_integers:
       if num % 2 != 0:
         odd = num
       elif num % 2 == 0:
         even = num

will just have the last seen odd in odd and the last seen even in even, without any info on how many were seen. You would need to count how many even/odd numbers are there and eventually need to store the first value encountered for each parity.

  • this second block
    for num in list_integers:
      if len([odd]) < len([even]):
        return odd
      else:
        return even

is always checking the length of the length-1 lists, and will always return even.

I see no simple way of fixing your code to make it with comparable efficiency as the above solution. But you could adapt your code to make it reasonably efficient (O(n) in time -- but without short-circuiting, O(1) in memory):

def find_outlier_2(seq):
    odd = None
    even = None
    n_odd = n_even = 0
    for x in seq:
        if x % 2 == 0:
            if even is None:  # save first occurrence
                even = x
            n_even += 1
        else:  # no need to compute the modulus again
            if odd is None:  # save first occurrente
                odd = x
            n_odd += 1
    if n_even > 1:
        return odd
    else:
        return even

The above is significantly more efficient than some of the other answers in that it does not create unnecessary lists. For example, these solutions are unnecessarily more memory consuming (being O(n) in time and O(n) in memory):

def find_outlier_3(list_integers):
    odd = []
    even = []
    for num in list_integers:
       if num % 2 != 0:
         odd.append(num)
       elif num % 2 == 0:
         even.append(num)
    if len(odd) < len(even):
      return odd[0]
    else:
      return even[0]
def find_outlier_4(lst):
    odds = [el % 2 for el in lst]
    if odds.count(0) == 1:
        return lst[odds.index(0)]
    else:         
        return lst[odds.index(1)]

Simple benchmarks show that these solutions are also slower:

%timeit [find_outlier(x) for x in (a, b, c) * 100]
# 10000 loops, best of 3: 128 µs per loop
%timeit [find_outlier_2(x) for x in (a, b, c) * 100]
# 1000 loops, best of 3: 229 µs per loop
%timeit [find_outlier_3(x) for x in (a, b, c) * 100]
# 1000 loops, best of 3: 341 µs per loop
%timeit [find_outlier_4(x) for x in (a, b, c) * 100]
# 1000 loops, best of 3: 248 µs per loop
norok2
  • 25,683
  • 4
  • 73
  • 99
  • Are you talking about binary search method? – Hamza Ahmed Jul 07 '20 at 20:51
  • No. Binary search typically require your input to be sorted or other similar properties. This is just a regular linear search. – norok2 Jul 08 '20 at 06:10
  • The result of the last three lines of the function here will be identical to `return [x for x in seq[3:] if x % 2 != par0][0]`, but I have understood this one-liner to not have to iterate through the entire list (despite the "`for x in seq[3:]`" part of it) because it includes the condition within the list comprehension. Is my understanding wrong? Will these have the same time complexity? – patmcb Jul 08 '20 at 14:57
  • @patmcb Your one-line code (list comprehension) *will* iterate through all elements of `seq[3:]`. By contrast the code from my answer will stop iterating as soon as the condition is met. – norok2 Jul 08 '20 at 15:12
  • Also, if one is after speed, `for x in seq[3:]:` is going to be slower than `for x in seq:` for sufficiently large inputs, as the former is essentially creating a copy of `seq`, which can be avoided at the cost of 3 extra iterations. – norok2 Jul 08 '20 at 15:50
  • 1
    @norok2 Thanks for the clarification. That makes complete sense. And the reason yours stops when the condition is met is because of how `return` works, right? – patmcb Jul 08 '20 at 20:07
  • 1
    I think that `seq[3:]` should really be `seq[2:]` (or `seq`), otherwise, if the first two have the same parity, and the third one does not, it is missed. – Idea O. Jul 13 '20 at 11:56
3

You can nicely use list comprehensions for this:

a = [2, 4, 6, 8, 10, 3]
b = [2, 4, 0, 100, 4, 11, 2602, 36]
c = [160, 3, 1719, 19, 11, 13, -21]

def outlier(lst):
    odds = [ el % 2 for el in lst ]    # list with 1's when odd, 0's when even
    print(odds)                        # just to show what odds contains
    if odds.count(0) == 1:             # if the amount of zeros (even numbers) = 1 in this list
        print(lst[odds.index(0)])      # find the index of the 'zero' and use it to read the value from the input lst
    else:         
        print(lst[odds.index(1)])      # find the index of the 'one' and use it to read the value from the input lst

outlier(a)
outlier(b)
outlier(c)

Output

[0, 0, 0, 0, 0, 1]        # only 1 'one' so use the position of that 'one'
3
[0, 0, 0, 0, 0, 1, 0, 0]  # only 1 'one' so use the position of that 'one'
11
[0, 1, 1, 1, 1, 1, 1]     # only 1 'zero' so use the position of that 'zero'
160
Ronald
  • 2,930
  • 2
  • 7
  • 18
  • Note that this is also O(N) both in time and memory, while it is possible to solve this with O(N) time and O(1) memory. – norok2 Jul 13 '20 at 16:12
2

I imagine it would be a bit more efficient to first determine if the outlier is odd or even by looking at a small sample, then return just the outlier using list comprehension. This way, if the list is massive, you won't have timeout issues.

Here's what I would do:

def findoutlier(yourlist):
    if (yourlist[0] % 2 == 0 and yourlist[1] % 2 == 0) or (yourlist[0] % 2 == 0 and yourlist[2] % 2 == 0) or (yourlist[1] % 2 == 0 and yourlist[2] % 2 == 0):
        oddeven = "even"
    else:
        oddeven = "odd"
    if oddeven == "even":
        return [i for i in yourlist if i % 2 != 0][0]
    else:
        return [i for i in yourlist if i % 2 == 0][0]

a = [2, 4, 6, 8, 10, 3]
b = [2, 4, 0, 100, 4, 11, 2602, 36]
c = [160, 3, 1719, 19, 11, 13, -21]

print(findoutlier(a))
print(findoutlier(b))
print(findoutlier(c))

This will return 3, 11, and 160 as expected.

patmcb
  • 443
  • 2
  • 9
2

Count the number of odd values in the first three items in the list. This can be done using a sum(). It the sum > 1, the list has mostly odd numbers, so find the even outlier. Otherwise find the odd outlier.

def find_outlier(sequence):
    if sum(x & 1 for x in numbers[:3]) > 1:

        # find the even outlier
        for n in sequence:
            if not n & 1:
                return n

    else:
        # find the odd outlier
        for n in sequence:
            if n & 1:
                return n
RootTwo
  • 4,288
  • 1
  • 11
  • 15
1

You want to use a list to store your odd/even numbers. Right now you're storing them as int and they're getting replaced on your loop's next iteration.

def find_outlier(list_integers):
    odd = []
    even = []
    for num in list_integers:
       if num % 2 != 0:
         odd.append(num)
       elif num % 2 == 0:
         even.append(num)
    if len(odd) < len(even):
      return odd[0]
    else:
      return even[0]
raccoons
  • 420
  • 3
  • 16
  • 1
    Thank you for your help! I thought that would be the case, but when I make an empty list, it gives the first integers in the list **a, b, c**. – Hamza Ahmed Jul 07 '20 at 20:50
  • Could you update your question with your code with the empty list? or do you mean you're getting that result with the above code I've posted. – raccoons Jul 07 '20 at 20:53
  • 1
    I’m getting the result with the code that you’ve posted above! Haha sorry for the confusion! – Hamza Ahmed Jul 07 '20 at 21:05
  • My mistake, didn't see the way you called `len` in your original code. Updated version should work now. – raccoons Jul 07 '20 at 21:07
  • 2
    @HamzaAhmed Note that this unnecessarily complex. The simplest improvement is to replace `if num % 2 != 0: ... elif num % 2 == 0: ...` with a simple `if num % 2 != 0: ... else: ...`. The second issue is that this solution requires `O(n)` extra memory for no particularly good reason, since it is storing all the numbers from the input into two separate lists, but then it uses, at best, the first number encountered for each parity. – norok2 Jul 13 '20 at 11:35