Make a Deal program - using sets in Python Mostly because I didn't believe the outcome of the classic game play Make-a-Deal (see https://math.stackexchange.com/questions/608957/monty-hall-problem-extended) I made this little program... and indeed if there are 3 doors and the quiz-master opens a door and you then switch from your initial choice of door your chances go up from 33% to 67%! This program is the generalized version whereby you can choose the number of doors and how many doors the quiz-master will open, it calculates the chance for a price for the initial choice (basically 1 over the number of doors) and the chance if you change your selected door after the doors have been opened.

I was pleasantly taken by the elegant solution using Python sets, but I wonder if this is the most efficient way. Comparing with other methods it looks like it becomes relatively more efficient with more doors and doors to open.

Thank you for your input...

#!/usr/bin/env python
'''  application of Make a deal statistics
     application is using sets {}
     for reference:
 https://math.stackexchange.com/questions/608957/monty-hall-problem-extended
'''
import random


def Make_a_Deal(doors, doors_to_open):
    '''  Generalised function of Make_a_Deal. Logic should be self explanatory
         Returns win_1 for the option when no change is made in the choice of
         door and win_2 when the option to change is taken.'''

    win_1, win_2 = False, False

    doors_set = set(range(1, doors+1))
    price_set = set(random.sample(doors_set, 1))
    choice1_set = set(random.sample(doors_set, 1))
    open_set = set(random.sample(doors_set.difference(price_set).
                   difference(choice1_set), doors_to_open))
    choice2_set = set(random.sample(doors_set.difference(open_set).
                      difference(choice1_set), 1))
    win_1 = choice1_set.issubset(price_set)
    win_2 = choice2_set.issubset(price_set)

    return win_1, win_2


def main():
    '''  input:
         - throws: number of times to Make_a_Deal (must be &gt; 0)
         - doors: number of doors to choose from (must be &gt; 2)
         - doors_to_open: number of doors to be opened before giving the option
           to change the initial choice (must be &gt; 0 and &lt;= doors-2)'''

    try:
        throws = int(input('how many throws: '))
        doors = int(input('how many doors: '))
        doors_to_open = int(input('how many doors to open: '))
        if (throws &lt; 1) or (doors &lt; 3) or \
                (doors_to_open &gt; doors-2) or (doors_to_open &lt; 1):
            print('invalid input')
            return

    except Exception as e:
        print('invalid input: ', e)
        return

    number_of_wins_1, number_of_wins_2, counter = 0, 0, 0

    while counter &lt; throws:
        win_1, win_2 = Make_a_Deal(doors, doors_to_open)

        if win_1:
            number_of_wins_1 += 1
        if win_2:
            number_of_wins_2 += 1

        counter += 1
        print('completion is {:.2f}%'.
              format(100*counter/throws), end='\r')

    print('number of wins option 1 is {:.2f}%: '.
          format(100*number_of_wins_1/counter))
    print('number of wins option 2 is {:.2f}%: '.
          format(100*number_of_wins_2/counter))


if __name__ == '__main__':
    main()
You can do this in \$O(1)\$ space and time, where you're doing it in \$O(n)\$ space and time. I cleaned your code and got roughly the same as Graipher's code:

def make_a_deal(doors, doors_to_open):
    doors = range(1, doors+1)
    price = set(random.choice(doors))
    choice_1 = set(random.choice(doors))
    doors = set(doors)
    open_doors = set(random.sample(doors - price - choice_1, doors_to_open))
    choice_2 = set(random.sample(doors - open_doors - choice_1, 1))
    return choice_1 &lt;= price, choice_2 &lt;= price


But there are some changes you can make to make it faster.


After generating price and choice_1 you can check if the user has won, and return True, False.
You don't need to randomly select price, this is as you can rotate the doors until price is the first, always. And so you can make price = 0.
Expanding on the above we now know that 0 is the only door with the price, and so \$1 \to n\$ are all loser doors. From this we know that one of the doors we picked was a loser door, and so we can remove that door, and shift all the other doors down. Which is the same as removing the last door.
Expanding on the above, you can remove \$k\$ more loser doors, as they are randomly selected.
And so the second choice has a range of \$0 \to n - k - 1\$.


And so you can get \$O(1)\$ space and time:

def make_a_deal(doors, doors_to_open):
    price = 0
    choice_1 = random.randrange(doors)
    if choice_1 == price:
        return True, False

    choice_2 = random.randrange(doors - 1 - doors_to_open)
    return False, choice_2 == price