Как выполнить высокопроизводительные сравнения в фиксированном наборе параметров в Python 3.7? - PullRequest
0 голосов
/ 06 июня 2019

Я борюсь с правильным способом представления фиксированного набора параметров (характеристик игральных карт) для наиболее эффективных сравнений на более поздних этапах разработки.

В примере с игральными картами у вас есть ранг (от 2 до 10, Джек, Король, Королева, Туз) и масть (Сердце, Бриллиант, Клуб, Спейд). В других областях моего кода у меня могут быть похожие параметры, такие как предустановленные режимы перемешивания (Без перемешивания, Фишер-Йейтс и т. Д.). Поскольку приложение фактически представляет собой симуляцию, эти сравнения будут частыми и должны быть максимально эффективными. Например, проверка, является ли карта определенной мастью.

Каков наилучший способ сделать это в Python 3.7 (или позже)? Я всегда могу использовать простые строки, но это может быть медленнее. Я мог бы просто использовать целые числа везде, но это затрудняет чтение кода (вспоминая 3 - это «Клубы», или «0» - «Нет случайного выбора»). Я мог бы использовать Enums, но мое исследование выявило некоторые плохие характеристики производительности.

В C # я использовал Enums (Rank / Suit / Shuffle Mode) в сочетании со структурой (Rank = Ace, Suit = Spade) для представления карты. Сравнения были эффективными, и код был читабельным, поскольку это целочисленное сравнение.

Каков наилучший способ сделать это в современных версиях Python?

TL; DR - Если у вас был фиксированный набор опций (скажем, элемент магического заклинания в видеоигре или что-то в этом роде), которые вам приходилось использовать в сравнении очень часто (каждый раз, когда вы наносите урон), как бы вы представляли эти варианты для оптимальной производительности?

1 Ответ

1 голос
/ 06 июня 2019

Представление карт в виде строк - это задом наперед. Струны для людей; компьютеры используют номера. Легко и быстро найти таблицу строк по номерам, когда вам нужно общаться с человеком; гораздо медленнее и сложнее искать числа за строкой, хотя Python скрывает эту сложность от вас, встроив ее в язык, создавая иллюзию, что это легко.

Можно использовать объект карты с отдельным целым числом для ранга и масти, но мой любимый способ представления карт - это просто простые целые числа, скажем, от 0 до 51 (или даже от 8 до 59), начиная с четырех двойок, затем четыре трэя и т. д., так что 8 = двойка булав, 9 = двойка алмазов, 10 = двойка сердец, 11 = двойка пиков, 12 = трея треф,. , , до 59 - пиковый туз.

При этой нумерации ранг карты - просто (c >> 2), а масти - (c & 3). И иногда вам даже не нужно разделять звание и костюм для сравнения. Например, если рука блэкджека является массивом этих целых чисел, вот функция для вычисления ее значения, а также, является ли оно жестким или мягким:

def value(hand):
  total = 0
  found_ace = False

  for card in hand:
    if card >= 56:
      found_ace = True
      total += 1
    elif card >= 40:
      total += 10
    else:
      total += (card >> 2)

  if total < 12 and found_ace:
    return total + 10, True
  return total, False

print(value([9, 22, 59])) # deuce, five, ace

В этом коде общая сумма рассчитывается с помощью нескольких сравнений, дополнений и сдвигов, причем все вместе, вероятно, занимают меньше времени, чем просто поиск, что «Семь» имеет значение 7. Если вам нужно имя a карта, это тоже просто:

rank_names = [ "?", "?", "Deuce", "Trey", "Four", "Five", "Six", "Seven",
  "Eight", "Nine", "Ten", "Jack", "Queen", "King", "Ace" ]
suit_names = [ "Club", "Diamond", "Heart", "Spade" ];

def name(card):
  return rank_names[card >> 2] + " of " + suit_names[card & 3] + "s"
...