Forming the best possible poker hand This is what the function is doing: it gets two arrays of tuples with 5 items each - hand = [H1,H2,H3,H4,H5] and board = [B1,B2,B3,B4,B5]

What I need to do is check all arrays formed by 2 items from hand and 3 items from board, like combination = [Hn,Hm,Bi,Bj,Bk] (100 combinations in total)

Then I need to compare each one of the combinations against a dictionary to get the combination rank, and then return the best array (best rank) and the rank itself:

def check_hand(hand, board, dictionary_A, dictionary_B):

 best_hand = []
 first_int = True

 for h1 in range (0, 4):
  for h2 in range (h1+1, 5):
   for b1 in range (0, 3):
    for b2 in range (b1+1, 4):
     for b3 in range (b2+1, 5):
      hand_check = []
      hand_check.append(hand[m1])
      hand_check.append(hand[m2])
      hand_check.append(board[b1])
      hand_check.append(board[b2])
      hand_check.append(board[b3])
      hand_check = sort(hand_check) #Custom sort for my array of objects
      hand_ranks = "".join([str(hand_check[0].rank),str(hand_check[1].rank),str(hand_check[2].rank),str(hand_check[3].rank),str(hand_check[4].rank)])

      if (hand_check[0].suit == hand_check[1].suit and hand_check[1].suit == hand_check[2].suit and hand_check[2].suit == hand_check[3].suit and hand_check[3].suit == hand_check[4].suit):
       control = [dictionary_A[hand_ranks][0],dictionary_A[hand_ranks][1]]
      else:
       control = [dictionary_B[hand_ranks][0],dictionary_B[hand_ranks][1]]

      if first_int:
       best_hand = hand_check
       rank = control
       first_int = False
      elif (int(control[0]) &gt; int(rank[0])):
       rank = control
       best_hand = hand_check       
      elif (int(control[0]) == int(rank[0])):
       if (int(control[1]) &gt; int(rank[1])):       
        rank = control
        best_hand = hand_check      

 return best_hand, rank[0]


I need to run this check for 2 million different hands and interact over 1000 times for every hand (Ideally I would run it for at least 100000 times for every hand, for a more statistically accurate result).
Any ideas on how to make it more efficient?

Examples:
I added some "prints" on the way for better understanding:

Hand: Q♥K♥Q♠K♠3♦︎
Board: 2♣2♥J♦︎6♥4♦︎

