0

I'm banging my head against the wall trying to figure out why this nested loop is miscounting the number of times an integer occurs in a list. I've set up a function to take two lines of input, n and ar, where n is the number of integers in ar and ar is the list of integers. My code is below:

import sys

n = sys.stdin.readline()
n = int(n)
ar = sys.stdin.readline()
ar = ar.split(' ')
ar = [int(i) for i in ar]

def find_mode(n,ar):
    # create an empty dict and initially set count of all integers to 1
    d = {}
    for i in range(n):
        d[ar[i]] = 1

    for i in range(n):
        # hold integer i constant and check subsequent integers against it
        # increase count if match
        x = ar[i]

        for k in range(i+1,n):
            if ar[k] == x:
                d[ar[k]] += 1

    print(d)

The counter seems to be increasing the count by 1 every time, which leads me to believe it's a problem with the nested loop.

>>> 9 
>>> 1 2 3 4 4 9 9 0 0
{0: 2, 1: 1, 2: 1, 3: 1, 4: 2, 9: 2}

OK

>>> 10
>>> 1 2 3 4 4 9 9 0 0 0
{0: 4, 1: 1, 2: 1, 3: 1, 4: 2, 9: 2}

Count of 0 increased by +2

>>> 11
>>> 1 2 3 4 4 9 9 0 0 0 0
{0: 7, 1: 1, 2: 1, 3: 1, 4: 2, 9: 2}

Count of 0 increased by +3

I understand there might be more efficient or "pythonic" ways to count the amount of times a number occurs in a list but this was the solution I came up with and as someone still learning Python, it would help to understand why this exact solution is failing. Many thanks in advance.

3
  • You don't need to have a nested loop in order to solve this problem. The problem you're facing is the existence of a nested loop. Commented Mar 6, 2018 at 21:55
  • Can you elaborate a bit more as to why a nested loop won't work? My thought process was to hold one integer constant then loop through the rest of them (via nested loop) and compare them to the constant. Can this not be achieved? Commented Mar 6, 2018 at 22:00
  • Var n feels unnecessary because you can just use len(). Commented Mar 7, 2018 at 0:08

5 Answers 5

2

This is because for each distinct number in the list (call it x) you count the number of subsequent appearances. This is fine if a number only occurs twice but if it occurs multiple times you will over-count for each additional appearance.

For example: [0, 0, 0, 0]. You iterate over the list and then for each item you iterate over the list that follows that item. So for the first 0 you count a total of 3 subsequent 0s. For the second however you will count a total of 2 and for the third a total of 1 which makes 6. This is why you have 3 too much in the end.

You can achieve this task by using collections.Counter:

>>> from collections import Counter
>>> d = Counter(ar)
Sign up to request clarification or add additional context in comments.

2 Comments

This can be observed via adding print("Found %s at position %s" % (x, k)) to the inner loop where you increment the dct.
Thank you for the explanation. That hit the nail on the head for me.
0

I'm not exactly sure that I can fix your specific problem, but would something like this work instead?

d={}
for x in ar:
    d[x] = d.get(x, 0) + 1

I understand that you want to fix your existing work as a learning exercise, but I'm not sure that that approach is even the right one. As it is, I can't really tell what you're going for, so it's hard for me to offer specific advice. I would recommend that you don't throw good time after bad.

Comments

0

python has a method to do exactly what you're describing.

It's called .count().

If you do ar.count(3), it will return the number of occurences of 3 in the list ar.

** In your case:**

There's no need for a nested loop as you only need one loop.

Try this:

dic = {}
for num in ar:
    if num not in dic:
        dic[num] = 1
    else:
        dic[num] += 1

This would produce the dict you want with the numbers and their occurences

Comments

0

You can refer to other answers as to how you should solve this problem more efficiently, but to answer the question you're asking (Why doesn't this nested loop work?):

To visualize what your nested loop is doing consider the following input:

0 0 0 0 0

Your algorithm will count the following:

0 0 0 0 0 ^ ^ ^ ^ ^ (5)

then,

0 0 0 0 0 ^ ^ ^ ^ (4)

then,

0 0 0 0 0 ^ ^ ^ (3)

then,

0 0 0 0 0 ^ ^ (2)

and finally,

0 0 0 0 0 ^ (1)

What happens is it counts the number of 0's multiple times over. In this instance it will count

15 0's (5+4+3+2+1)

2 Comments

Thank you very much for this explanation. Essentially, the first pass is the correct answer, which is why it was recommended not to use a nested loop?
Yeah, that's exactly why
0

itertools are your friend

from itertools import groupby

def group_by_kv_l(n):
    l = []
    for k, v in groupby(n):
        l.append((len(list(v)),int(k)))
    return l

def group_by_kv_d(n):
    d = {}
    for k, v in groupby(n):
        d[(int(k))] = len(list(v))
    return d

if __name__ == "__main__":

    n = input().split()
    n = "".join(n)

    print(
        "STDIN: {}".format(n)
    )

    print(
        group_by_kv_l(n)
    )

    print(
        group_by_kv_d(n)
    )

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.