Python codility lesson : stacks and queues fish bug

Question:

My code seems to be returning the wrong answer for one test case on codility.(rest of the test cases are correct)

for a test case:
large_random

large random test, N = ~100,000
i’m getting a

got 868 expected 840

question link:
https://app.codility.com/programmers/lessons/7-stacks_and_queues/fish/

def solution(A, B): #declare stacks for fish traveling downstrea, upstream dstrm = [] ustrm = [] #set counter to zero for fish traveling upstream UNHINDERED #by fish traveling downstream, needed because iteration starts upstream counter = 0 n = len(A) #iterate over input, starting from upstream for i in range(n): if B[i] == 0: ustrm.append(A[i]) elif B[i] == 1: dstrm.append(A[i])

# clear upstream stack of fish known to be UNHINDERED, increase counter if len(ustrm) > 0 and len(dstrm) == 0: counter += len(ustrm) ustrm.clear() #compare what's bigger and decrease stack of fish that is eaten while len(dstrm) > 0 and len(ustrm) > 0: if dstrm[-1] > ustrm[-1]: ustrm.pop() elif ustrm[-1] > dstrm[-1]: dstrm.pop() return len(dstrm) + len(ustrm) + counter

Asked By: codinggosu

||

Answers:

Your overall strategy doesn’t make sense to me, and I’m actually surprised that most test cases pass. Here is a very simple case that your code gets wrong:

A:[1,2] B:[0,1]

If I’ve understood the notation correctly, fish 0 is the most upstream fish, and it is swimming upstream, and fish 1 is the most downstream fish, and it is swimming downstream. Thus, the fish won’t meet, and the answer is two.

However, your code outputs 1, because it doesn’t actually check their relative positions at all. It just compares the most upstream downstream-headed fish to the most upstream upstream-headed fish, and declares that one will eat the other, regardless of whether they meet at all.

(If I got the notation backwards, just invert array B – your code outputs 1 in both cases, which is definitely not correct.)

A correct solution needs to compare the positions of the fish, not just their sizes.

Answered By: Personman

Here’s my Python approach using tuples if somebody is still interested in the solution to this problem. I got a 100%.

def solution(A, B):

    fishes, stack = [], []

    for i in range(len(A)):
        fishes.append((B[i], A[i]))

    while fishes:
        if stack and not stack[-1][0] and fishes[-1][0]:
            if stack[-1][1] > fishes[-1][1]:
                fishes.pop()
            else:
                stack.pop()
        else:
            stack.append(fishes.pop())

    return len(stack)
Answered By: Fran Sandi

Here is my approach – may be some one can get idea – how i solved it
Codility 100 % in Codility Python

Code

def solution(A, B):
"""
Codility 100%
https://app.codility.com/demo/results/trainingF5HTCA-YFV/

Idea is to use stack concept
For each iteration if current fish is of type 1 add it to stack 1 fish
Create stack 1 fish - it always holds the downstream ie 1 kind of fish
 and keep killing the smaller fish from the list if it is greater
  else killed by current fish.
Now if stack 1 fish has no fish it means current fish can be counted as remaining fish.


0 represents a fish flowing upstream - 0 fish
1 represents a fish flowing downstream - 1 fish

A[0] = 4    B[0] = 0 alive fish
A[1] = 3    B[1] = 1 downstream
A[2] = 2    B[2] = 0 eaten by A[1]
A[3] = 1    B[3] = 0 eaten by A[1]
A[4] = 5    B[4] = 0 eat to A[1] and remain alive

"""

count = 0
# stores the 1 fish in stack
stack_1_fish = []
print(A)
print(B)
for index in range(len(A)):
    if B[index] == 0:
        # until stack has some 1 fish
        while stack_1_fish:
            # get last fish from stack and check if it can die or not
            # the larger fish eats the smaller one.
            # two fish P and Q meet each other when P < Q, B[P] = 1 and B[Q] = 0, and there are no living fish between them
            if stack_1_fish[-1] > A[index]:
                # stack 1 fish kill to current fish
                # exit from while loop and else block also execute next top for loop
                # check for other fish fight
                print("Killed by " + str(stack_1_fish[-1]) + " Die " + str(A[index]))
                break
            else:
                # stack 1 fish is killed by current fish
                p = stack_1_fish.pop()
                print("Killed by " + str(A[index]) + " Die " + str(p))

        # this is the case when stack becomes empty ie. no fish of kind 1
        # it would never meet previous fish but can fight with downstream fish
        else:
            print("Count fish as remaining......." + str(A[index]))
            count += 1
    else:
        # fish 1 found add to stack
        stack_1_fish.append(A[index])
        print("1 fish found, add to stack, it can kill or killed by someone..." + str(A[index]))
        print(stack_1_fish)

print("Count: " + str(count))
print("Stack 1 fish: " + str(len(stack_1_fish)))
return count + len(stack_1_fish)

Execution Output –