2♣2♥J♦︎Q♥K♥ - 2♣2♥6♥Q♥K♥ - 2♣2♥4♦︎Q♥K♥ - 2♣6♥J♦︎Q♥K♥ - 2♣4♦︎J♦︎Q♥K♥ -
   2♣4♦︎6♥Q♥K♥ - 2♥6♥J♦︎Q♥K♥ - 2♥4♦︎J♦︎Q♥K♥ - 2♥4♦︎6♥Q♥K♥ - 4♦︎6♥J♦︎Q♥K♥ -
   2♣2♥J♦︎Q♥Q♠ - 2♣2♥6♥Q♥Q♠ - 2♣2♥4♦︎Q♥Q♠ - 2♣6♥J♦︎Q♥Q♠ - 2♣4♦︎J♦︎Q♥Q♠ -
   2♣4♦︎6♥Q♥Q♠ - 2♥6♥J♦︎Q♥Q♠ - 2♥4♦︎J♦︎Q♥Q♠ - 2♥4♦︎6♥Q♥Q♠ - 4♦︎6♥J♦︎Q♥Q♠ -
   2♣2♥J♦︎Q♥K♠ - 2♣2♥6♥Q♥K♠ - 2♣2♥4♦︎Q♥K♠ - 2♣6♥J♦︎Q♥K♠ - 2♣4♦︎J♦︎Q♥K♠ -
   2♣4♦︎6♥Q♥K♠ - 2♥6♥J♦︎Q♥K♠ - 2♥4♦︎J♦︎Q♥K♠ - 2♥4♦︎6♥Q♥K♠ - 4♦︎6♥J♦︎Q♥K♠ -
   2♣2♥3♦︎J♦︎Q♥ - 2♣2♥3♦︎6♥Q♥ - 2♣2♥3♦︎4♦︎Q♥ - 2♣3♦︎6♥J♦︎Q♥ - 2♣3♦︎4♦︎J♦︎Q♥ -
   2♣3♦︎4♦︎6♥Q♥ - 2♥3♦︎6♥J♦︎Q♥ - 2♥3♦︎4♦︎J♦︎Q♥ - 2♥3♦︎4♦︎6♥Q♥ - 3♦︎4♦︎6♥J♦︎Q♥ -
   2♣2♥J♦︎Q♠K♥ - 2♣2♥6♥Q♠K♥ - 2♣2♥4♦︎Q♠K♥ - 2♣6♥J♦︎Q♠K♥ - 2♣4♦︎J♦︎Q♠K♥ -
   2♣4♦︎6♥Q♠K♥ - 2♥6♥J♦︎Q♠K♥ - 2♥4♦︎J♦︎Q♠K♥ - 2♥4♦︎6♥Q♠K♥ - 4♦︎6♥J♦︎Q♠K♥ -
   2♣2♥J♦︎K♥K♠ - 2♣2♥6♥K♥K♠ - 2♣2♥4♦︎K♥K♠ - 2♣6♥J♦︎K♥K♠ - 2♣4♦︎J♦︎K♥K♠ -
   2♣4♦︎6♥K♥K♠ - 2♥6♥J♦︎K♥K♠ - 2♥4♦︎J♦︎K♥K♠ - 2♥4♦︎6♥K♥K♠ - 4♦︎6♥J♦︎K♥K♠ -
   2♣2♥3♦︎J♦︎K♥ - 2♣2♥3♦︎6♥K♥ - 2♣2♥3♦︎4♦︎K♥ - 2♣3♦︎6♥J♦︎K♥ - 2♣3♦︎4♦︎J♦︎K♥ -
   2♣3♦︎4♦︎6♥K♥ - 2♥3♦︎6♥J♦︎K♥ - 2♥3♦︎4♦︎J♦︎K♥ - 2♥3♦︎4♦︎6♥K♥ - 3♦︎4♦︎6♥J♦︎K♥ -
   2♣2♥J♦︎Q♠K♠ - 2♣2♥6♥Q♠K♠ - 2♣2♥4♦︎Q♠K♠ - 2♣6♥J♦︎Q♠K♠ - 2♣4♦︎J♦︎Q♠K♠ -
   2♣4♦︎6♥Q♠K♠ - 2♥6♥J♦︎Q♠K♠ - 2♥4♦︎J♦︎Q♠K♠ - 2♥4♦︎6♥Q♠K♠ - 4♦︎6♥J♦︎Q♠K♠ -
   2♣2♥3♦︎J♦︎Q♠ - 2♣2♥3♦︎6♥Q♠ - 2♣2♥3♦︎4♦︎Q♠ - 2♣3♦︎6♥J♦︎Q♠ - 2♣3♦︎4♦︎J♦︎Q♠ -
   2♣3♦︎4♦︎6♥Q♠ - 2♥3♦︎6♥J♦︎Q♠ - 2♥3♦︎4♦︎J♦︎Q♠ - 2♥3♦︎4♦︎6♥Q♠ - 3♦︎4♦︎6♥J♦︎Q♠ -
   2♣2♥3♦︎J♦︎K♠ - 2♣2♥3♦︎6♥K♠ - 2♣2♥3♦︎4♦︎K♠ - 2♣3♦︎6♥J♦︎K♠ - 2♣3♦︎4♦︎J♦︎K♠ -
   2♣3♦︎4♦︎6♥K♠ - 2♥3♦︎6♥J♦︎K♠ - 2♥3♦︎4♦︎J♦︎K♠ - 2♥3♦︎4♦︎6♥K♠ - 3♦︎4♦︎6♥J♦︎K♠ -

Best Hand: 2♣2♥J♦︎K♥K♠ Rank: 3

Just wondering if there is a way to run the piece of code below more efficiently. I just started to get acquainted with parallel program and think this might be an answer? But I have no idea how to do it using imap or processes.
for h1 in range (0, 4):
  for h2 in range (h1+1, 5):
   for b1 in range (0, 3):
    for b2 in range (b1+1, 4):
     for b3 in range (b2+1, 5):
      hand_check = []
      hand_check.append(hand[m1])
      hand_check.append(hand[m2])
      hand_check.append(board[b1])
      hand_check.append(board[b2])
      hand_check.append(board[b3])



