1

I have set objects which are comparable in some way and I want to remove objects from the set. I thought about how this problem changes, for different comparable relations between the elements. I was interested in the development of the search space, the use of memory and how the problem scales.

  • 1st scenario: In the most simple scenario the relation would be bidirectional and thus we could remove both elements, as long as we can ensure that by removing the elements do not remove other 'partners'.
  • 2nd scenario: The comparable relation is not bidirectional. Remove only the element in question, not the one it is comparable to. A simplified scenario would be the set consisting of integers and the comparable operation would be 'is dividable without rest'

I can do the following, not removing elements:

a_set = set([4,2,3,7,9,16])

def compare(x, y):
    if (x % y == 0) and not (x is y):
        return True
    return False

def list_solution():
    out = set()
    for x in a_set:
        for y in a_set:
            if compare(x,y):
                out.add(x)
                break
    result = a_set-out
    print(result)

    

Of course, my first question as a junior Python programmer would be:

  • What is the appropriate set comprehension for this?
  • Also: I cannot modify the size of Python sets during iteration, without copying, right?
  • And now for the algo people out there: How does this problem change if the relation between the number of elements an element can be comparable to increases. How does it change if the comparable relation represent partial orders?
smci
  • 32,567
  • 20
  • 113
  • 146
Mac C.
  • 153
  • 10

1 Answers1

3

I will precede with confirming your claim - changing set while iterating it would trigger a RuntimeError, which would claim something along the lines of "Set changed size during iteration".


Now, lets start from the compare function: since you are using sets, x is y is probably like x == y and the last one is always a better choice when possible.

Furthermore, no need for the condition; you are already performing one:

def compare (x, y):
    return x != y and x % y == 0

Now to the set comprehension - that's a messy one. After setting the set as an argument - which is better than using global variable - the normal code would be something like

for x in my_set:
    for y in my_set:
        if compare(x, y):
            for a in (x, y):
                temp.append(a)

notice the last two lines, which do not use unpacking because that would be impossible in the comprehension. Now, all what's left is to move the a to the front and make all : disappear - and the magic happens:

def list_solution (my_set):
    return my_set - {a for x in my_set for y in my_set if compare(x, y) for a in (x, y)}

you can test it with something like

my_set = set([4, 2, 3, 7, 9, 16])
print(list_solution(my_set)) # {7}
  • The condition and the iteration on (x, y) can switch places - but I believe that iterating after confirming would be faster then going in and starting to iterate when there's a chance you wouldn't perform any action.

The change for the second case is minor - merely using x instead of the x, y unpacking:

def list_solution_2 (my_set):
    return my_set - {x for x in my_set for y in my_set if compare(x, y)}
Uriel
  • 15,579
  • 6
  • 25
  • 46