code: def get_bank_sizes(num_constraints: int,
                     beam_size: int,
                     candidate_counts: List[int]) -> List[int]:
      
  
      num_banks = num_constraints + 1
      bank_size = beam_size // num_banks
      remainder = beam_size - bank_size * num_banks
  
      # Distribute any remainder to the end
      assigned = [bank_size for x in range(num_banks)]
      assigned[-1] += remainder
  
      # Now, moving right to left, push extra allocation to earlier buckets.
      # This encodes a bias for higher buckets, but if no candidates are found, space
      # will be made in lower buckets. This may not be the best strategy, but it is important
      # that you start pushing from the bucket that is assigned the remainder, for cases where
      # num_constraints >= beam_size.
      for i in reversed(range(num_banks)):
          overfill = assigned[i] - candidate_counts[i]
          if overfill > 0:
              assigned[i] -= overfill
              assigned[(i - 1) % num_banks] += overfill
  
      return assigned
docs: Evenly distributes the beam across the banks, where each bank is a portion of the beam devoted
      to hypotheses having met the same number of constraints, 0..num_constraints.
      After the assignment, banks with more slots than candidates are adjusted.
  
      :param num_constraints: The number of constraints.
      :param beam_size: The beam size.
      :param candidate_counts: The empirical counts of number of candidates in each bank.
      :return: A distribution over banks.