#python #algorithm #linked-list
#python #алгоритм #связанный список
Вопрос:
Я пытаюсь генерировать игры NBA по играм за квартал.
Как я это делал, я выбирал количество очков, которые одна команда наберет за квартал, а затем генерировал игры, чтобы соответствовать этому количеству очков.
Итак, допустим, я выбираю команду А, чтобы набрать 5 очков за четверть. Я создал алгоритм для генерации этого списка примеров:
[ [‘2 указателя’, ‘ПЕРЕКЛЮЧАТЕЛЬ’], [‘ПРОПУЩЕННЫЕ 2 указателя’, ‘Отскок в атаке’, ‘ОСТАТЬСЯ’], [‘3 указателя’, ‘ПЕРЕКЛЮЧАТЕЛЬ’]]
(Этот список намного проще, чем тот, который создается на самом деле. Я добавил больше функций, таких как передачи и подборы, за исключением перехватов и передач.)
«ПЕРЕКЛЮЧИТЬ» означает поменять владения, в то время как «ОСТАТЬСЯ» означает, что текущая команда сохраняет владение. У каждого «ПЕРЕКЛЮЧЕНИЯ» должно быть хотя бы одно действие, доступное в списке другой команды, в то время как у каждого «ПРЕБЫВАНИЯ» должно быть хотя бы одно действие, доступное в списке текущей команды.
Теперь, когда я создаю два списка для 2 разных команд, их списки могут отличаться по длине, потому что (среди прочего) одна команда может набрать больше очков, чем другая.
Когда я пытаюсь объединить два списка вместе, чтобы создать симуляцию игры за игрой, это может привести к тому, что у одной команды не будет действий, когда в другой команде есть «ПЕРЕКЛЮЧАТЕЛИ».
Мой вопрос в том, как я могу добавить дополнительные «ПЕРЕКЛЮЧАТЕЛИ» или «ПРЕБЫВАНИЕ» в список любых команд, чтобы каждый «ПЕРЕКЛЮЧАТЕЛЬ» выполнял действие в списке другой команды, а каждое «ПРЕБЫВАНИЕ» выполняло действие в списке текущей команды.
Моей первой мыслью было добавить случайные обороты и перехваты (приводящие к «ПЕРЕКЛЮЧЕНИЮ») в списки и надеяться, что это сработает, но я не могу придумать, как это сделать.
Кроме того, порядок списка начальной команды не имеет значения, если список соответствует количеству требуемых очков.
Это то, что у меня есть до сих пор:
def join_steals_tov(teamA_list, teamB_list):
overall_list = []
possession = 'Team A'
print(len(teamA_list))
print(len(teamB_list))
while teamA_list and teamB_list:
if possession == 'Team A':
play = teamA_list.pop(0)
play.insert(0,'Team A')
overall_list.append(play)
print(f"Team A: {play}")
if play[-1] == 'SWITCH':
possession = 'Team B'
else:
play = teamB_list.pop(0)
play.insert(0,'Team B')
overall_list.append(play)
print(f"Team B: {play}")
if play[-1] == 'SWITCH':
possession = 'Team A'
return teamA_list, teamB_list, overall_list
Комментарии:
1. Имеет ли значение, сколько очков набирает другая команда? Или вы просто пытаетесь генерировать игру за игрой для 1 команды?
2. ДА. Итак, изначально я создаю два списка для двух отдельных команд. Один список добавит столько очков, сколько я установил. Таким образом, например, один список добавит до 20 очков, что означает, что команда, соответствующая этому списку, наберет 20 очков. Затем я пытаюсь объединить эти 2 списка вместе, чтобы сформировать данные о воспроизведении.
3. Я думаю, что могу случайным образом добавлять обороты и перехваты в каждый список, но 1) Я не знаю, как это будет работать 2) выполнение этого может просто усложнить задачу. Может быть, я не могу начать с требуемого количества очков? Я сейчас в тупике
4. Хорошо, я подумаю. Я думаю, есть несколько способов, которыми вы могли бы сделать это, используя вероятности для создания этих случайных событий.
Ответ №1:
Вот один из возможных подходов к созданию списка игр за квартал, начиная с двух списков событий подсчета очков, например
teamA_scores = ['A2', 'A3']
teamB_scores = ['B3', 'B3', 'B2']
Шаг 1. объедините списки в один список и перетасуйте объединенный список, например
scores: ['A3', 'B3', 'A2', 'B3', 'B2']
^^ --- team A scores 3 points
Шаг 2. Когда одна и та же команда забивает дважды подряд, должно быть событие, в котором другая команда не забивает. Поэтому создайте новый список игр, который включает в себя обязательные игры с переключением, например
switch: ['A3', 'B3', 'A2', 'B3', 'AS', 'B2']
^^ --- team A gives the ball back to team B without scoring
Шаг 3. Добавьте в список игру на конец квартала (просто чтобы упростить следующий шаг), например
end Q: ['A3', 'B3', 'A2', 'B3', 'AS', 'B2', 'AQ']
^^ --- time runs out with team A in possession
Шаг 4. Теперь добавьте несколько дополнительных игр без очков, чтобы заполнить квартал. В примере кода список игр дополняется до произвольной длины 10. Для реалистичности вам нужно будет назначить время для каждой игры и дополнять список, пока общее время не составит 12 минут.
plays: ['AK', 'A3', 'B3', 'AS', 'BS', 'A2', 'B3', 'AS', 'B2', 'AQ']
^^ --- team A keeps the ball, e.g. missed shot, offensive rebound
^^ --- team A loses the ball
^^ --- team B loses the ball
Вот пример кода, который показывает, как все это объединяется, чтобы создать список из 10 игр из двух списков выигрышных игр:
from random import shuffle
from random import randrange
from random import randint
# helper functions
def getTeam(event):
return event[0]
def makeSameTeamItem(event, item):
if getTeam(event) == 'A':
return 'A' item
else:
return 'B' item
def makeOtherTeamItem(event, item):
if getTeam(event) == 'B':
return 'A' item
else:
return 'B' item
# start with a list of scores for each team
teamA_scores = ['A2', 'A3']
teamB_scores = ['B3', 'B3', 'B2']
# step 1: combine and shuffle to create an overall list of scores
scores = teamA_scores teamB_scores
shuffle(scores)
print 'scores:', scores
# step 2: create list of plays including mandatory SWITCH plays
plays = []
plays.append(scores[0])
for i in range(1, len(scores)):
if getTeam(scores[i]) == getTeam(scores[i-1]):
plays.append(makeOtherTeamItem(scores[i], 'S'))
plays.append(scores[i])
print 'switch:', plays
# step 3: add the end of quarter play
plays.append(makeOtherTeamItem(scores[-1], 'Q'))
print 'end Q: ', plays
# step 4: insert additional non-scoring plays to fill the quarter
quarter = 10
while len(plays) < quarter:
index = randrange(0, len(plays))
if quarter - len(plays) == 1 or randint(1,2) == 1:
plays.insert(index, makeSameTeamItem(plays[index], 'K'))
else:
plays.insert(index, makeOtherTeamItem(plays[index], 'S'))
plays.insert(index, makeOtherTeamItem(plays[index], 'S'))
print 'plays: ', plays
Комментарии:
1. Это отличный подход. Чтобы сначала записать события подсчета очков, а затем заполнить последовательные события подсчета очков одной и той же командой играми без подсчета очков. Это было не в моем вопросе, но что, если я также пытаюсь добавить «узкий диапазон» определенного события без подсчета очков? Например, если команды НБА делают 50% своих 2 указателей, количество пропущенных 2 указателей должно быть «примерно» (плюс-минус 1 или 2) таким же количеством сделанных 2 указателей. И как я могу дополнительно добавить передачи, подборы в нападении, подборы в защите? Я попытаюсь решить эту проблему самостоятельно, исходя из вашего подхода, поэтому я буду обновлять это, но если вы
2. (продолжение …) есть идея о том, как это сделать, прежде чем я это выясню, это было бы здорово!
3. @theyeinkye Это легко, если в списке много событий, не приносящих очков. Например, если есть десять событий «A2» и двадцать событий, не приносящих очков («AS» или «AK»), тогда вы можете случайным образом выбрать от 9 до 11 событий, не приносящих очков, и преобразовать их в события «AM2» (команда A пропускает 2 указателя). Вам нужно будет сделать то же самое для 3 указателей. Остальные «AS» и «AK» преобразуются в мяч за пределами поля, блок-шоты, вынос, пенальти в нападении и т. Д.
4. Это хорошая идея разделить события на выигрышные и не выигрышные и разделить их соответственно процентам событий. Я попробую, спасибо.
Ответ №2:
Я думаю, что это хороший способ играть с цепями Маркова. Взгляните сюда
Основная идея заключается в том, что для каждого текущего состояния существует вероятность перехода в следующее состояние. Вы можете создать матрицу вероятностей текущего состояния и вероятность того, каким может быть следующее состояние. Затем используйте эти веса для извлечения из random.choices
. Вы можете сделать это настолько сложным, насколько захотите. Ключ в том, чтобы начать с простого, заставить его работать, затем вы можете добавить больше и приукрасить его (например, я собирался добавить состояния пропущенных 3 очка и сделанных 3 очка, (вам также нужно будет включить вероятность пропущенных 2 очка, сделанных 2 очка … иливы могли бы использовать это как расширение пропущенного удара и сделанного удара), но я решил, что я оставлю это вам для работы и добавления. Вы также можете добавить туда атакующие и оборонительные фолы, если это был фол при стрельбе в защите, вероятность того, что сделанный удар имел голевую передачу и т. Д.)
Тогда нужно всего лишь настроить несколько функций, чтобы отслеживать все и «разыгрывать» владение.
Итак, вот мой пример:
1. Создайте матрицу вероятностей для каждой команды. Индекс будет текущим состоянием, а столбцы — следующим состоянием.
Примечание: вы хотите, чтобы сумма каждой строки была равна 1
import pandas as pd
import random
prob_matrix_A = pd.DataFrame([
[0, 0.05, 0.1, 0.375, 0.475, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 1],
[0, 0.02, 0, 0, 0, 0.19, 0.79, 0],
[0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0]],
columns = ['Possession', 'Unforced Turnover','Steal','Miss Shot','Made Shot','Off Reb','Def Rebound', 'Change Possession'],
index = ['Possession', 'Unforced Turnover','Steal','Miss Shot','Made Shot','Off Reb','Def Rebound', 'Change Possession'])
prob_matrix_B = pd.DataFrame([
[0, 0.05, 0.1, 0.475, 0.375, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 1],
[0, 0.02, 0, 0, 0, 0.09, 0.89, 0],
[0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0]],
columns = ['Possession', 'Unforced Turnover','Steal','Miss Shot','Made Shot','Off Reb','Def Rebound', 'Change Possession'],
index = ['Possession', 'Unforced Turnover','Steal','Miss Shot','Made Shot','Off Reb','Def Rebound', 'Change Possession'])
Итак, вот матрица вероятностей для команды A:
print (prob_matrix_A.to_string())
Possession Unforced Turnover Steal Miss Shot Made Shot Off Reb Def Rebound Change Possession
Possession 0 0.05 0.1 0.375 0.475 0.00 0.00 0
Unforced Turnover 0 0.00 0.0 0.000 0.000 0.00 0.00 1
Steal 0 0.00 0.0 0.000 0.000 0.00 0.00 1
Miss Shot 0 0.02 0.0 0.000 0.000 0.19 0.79 0
Made Shot 0 0.00 0.0 0.000 0.000 0.00 0.00 1
Off Reb 1 0.00 0.0 0.000 0.000 0.00 0.00 0
Def Rebound 0 0.00 0.0 0.000 0.000 0.00 0.00 1
Change Possession 0 0.00 0.0 0.000 0.000 0.00 0.00 0
2. Настройте, как отслеживать счет и какая команда владеет мячом:
team_score_limit = 25
# game setup
game_dict = {'team_a':{'score':0, 'possession':False, 'prob_matrix': prob_matrix_A},
'team_b':{'score':0, 'possession':False, 'prob_matrix': prob_matrix_B},
'score_limit':team_score_limit}
# Do a jump ball to see who starts with ball
# Lets say team_a has 55% of getting the jump, and team_b 45%
wins_jump = random.choices(['team_a','team_b'], weights=[.55,.45], k=1)[0]
game_dict[wins_jump]['possession'] = True
print('%s: Wins the jump ball' %wins_jump)
3. Создайте функции для определения того, кто владеет мячом (т. Е. кто играет в нападении, защите):
# Function to return who has possession
def possession(game_dict):
for k, v in game_dict.items():
if type(v) == dict:
if v['possession'] == True:
offense_team = k
if v['possession'] == False:
defense_team = k
return offense_team, defense_team
4. Создайте функцию для разыгрывания владения мячом на основе вероятностей:
def play(current_state, game_dict):
# Determine who has possession and get their prob matrix
offense, defense = possession(game_dict)
prob_matrix = game_dict[offense]['prob_matrix']
states_list = prob_matrix.columns.tolist()
weights = prob_matrix.loc[current_state,:].tolist()
# Based on current state weights, get the next state
new_state = random.choices(states_list, weights=weights, k=1)[0]
if 'Possession' not in new_state:
if new_state in ['Def Rebound', 'Steal']:
print ('%s: %s by %s' %(offense, new_state, defense))
else:
print ('%s: %s' %(offense, new_state))
current_state = new_state
if current_state == 'Made Shot':
game_dict[offense]['score'] = 2
# if team_a score reached the limit, we'll return False to stop the game
if game_dict['team_a']['score'] >= game_dict['score_limit']:
return False
# if it's a change of possession, we'll update our game_dict
if new_state == 'Change Possession':
game_dict[offense]['possession'] = False
game_dict[defense]['possession'] = True
# else the team who currently possess the ball didn't change from the previous outcome (Ie. Offensive rebound), then they get another possession play
else:
play(current_state, game_dict)
5. Установите начальное current_state
значение для запуска «игры» и разыграйте ее:
# Play the first quarter
current_state = 'Possession'
continue_play = True
while continue_play != False:
continue_play = play(current_state, game_dict)
print ('nEnd of 1st Quarter!n')
print ('**************************')
print ('team_a: %stteam_b: %s' %(game_dict['team_a']['score'],game_dict['team_b']['score']))
Вывод:
runfile('test.py', wdir='C:/test')
team_a: Wins the jump ball
team_a: Made Shot
team_b: Miss Shot
team_b: Off Reb
team_b: Made Shot
team_a: Made Shot
team_b: Made Shot
team_a: Miss Shot
team_a: Def Rebound by team_b
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Steal by team_b
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Miss Shot
team_a: Def Rebound by team_b
team_b: Made Shot
team_a: Miss Shot
team_a: Def Rebound by team_b
team_b: Steal by team_a
team_a: Made Shot
team_b: Made Shot
team_a: Made Shot
team_b: Made Shot
team_a: Made Shot
team_b: Steal by team_a
team_a: Made Shot
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Unforced Turnover
team_b: Made Shot
team_a: Steal by team_b
team_b: Made Shot
team_a: Unforced Turnover
team_b: Steal by team_a
team_a: Made Shot
team_b: Unforced Turnover
team_a: Miss Shot
team_a: Off Reb
team_a: Made Shot
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Made Shot
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Made Shot
team_b: Made Shot
team_a: Made Shot
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Miss Shot
team_a: Off Reb
team_a: Made Shot
team_b: Miss Shot
team_b: Def Rebound by team_a
team_a: Made Shot
End of 1st Quarter!
**************************
team_a: 26 team_b: 16