Use itertools for this kind of thing:

for h in itertools.combinations(hand, 2):
  for b in itertools.combinations(board, 3):
    hand_check = list(h) + list(b)


Also, I don't find hand_check to be a helpful name. In general I expect variable names to be nouns (unless they're Booleans, when predicates make sense), and when I parse hand_check as a noun I find that it's a check modified by hand. The smallest change would be to rename it hand_to_check; I prefer candidate_hand.




      if (hand_check[0].suit == hand_check[1].suit and hand_check[1].suit == hand_check[2].suit and hand_check[2].suit == hand_check[3].suit and hand_check[3].suit == hand_check[4].suit):



Ugh. Use set.

if len(set(card.suit for card in hand_check)) == 1:





      hand_check = sort(hand_check) #Custom sort for my array of objects
      hand_ranks = "".join([str(hand_check[0].rank),str(hand_check[1].rank),str(hand_check[2].rank),str(hand_check[3].rank),str(hand_check[4].rank)])






       control = [dictionary_A[hand_ranks][0],dictionary_A[hand_ranks][1]]
      else:
       control = [dictionary_B[hand_ranks][0],dictionary_B[hand_ranks][1]]



Firstly, why the quadruple-lookup? Why not

lookup_table = dictionary_A if only_one_suit else dictionaryB
control = lookup_table[hand_ranks]


?

Secondly, although Python's string manipulation is quite fast, since you're aiming to microoptimise you should think in terms of integers rather than strings. I'd delve into the fascinating world of perfect hashes to try to get a lookup table which fits into L2 cache, or maybe even L1 with luck (there are only 6188 multisets of 5 ranks, so less than 25 kB of lookup table suffices).




      if first_int:
       best_hand = hand_check
       rank = control
       first_int = False
      elif (int(control[0]) &gt; int(rank[0])):
       rank = control
       best_hand = hand_check       
      elif (int(control[0]) == int(rank[0])):
       if (int(control[1]) &gt; int(rank[1])):       
        rank = control
        best_hand = hand_check      



If you initialise rank to [-1] then you should be able to eliminate first_int and simplify this to

      if (control &gt; rank):
        rank = control
        best_hand = hand_check       




Now, I've addressed micro-optimisation above, but the first rule of optimisation is to start by looking at the algorithm.


  What I need to do is check all arrays formed by 2 items from hand and
  3 items from board, like combination = [Hn,Hm,Bi,Bj,Bk] (100
  combinations in total)
  
  Then I need to compare each one of the combinations against a
  dictionary


is wrong. What you need to do is find the set of five cards formed by 2 cards from the hand and 3 cards from the board which has the highest score.

I'd suggest that you micro-optimise and then benchmark against a major restructuring with the following outline:

for suit in the_four_suits:
  hand_mask = build_mask((card for card in hand if card.suit == suit))
  board_mask = build_mask((card for card in board is card.suit == suit))
  check_royal_flush(hand_mask, board_mask)
  check_straight_flush(hand_mask, board_mask)
  check_flush(hand_mask)

hand_counts = count_by_ranks(hand)
board_counts = count_by_ranks(board)
check_four_of_a_kind(hand_counts)
check_full_house(hand_counts)
...


The key ideas are to do everything with bit manipulation and to return as soon as you know that no further checks will produce a hand better than the best you've already found. For count_by_ranks, if you allocate three bits per rank then overflow into the next rank is impossible since there are only four suits. So e.g. to check for a four of a kind:

total_counts = hand_counts + board_counts
possible_fours = total_counts &amp; 0x4924924924
# possible_fours indicates ranks where we have all four cards between hand and board
# We take three cards from board, so rule out ranks where all four are in the board
possible_fours &amp;= ~board_counts
# Similarly, rule out ranks where all four are in hand
possible_fours &amp;= ~hand_counts
# And rule out ranks where three are in hand
possible_fours &amp;= ~((hand_counts &lt;&lt; 1) &amp; (hand_counts &lt;&lt; 2))
if possible_fours != 0:
  # We have a four-of-a-kind: just need to extract it