Why my code doesn't generate the desired result?

Question:

What I want the code to do is to print the list containing numbers that are:

  • product of 2- or 3-digit numbers
  • palindrome
  • in the range [101101, 1000000).

There should be no 5-digit numbers and some 6-digit numbers in the final list as they are less than 101101. But there still are some 5-digit numbers after processing. Why is that happening?

list1 = []
for i in range(100, 1000):
    for j in range(100, 1000):
        if str(i*j) == str(i*j)[::-1]:   # checking for palindrome
            list1.append(i*j)


list1 = list(set(list1)) # removing duplicates

print(sorted(list1))
# print(len(list1))

for ii in list1:                      # removing numbers, out of range
    if ii < 101101 or ii >= 1000000:
        list1.remove(ii)


print(sorted(list1))
# print(len(list1))

But when I use sets to remove the elements out of range, it works. The below given code does the job.

set1 = set(range(10000, 101102))
list1 = list(set(list1) - set1)

But I am not understanding, why the previous code fails to print the desired output?

Edit: As 1 of you suggested, yes it is a duplicate of this. My bad for not checking for the question in the existing bank.

Asked By: vighnesh153

||

Answers:

This is the most simple way to solve your problem;

list1 = []
for i in range(100, 1000):
     for j in range(100, 1000):
        if str(i*j) == str(i*j)[::-1]:   # checking for palindrome
            list1.append(i*j)

# Use list comprehension to filter unwanted values from the list.
list1 = sorted([value for value in set(list1) if 101101 <= value < 1000000])


print(list1)
Answered By: ruohola

This is a textbook example of why you don’t modify a list in-place. The culprit is this loop:

for ii in list1:                      # removing numbers, out of range
     if ii < 101101 or ii >= 1000000:
          list1.remove(ii)

The initial parts of list1 are all going to be 5 digits long, as you already noticed. List iterators step along by index. Let’s take a look at what happens when you remove an element from a list of 5-digit numbers:

  1. Start with a list, and ii referencing the first number:

      10001, 20002, 30003, 40004, ...
        ^
        ii
    
  2. Remove ii from the list:

      20002, 30003, 40004, ...
    
        ii -> 10001
    

    ii is still a valid reference, but not to an item in the list. The list has naturally shifted back by one element.

  3. Continue the loop to the next element:

      20002, 30003, 40004, ...
               ^
               ii
    

Hopefully you can see how that skips adjacent elements that you want to filter out.

You have a number of viable workarounds. Here are a few to get you started:

  1. Use a list comprehension to make a new list:

      list1 = [x for x in list1 if len(x) > 5]
    
  2. Get rid of the offending loop entirely, and only append a string to the list on the first loop if it’s longer than the retirement.

  3. Iterate backwards, so the shifts don’t affect you:

     for ii in reversed(list1):
          if ii < 101101 or ii >= 1000000:
               list1.remove(ii)
    

    OR

     for ii in range(len(list1), -1, -1):
          if list1[ii] < 101101 or list1[ii] >= 1000000:
               del list1[ii]
    

Personally, I’d recommend option 2 since it’s the least hassle in the end. You don’t even need to check the length of the string, just whether the number is greater than 99999.

Answered By: Mad Physicist
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.