if __name__ == '__main__':
result = solution([4, 3, 2, 1, 5], [0, 1, 0, 0, 0])
# result = solution([4, 3, 2, 1, 5], [0, 0, 0, 0, 0])
# result = solution([4, 3, 2, 1, 5], [1, 1, 1, 1, 1])
print("")
print("Solution " + str(result))

[4, 3, 2, 1, 5]
[0, 1, 0, 0, 0]
Count fish as remaining.......4
1 fish found, add to stack, it can kill or killed by someone...3
[3]
Killed by 3 Die 2
Killed by 3 Die 1
Killed by 5 Die 3
Count fish as remaining.......5
Count: 2
Stack 1 fish: 0

Solution 2

[4, 3, 2, 1, 5]
[0, 0, 0, 0, 0]
Count fish as remaining.......4
Count fish as remaining.......3
Count fish as remaining.......2
Count fish as remaining.......1
Count fish as remaining.......5
Count: 5
Stack 1 fish: 0

Solution 5

[4, 3, 2, 1, 5]
[1, 1, 1, 1, 1]
1 fish found, add to stack, it can kill or killed by someone...4
[4]
1 fish found, add to stack, it can kill or killed by someone...3
[4, 3]
1 fish found, add to stack, it can kill or killed by someone...2
[4, 3, 2]
1 fish found, add to stack, it can kill or killed by someone...1
[4, 3, 2, 1]
1 fish found, add to stack, it can kill or killed by someone...5
[4, 3, 2, 1, 5]
Count: 0
Stack 1 fish: 5

Solution 5

I found the most elegant Codility fish solution may be found in here.
It is using the stack:

def solution(a, b):
    l = 0 # left
    s = [] # stack
    for i in range(len(a)):
        if b[i]==1: # fish moving upwards
            s.append(a[i]) # to the stack
        else: # eat fish mowing downwards.
            while len(s)>0:
                if s[-1]<a[i]:
                    s.pop() # eat from the stack
                else:
                    break
            else:
                l+=1
    l+=len(s)
    return l
Answered By: 555

Python 3

For those that are looking for a step by step explanation that is easy to follow here’s my 100% solution.

It uses queues/stacks via append and pop as sort of suggested by the lesson.

You can add various tests at the bottom. Don’t forget to remove print outputs before submitting!

def solution(A, B):
    N = len(A)
    print('START')
    print('Fish sizes:      ' + str(A))
    print('Fish directions: ' +  str(B))
    print()
    
    upstream = 0
    downstream = []
    
    for n in range(N):
        fs = A[n]
        fd = B[n]
    
        print('Fish pos: ' + str(n) + ', fish dir: ' + str(fd)+ ', fish size: ' + str(fs))

        if fd == 0 and downstream == []:
            print('No fishes in the way')
            upstream += 1
            print('Fishes gone upstream: ' + str(upstream))

        elif fd == 1:
            downstream.append(fs)
            print('No fishes in the way')
            print('Fishes going downstream: ' + str(downstream))

        elif fd == 0 and downstream != []:
            print('Challenge!')

            while downstream:
                # pick the downstream head
                pfs = downstream.pop()

                # if it's bigger kills upstream, puts back downstream, move on
                if pfs > fs:
                    downstream.append(pfs)
                    print('Downward eats upward')
                    print('Fishes going downstream: ' + str(downstream))
                    break

                    # if it's smaller don't put it back and move on to next fish
                elif pfs < fs:
                    print('Upward eats downward')
                    print('Fishes going downstream: ' + str(downstream))
                    if downstream == []:
                        upstream += 1
                        print('Fishes gone upstream: ' + str(upstream))
                        break
                continue
        print()

    print('FINISH')
    print('Fishes gone upstream: ' + str(upstream))
    print('Fishes going downstream: ' + str(len(downstream)))
    fishes = upstream + len(downstream)
    print('Fishes alive: ' + str(fishes))

    return fishes

    
if __name__ == '__main__':
    assert solution([4,3,2,1,5], [0,1,0,0,0]) == 2
    print('Test 1 OK!')
    print()
    assert solution([1,2,3,4,5], [1,1,1,1,0]) == 1
    print('Test 2 OK!')
    print()
    assert solution([5,4,3,2,1], [1,0,0,0,0]) == 1
    print('Test 3 OK!')
    print()
    assert solution([1,2,3,4,5], [1,0,0,0,0]) == 4
    print('Test 4 OK!')
    print()
    assert solution([1,2,3,4,5], [1,1,1,1,1]) == 5
    print('Test 4 OK!')
    print()

Here is the output clearly explained step by step:

START
Fish sizes:      [4, 3, 2, 1, 5]
Fish directions: [0, 1, 0, 0, 0]

Fish pos: 0, fish dir: 0, fish size: 4
No fishes in the way
Fishes gone upstream: 1

Fish pos: 1, fish dir: 1, fish size: 3
No fishes in the way
Fishes going downstream: [3]

Fish pos: 2, fish dir: 0, fish size: 2
Challenge!
Downward eats upward
Fishes going downstream: [3]

