You need to create a Bingo generator in which the size can be specified.
bg = Bingo(size)
Once created, every time a ball is taken out, the ball number must be provided, what are the balls that have been taken out since the start and how many balls are remaining.
>>>> next(bg) >>>> (60, [60], 74)
I have the below class
class Bingo:
def __init__(self, n):
self.initial_size = n
self.current_ball = None
self.picked_balls = []
self.remainder_balls = n
def pick_ball(self):
# Do not consider the already picked balls for choosing randomly
self.current_ball = \
random.choice([i for i in range(1, self.initial_size)
if i not in self.picked_balls])
self.picked_balls.append(self.current_ball)
self.remainder_balls -= 1
In order to extract a ball from my Bingo, I would need to do the below:
bg = Bingo(100)
bg.pick_ball()
However, I have been asked to do it this way
bg = Bingo(100)
pick_ball(bg)
with a generator, so I was wondering if that was possible with the class itself or if it is a completely different approach. Any help will be hugely appreciated.
CodePudding user response:
Here's an actual generator solution. I kept the requested name as-is, although it should normally be bingo
.
import random
def Bingo(n):
taken = []
for _ in range(n):
take = random.choice(list(set(range(1, n 1)) - set(taken)))
taken.append(take)
yield take, taken, n - len(taken)
bg = Bingo(75)
print(next(bg))
print(next(bg))
print(next(bg))
Sample output (Try it online!):
(19, [19], 74)
(30, [19, 30], 73)
(52, [19, 30, 52], 72)
The task description is not concerned about efficiency, but here comes an efficient one anyway. The above solution is already more efficient than yours, taking O(n) time for each pick instead of O(n2). The below solution takes only O(1):
def Bingo(n):
remaining = list(range(1, n 1))
taken = []
while remaining:
i = random.randrange(len(remaining))
taken.append(remaining[i])
remaining[i] = remaining[-1]
remaining.pop()
yield taken[-1], taken, len(remaining)
CodePudding user response:
You can make your class implement the iterator protocol and then use it in your function:
class Bingo:
def __init__(self, n):
# ...
def __next__(self):
if self.remainder_balls <= 0:
raise StopIteration
self.current_ball = # ...
self.remainder_balls -= 1
return self.current_ball
# this is not strictly required for your use case, but makes
# it iterable as well, as in: `for ball in bingo:`
def __iter__(self):
return self
def pick_ball(bingo):
return next(bingo)