Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Python: Coupon Collector is a classic statistics problem with many practical app

ID: 3776049 • Letter: P

Question

Python:

Coupon Collector is a classic statistics problem with many practical applications. The problem is to pick objects from a set of objects repeatedly and find out how many picks are needed for all the objects to be picked at least once. A variation of the problem is to pick cards from a shuffled deck of 52 cards repeatedly and find out how many picks are needed before you see one of each suit. Assume a picked card is placed back in the deck

before picking another. Write a program to simulate the number of picks needed to get four cards, one from each suit and display the four cards picked (it is possible a card may be picked twice). Here is a sample run of the program:
Queen of Spades

5 of Clubs

Queen of Hearts

4 of Diamonds

Number of picks: 12

Explanation / Answer

import random


class Card(object):
"""Represents a standard playing card.
  
Attributes:
suit: integer 0-3
rank: integer 1-13
"""

suit_names = ["Clubs", "Diamonds", "Hearts", "Spades"]
rank_names = [None, "Ace", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "Jack", "Queen", "King"]

def __init__(self, suit=0, rank=2):
self.suit = suit
self.rank = rank

def __str__(self):
"""Returns a human-readable string representation."""
return '%s of %s' % (Card.rank_names[self.rank],
Card.suit_names[self.suit])

def __cmp__(self, other):
"""Compares this card to other, first by suit, then rank.

Returns a positive number if this > other; negative if other > this;
and 0 if they are equivalent.
"""
t1 = self.suit, self.rank
t2 = other.suit, other.rank
return cmp(t1, t2)


class Deck(object):
"""Represents a deck of cards.

Attributes:
cards: list of Card objects.
"""
  
def __init__(self):
self.cards = []
for suit in range(4):
for rank in range(1, 14):
card = Card(suit, rank)
self.cards.append(card)

def __str__(self):
res = []
for card in self.cards:
res.append(str(card))
return ' '.join(res)

def add_card(self, card):
"""Adds a card to the deck."""
self.cards.append(card)

def remove_card(self, card):
"""Removes a card from the deck."""
self.cards.remove(card)

def pop_card(self, i=-1):
"""Removes and returns a card from the deck.

i: index of the card to pop; by default, pops the last card.
"""
return self.cards.pop(i)

def shuffle(self):
"""Shuffles the cards in this deck."""
random.shuffle(self.cards)

def sort(self):
"""Sorts the cards in ascending order."""
self.cards.sort()

def move_cards(self, hand, num):
"""Moves the given number of cards from the deck into the Hand.

hand: destination Hand object
num: integer number of cards to move
"""
for i in range(num):
hand.add_card(self.pop_card())


class Hand(Deck):
"""Represents a hand of playing cards."""
  
def __init__(self, label=''):
self.cards = []
self.label = label


def find_defining_class(obj, method_name):
"""Finds and returns the class object that will provide
the definition of method_name (as a string) if it is
invoked on obj.

obj: any python object
method_name: string method name
"""
for ty in type(obj).mro():
if method_name in ty.__dict__:
return ty
return None


if __name__ == '__main__':
deck = Deck()
deck.shuffle()

hand = Hand()
print find_defining_class(hand, 'shuffle')

deck.move_cards(hand, 5)
hand.sort()
print hand