Fish pos: 3, fish dir: 0, fish size: 1
Challenge!
Downward eats upward
Fishes going downstream: [3]

Fish pos: 4, fish dir: 0, fish size: 5
Challenge!
Upward eats downward
Fishes going downstream: []
Fishes gone upstream: 2

FINISH
Fishes gone upstream: 2
Fishes going downstream: 0
Fishes alive: 2
Test 1 OK!

START
Fish sizes:      [1, 2, 3, 4, 5]
Fish directions: [1, 1, 1, 1, 0]

Fish pos: 0, fish dir: 1, fish size: 1
No fishes in the way
Fishes going downstream: [1]

Fish pos: 1, fish dir: 1, fish size: 2
No fishes in the way
Fishes going downstream: [1, 2]

Fish pos: 2, fish dir: 1, fish size: 3
No fishes in the way
Fishes going downstream: [1, 2, 3]

Fish pos: 3, fish dir: 1, fish size: 4
No fishes in the way
Fishes going downstream: [1, 2, 3, 4]

Fish pos: 4, fish dir: 0, fish size: 5
Challenge!
Upward eats downward
Fishes going downstream: [1, 2, 3]
Upward eats downward
Fishes going downstream: [1, 2]
Upward eats downward
Fishes going downstream: [1]
Upward eats downward
Fishes going downstream: []
Fishes gone upstream: 1

FINISH
Fishes gone upstream: 1
Fishes going downstream: 0
Fishes alive: 1
Test 2 OK!

START
Fish sizes:      [5, 4, 3, 2, 1]
Fish directions: [1, 0, 0, 0, 0]

Fish pos: 0, fish dir: 1, fish size: 5
No fishes in the way
Fishes going downstream: [5]

Fish pos: 1, fish dir: 0, fish size: 4
Challenge!
Downward eats upward
Fishes going downstream: [5]

Fish pos: 2, fish dir: 0, fish size: 3
Challenge!
Downward eats upward
Fishes going downstream: [5]

Fish pos: 3, fish dir: 0, fish size: 2
Challenge!
Downward eats upward
Fishes going downstream: [5]

Fish pos: 4, fish dir: 0, fish size: 1
Challenge!
Downward eats upward
Fishes going downstream: [5]

FINISH
Fishes gone upstream: 0
Fishes going downstream: 1
Fishes alive: 1
Test 3 OK!

START
Fish sizes:      [1, 2, 3, 4, 5]
Fish directions: [1, 0, 0, 0, 0]

Fish pos: 0, fish dir: 1, fish size: 1
No fishes in the way
Fishes going downstream: [1]

Fish pos: 1, fish dir: 0, fish size: 2
Challenge!
Upward eats downward
Fishes going downstream: []
Fishes gone upstream: 1

Fish pos: 2, fish dir: 0, fish size: 3
No fishes in the way
Fishes gone upstream: 2

Fish pos: 3, fish dir: 0, fish size: 4
No fishes in the way
Fishes gone upstream: 3

Fish pos: 4, fish dir: 0, fish size: 5
No fishes in the way
Fishes gone upstream: 4

FINISH
Fishes gone upstream: 4
Fishes going downstream: 0
Fishes alive: 4
Test 4 OK!

START
Fish sizes:      [1, 2, 3, 4, 5]
Fish directions: [1, 1, 1, 1, 1]

Fish pos: 0, fish dir: 1, fish size: 1
No fishes in the way
Fishes going downstream: [1]

Fish pos: 1, fish dir: 1, fish size: 2
No fishes in the way
Fishes going downstream: [1, 2]

Fish pos: 2, fish dir: 1, fish size: 3
No fishes in the way
Fishes going downstream: [1, 2, 3]

Fish pos: 3, fish dir: 1, fish size: 4
No fishes in the way
Fishes going downstream: [1, 2, 3, 4]

Fish pos: 4, fish dir: 1, fish size: 5
No fishes in the way
Fishes going downstream: [1, 2, 3, 4, 5]

FINISH
Fishes gone upstream: 0
Fishes going downstream: 5
Fishes alive: 5
Test 4 OK!

> 
Answered By: Lele Canfora

I think it’s not easy lesson. I spent one day to solve it with time complexity:
O(N).
https://app.codility.com/demo/results/trainingC2YHP6-DR5/

def solution(A, B):
    # write your code in Python 3.6
    n = len(A)
    
    if n<= 1:
        return n
    stack = [None]*n
    size = 0
    for i in range(0,n):
        if size==0:
            stack[size] = i
            size+=1
        else:
            for j in range(size-1,-1,-1):

                if B[i]==B[stack[j]] or B[stack[j]]==0:
                    stack[j+1] = i
                    size = j+2
                    break
                else:
                    if A[i]>A[stack[j]]:
                        
                        stack[j]=i
                        size = j+1
                    else:
                        size = j+1
                        break



    return size
    pass
Answered By: oldnewdb
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.