Module metodos_ordenacao

Expand source code
#!/usr/bin/env python3
#coding=utf-8

 #####################################################
#                                                    #
#  author: Éder A. R. Marques                        #
#  email:  earmarques@gmail.com                      #
#  local:  São José do Rio Preto/SP Brazil           #
#  date:   28/10/2020                                #
#                                                    #
#  versioned for Python 2 from the Professor's       #
#  original C code.                                  #
#  ------------------------------------------------  #
#  professor: Carlos Magnus Carlson Filho            #
#  course:    Estruturas de Dados                    #
#  program:   Análise e Desenvolvimento de Sistemas  #
#  college:   Fatec Rio Preto                        #
#                                                    #
####################################################


import random
from datetime import datetime


# ==================================================================================================
# Cabeçalho     ------------------------------------------------------------------------------------

print("\n\n")
print("\033[1;{:_^80}".format("  Métodos de Ordenação  "))
print("\033[0;")


# ==================================================================================================
# Constantes     -----------------------------------------------------------------------------------

# Constantes para escolha do método de ordenação
BOLHA_DIRETO =              1
BOLHA_INVERTIDO =           2
INSERCAO =                  3
SELECAO  =                  4
MESCLAGEM_NAO_RECURSIVO =   5
MESCLAGEM_RECURSIVO =       6
QUICK_RECURSIVO =           7
QUICK_RECURSIVO_CAUDAL_1 = 71
QUICK_RECURSIVO_CAUDAL_2 = 72
MUDAR_METODO =              8
SAIR =                      9

# Constantes para escolha do tipo de teste do método de ordenção
FIXO =                 1
GRANDE_1_ALEATORIO =   2
GRANDE_2_ALEATORIO =   3
GRANDE_1_DECRESCENTE = 4
GRANDE_2_DECRESCENTE = 5


# ==================================================================================================
# Menus de Interação com o Usuário     -------------------------------------------------------------

# Menu 1 - Método de Ordenação     -----------------------------------------------------------------

def escolhe_metodo():

  """ Retorna o método escolhido para o teste.

  Apresenta uma menu de opções para que o usuário escolha o método de ordenação.

  Opções:
      1  - Bolha direto    (bubble sort)    - esquerda->direita
      2  - Bolha invertido (bubble sort)    - direita->esquerda
      3  - Inserção     (insertion sort)
      4  - Seleção      (selection sort)
      5  - Mesclagem    (merge sort)        - não recursivo
      6  - Mesclagem    (merge sort)        - recursivo
      7  - Partição     (quick sort)        - recursivo
      71 - Partição     (quick sort tail 1) - recursivo
      72 - Partição     (quick sort tail 2) - recursivo
      9  - Sair do programa

  Returns:
      int: A escolha do usuário.
  """

  escolha = None

  # Exibição das opções
  print('_{:-<47}'.format(''))
  print('{}'.format('Escolha do método de ordenação a ser executado:'))
  print('{:-<47}'.format(''))
  print('  1 - Bolha direto    (bubble sort)    - esquerda->direita')
  print('  2 - Bolha invertido (bubble sort)    - direita->esquerda')
  print('  3 - Inserção     (insertion sort)')
  print('  4 - Seleção      (selection sort)')
  print('  5 - Mesclagem    (merge sort)        - não recursivo')
  print('  6 - Mesclagem    (merge sort)        - recursivo')
  print('  7 - Partição     (quick sort)        - recursivo')
  print(' 71 - Partição     (quick sort tail 1) - recursivo')
  print(' 72 - Partição     (quick sort tail 2) - recursivo')
  print('  9 - Sair do programa')

  # Obtenção, via teclado, da escolha do usuário
  escolha = int(input('\nInforme a opção desejada: '))

  return escolha


# Menu 2 - Tipo de Teste de Carga - Benchmark     --------------------------------------------------

def escolhe_teste():

  """ Retorna o tipo de teste escolhido.

  Apresenta uma menu de opções para que o usuário escolha do tipo de teste de carga
  para fazer análise de desempenho do método de ordenação escolhido.

  Opões:
      1 - Vetor fixo para validação do método de ordenação
      2 - Vetor grande 1 (20000 elementos) preenchido aleatoriamente
      3 - Vetor grande 2 (40000 elementos) preenchido aleatoriamente
      4 - Vetor grande 1 (20000 elementos) preenchido com valores decrescentes
      5 - Vetor grande 2 (40000 elementos) preenchido com valores decrescentes
      8 - Voltar à escolha do método de ordenação
      9 - Sair do programa

  Returns:
      int: A escolha do usuário.
  """
  escolha = None;

  # Exibição das opções
  print('\n{:-<41}'.format(''))
  print('{}'.format('Escolha do tipo de teste a ser executado:'))
  print('{:-<41}'.format(''))

  print(' 1 - Vetor fixo para validação do método de ordenação')
  print(' 2 - Vetor grande 1 (20000 elementos) preenchido aleatoriamente')
  print(' 3 - Vetor grande 2 (40000 elementos) preenchido aleatoriamente')
  print(' 4 - Vetor grande 1 (20000 elementos) preenchido com valores decrescentes')
  print(' 5 - Vetor grande 2 (40000 elementos) preenchido com valores decrescentes')
  print(' 8 - Voltar à escolha do método de ordenação')
  print(' 9 - Sair do programa')

  # Obtenção, via teclado, da escolha do usuário
  escolha = int(input('\nInforme a opção desejada:'))

  return escolha


# ==================================================================================================
# Montagem dos Vetores de Testes     ---------------------------------------------------------------

# Vetor de Validação - 8 Elementos Aleatórios     --------------------------------------------------

def monta_vetor_FIXO(metodo_ordenacao):

  """ Procedimento que monta o VETOR FIXO e o imprime antes da ordenação.

  Args:
      metodo_ordenacao (str): O nome do método.

  Returns:
    list: Um vetor pequeno para validação dos métodos.
  """

  print('\n------------------------------------')
  print('Inicializando dados do vetor V1 para: {}'.format(metodo_ordenacao))
  print('------------------------------------')

  V1 = [89, 44, 75, 66, 11, 38, 93, 56]

  #V1 = [3,0,1,8,7,2,5,4,9,6]  # vetor da dança húngara: https://www.youtube.com/watch?v=ywWBy6J5gz8

  print('Valor de V1: {}'.format(V1))

  return V1


# 20.000 Elementos - Ordem Decrescente     ---------------------------------------------------------

def monta_vetor_GRANDE_1_DECRESCENTE(metodo_ordenacao):

  """ Procedimento que monta o VETOR GRANDE 1 e o preenche com números decrescentes.

  Args:
      metodo_ordenacao (str): O nome do método.

  Returns:
    list: Um vetor de 20.000 elementos em ordem descrescente.
  """

  print('\n------------------------------------')
  print('Inicializando dados do vetor T1 para: {}'.format(metodo_ordenacao))
  print('------------------------------------')

  T1=[]
  for i in range(20000):
    T1.append(20000 - i)

  print('\nVocê escolheu gerar vetores com números decrescentes !')
  print('Primeiro elemento de T1: {}'.format( T1[0] ))
  print('Último   elemento de T1: {}'.format( T1[19999] ))

  return T1


# 40.000 Elementos - Ordem Decrescente     ---------------------------------------------------------

def monta_vetor_GRANDE_2_DECRESCENTE(metodo_ordenacao):

  """ Procedimento que monta o VETOR GRANDE 2 e o preenche com números decrescentes.

  Args:
      metodo_ordenacao (str): O nome do método.

  Returns:
    list: Um vetor de 40.000 elementos em ordem descrescente.
  """
  print('\n------------------------------------')
  print('Inicializando dados do vetor T2 para: {}'.format(metodo_ordenacao))
  print('------------------------------------')

  T2=[]
  for i in range(40000):
    T2.append(40000 - i)

  print('\nVocê escolheu gerar vetores com números decrescentes !')
  print('Primeiro elemento de T2: {}'.format( T2[0] ))
  print('Último   elemento de T2: {}'.format( T2[39999] ))

  return T2


# 20.000 Elementos - Aleatórios     ----------------------------------------------------------------

def monta_vetor_GRANDE_1_ALEATORIO(metodo_ordenacao):

  """ Procedimento que monta o VETOR GRANDE 1 e o preenche com números aleatórios.

  Args:
      metodo_ordenacao (str): O nome do método.

  Returns:
    list: Um vetor de 20.000 elementos aleatórios.
  """
  print('\n------------------------------------')
  print('Inicializando dados do vetor T1 para: {}'.format(metodo_ordenacao))
  print('------------------------------------')

  T1=[]
  for i in range(20000):
    T1.append( random.randint(1, 20000) )

  print('\nVocê escolheu gerar vetores com números aleatórios !')
  print('Primeiro elemento de T1: {}'.format( T1[0] ))
  print('Último   elemento de T1: {}'.format( T1[19999] ))

  return T1


# 40.000 Elementos - Aleatórios     ----------------------------------------------------------------

def monta_vetor_GRANDE_2_ALEATORIO(metodo_ordenacao):

  """ Procedimento que monta o VETOR GRANDE 2 e o preenche com números aleatórios.

  Args:
      metodo_ordenacao (str): O nome do método.

  Returns:
    list: Um vetor de 40.000 elementos aleatórios.
  """
  print('\n------------------------------------')
  print('Inicializando dados do vetor T2 para: {}'.format(metodo_ordenacao))
  print('------------------------------------')

  T2=[]
  for i in range(40000):
    T2.append( random.randint(1, 40000) )

  print('\nVocê escolheu gerar vetores com números aleatórios !')
  print('Primeiro elemento de T2: {}'.format( T2[0] ))
  print('Último   elemento de T2: {}'.format( T2[39999] ))

  return T2


# ==================================================================================================
# Métodos de Ordenação     -------------------------------------------------------------------------

# Bubble Sort     ----------------------------------------------------------------------------------

def bubble_sort(vetor, imprimir=False):

  """ Método da BOLHA - Bubble Sort - Percurso Esquerda -> Direita.

  Args:
      vetor (list): Vetor a ser ordenado.
      n (int): Tamanho da porção do vetor que ainda não está ordenada.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: O vetor fornecido como parâmetro ordenado.
  """
  # Auxiliar para interromper o loop - se não houver troca de posição o vetor está ordenado
  trocou = True        # inicia com true só para entrar no loop
  n = len(vetor)       # número de elementos no vetor
  cursor = n           # posição do cursor, inicialmente após o último elemento

  # loop de passos
  while trocou:
    # reinicializa porque se supõe que neste passo ainda não houve troca
    trocou = False
    # recua o cursor em uma posição
    cursor -= 1

    if imprimir: print('Passo {}'.format(n - cursor))

    # loop de trocas dentro do passo, até o cursor
    for i in range(cursor):

      if vetor[i] > vetor[i + 1]:

        # troca a posição dos dois elementos adjacentes
        temp = vetor[i]
        vetor[i] = vetor[i + 1]
        vetor[i + 1] = temp
        trocou = True

    if imprimir: print('Valor de V1: {}'.format(vetor))

  # retorna vetor ordenado
  return vetor


# Bubble Sort Invertido     ------------------------------------------------------------------------

def bubble_sort_invertido(vetor, imprimir=False):

  """ Método da BOLHA - Bubble Sort - Percurso Direita -> Esquerda.

  Args:
      vetor (list): Vetor a ser ordenado.
      n (int): Tamanho da porção do vetor que ainda não está ordenada.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: O vetor fornecido como parâmetro ordenado.
  """
  # Auxiliar para interromper o loop - se não houver troca de posição o vetor está ordenado
  trocou = True       # inicia com true só para entrar no loop
  n = len(vetor)      # número de elementos no vetor
  cursor = -1          # posição do cursor, inicialmente antes do primeiro elemento

  # loop de passos
  while trocou:
   # reinicializa porque se supõe que neste passo ainda não houve troca
   trocou = False
   # avança o cursor em uma posição
   cursor += 1

   if imprimir: print('Passo {}'.format(cursor + 1))

   # loop de trocas dentro do passo, até o cursor
   for i in range(n - 1, cursor, -1):

     if vetor[i] < vetor[i - 1]:

       # troca a posição dos dois elementos adjacentes
       temp = vetor[i]
       vetor[i] = vetor[i - 1]
       vetor[i - 1] = temp
       trocou = True

   if imprimir: print('Valor de V1: {}'.format(vetor))

  # retorna vetor ordenado
  return vetor


# Insertion Sort     -----------------------------------------------------------------------------

def insertion_sort(vetor, imprimir=False):

  """ Método da INSERÇÃO - Insertion Sort.

  Args:
      vetor (list): Vetor a ser ordenado.
      n (int): Tamanho da porção do vetor que ainda não está ordenada.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: O vetor fornecido como parâmetro ordenado.
  """

  n = len(vetor)      # número de elementos no vetor
  cursor = 0           # posição do cursor, inicialmente no primeiro elemento

  # loop de passos
  for passo in range(1, n):

   if imprimir: print('Passo {}'.format(passo))

   # avança o cursor em uma posição
   cursor += 1;

   # Corro todos os elementos do fim para trás até a posição do cursor,
   # se o vetor[cursor] for menor que o antecessor, troco de posição e
   # comparo com anterior seguinte, levando o elemento (se menor) até a sua posição correta
   for i in range(cursor, 0, -1):
     # do cursor pra trás, todos os elementos já estão em ordem crescente,
     # se o elemento for maior que seu antecessor ele já está na posição correta,
     # posso interromper o loop e ir ao próximo elemento
     if vetor[i - 1] < vetor[i]:
       break

     # troca a posição dos dois elementos adjacentes
     temp = vetor[i]
     vetor[i] = vetor[i - 1]
     vetor[i - 1] = temp

   if imprimir: print('Valor de V1: {}'.format(vetor))

  # retorna vetor ordenado
  return vetor


# Selection Sort     -------------------------------------------------------------------------------

def selection_sort(vetor, imprimir=False):

  """ Método da SELEÇÃO - Selection Sort.

  Args:
      vetor (list): Vetor a ser ordenado.
      n (int): Tamanho da porção do vetor que ainda não está ordenada.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: O vetor fornecido como parâmetro ordenado.
  """
  # Variáveis auxiliares
  indice_menor = -1
  n = len(vetor)      # número de elementos no vetor
  cursor = -1          # posição do cursor, inicialmente antes do primeiro elemento


  # loop de passos
  for passo in range(1, n):

    if imprimir: print('Passo {}'.format(passo))

    # avança o cursor em uma posição
    cursor += 1;
    # encontra o menor valor dentre os elementos não ordenados
    menor = vetor[cursor]
    indice_menor = cursor
    for i in range(cursor + 1, n):
      if vetor[i] < menor:
        menor = vetor[i]
        indice_menor = i

    # troca o menor com o elemento ao lado direito do cursor
    vetor[indice_menor] = vetor[cursor]
    vetor[cursor] = menor

    if imprimir: print('Valor de V1: {}'.format(vetor))

  # retorna vetor ordenado
  return vetor


# Merge Sort    ------------------------------------------------------------------------------------

def merge_sort(metade_esquerda, metade_direita, imprimir=False):

  """ Método da MESCLAGEM - Merge Sort.

  Por não ser o recursivo e ordena as duas metades primeiramente para depois mesclar
  os elementos em ordem crescente.

  Args:
      metade_esquerda (list): primeira metade
      metade_direita (list): segunda metade
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: Um vetor ordenado.
  """
  i = j = passo = 0
  tamanho_metade_esquerda = len(metade_esquerda)
  tamanho_metade_direita  = len(metade_direita)

  # Para garantir que as duas metades estejam ordenadas, passamos os vetores pelo Bubble Sort.
  # Se estiverem previamente ordenadas, o método bubble sort vai descobrir em apenas uma
  # passada pelo vetor e retornará rapidamente.
  metade_esquerda = bubble_sort(metade_esquerda, imprimir)
  metade_direita  = bubble_sort(metade_direita,  imprimir)

  # Cria o vetor que receberá os elementos ordenados
  vetor = list(metade_esquerda)
  vetor.extend(metade_direita)


  # Compara as duas metades sequencialmentes e adiciona no vetor de forma crescente.
  # OBS IMPORTANTE|=> as duas metades precisam já estarem ordenadas

  while passo < (tamanho_metade_esquerda + tamanho_metade_direita):

    if imprimir: print('Passo {}'.format(passo + 1))

    # Comparo os elementos de ambos os lados
    if metade_esquerda[i] <= metade_direita[j]:
      # guardo o de menor valor que está do lado esquerdo
      vetor[passo] = metade_esquerda[i]
      # ando pra frente no vetor do lado esquerdo
      i += 1
      # se já terminei de fazer comparações com a primeira metade, do lado esquerdo
      if i == tamanho_metade_esquerda:
        # copio o resto dos elementos da segunda metade do lado direito
        while j < tamanho_metade_direita:
          passo += 1
          vetor[passo] = metade_direita[j]
          j += 1
    else:
      # guardo o de menor valor que está do lado direito
      vetor[passo] = metade_direita[j]
      # ando pra frente no vetor do lado direito
      j += 1
      # se já terminei a segunda metade, do lado direito
      if j == tamanho_metade_direita:
        # copio os elementos restantes da metade esquerda
        while i < tamanho_metade_esquerda:
          passo += 1
          vetor[passo] = metade_esquerda[i]
          i += 1

    passo += 1

    if imprimir: print('Valor de V1: {}'.format(vetor))

  # retorna vetor ordenado
  return vetor


# Merge Sort - Serviço Completo     ----------------------------------------------------------------

def merge_sort_full_service(vetor, imprimir=False):

  """ Método da MESCLAGEM - Merge Sort.

  Divide o vetor em duas metades e invoca o #merge_sort() para fazer efetivamente a ordenação.

  Args:
      vetor (list): Vetor que será ordenado.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: Um novo vetor ordenado.
  """
  tamanho = len(vetor)
  meio = int(tamanho / 2)
  metade_esquerda = vetor[:meio]
  metade_direita  = vetor[meio:tamanho]

  if imprimir:
    print('metade_esquerda: {}'.format(metade_esquerda))
    print('metade_direita:  {}'.format(metade_direita))

  # retorna um novo vetor ordenado
  return merge_sort(metade_esquerda, metade_direita, imprimir)


# Merge Sort Recursivo     -------------------------------------------------------------------------

def merge_sort_recursivo(vetor, imprimir=False):

  """ Método da MESCLAGEM - Merge Sort - Versão Recursiva.

  Divide o vetor em duas metades e invoca o merge_sort recursivamente para fazer efetivamente a ordenação.

  Args:
      vetor (list): Vetor que será ordenado.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: O vetor ordenado.
  """

  if imprimir: print('ENTRADA - Elementos do vetor: {}'.format(vetor))

  tamanho = len(vetor)

  # CASO BASE: tamanho == 1 (nada a fazer)
  if tamanho == 1:
    return vetor

  # RECURSÃO

  meio = int(tamanho / 2)
  metade_esquerda = vetor[:meio]
  metade_direita  = vetor[meio:tamanho]

  tamanho_metade_esquerda = len(metade_esquerda)
  tamanho_metade_direita = len(metade_direita)


  if imprimir:
    print('ENTRADA - Elementos da metade esquerda: {}'.format(metade_esquerda))
    print('ENTRADA - Elementos da metade direita:  {}\n'.format(metade_direita))


  # Recursividade - Aciona a ordenação recursiva para cada metade

  metade_esquerda = merge_sort_recursivo(metade_esquerda, imprimir)
  metade_direita = merge_sort_recursivo(metade_direita, imprimir)


  # Mescla as metades, reconstruindo o vetor ordenadamente

  i = j = passo = 0

  while passo < tamanho:

    if imprimir: print('Passo {}'.format(passo + 1))

    # Comparo os elementos de ambos os lados
    if metade_esquerda[i] <= metade_direita[j]:
      # guardo o de menor valor que está do lado esquerdo
      vetor[passo] = metade_esquerda[i]
      # ando pra frente no vetor do lado esquerdo
      i += 1
      # se já terminei de fazer comparações com a primeira metade do lado esquerdo
      if i == tamanho_metade_esquerda:
        # copio o resto dos elementos da segunda metade do lado direito que já estão ordenados
        while j < tamanho_metade_direita:
          passo += 1
          vetor[passo] = metade_direita[j]
          j += 1
    else:
      # guardo o de menor valor que está do lado direito
      vetor[passo] = metade_direita[j]
      # ando pra frente no vetor do lado direito
      j += 1
      # Se já terminei a segunda metade, é porque já copie todos os elementos do lado direito
      # para o vetor final ordenado e todos os demais restantes do lado esquerdo são valores maiores.
      # Não há mais nenhum elemento do lado direito que seja menor que do lado esquerdo.
      if j == tamanho_metade_direita:
        # copio os elementos restantes da metade esquerda
        while i < tamanho_metade_esquerda:
          passo += 1
          vetor[passo] = metade_esquerda[i]
          i += 1

    passo += 1

  if imprimir: print('\nSAÍDA - Elementos do vetor: {}\n'.format(vetor))

  # retorna o vetor ordenado
  return vetor


# Particionador do Quick Sort     ------------------------------------------------------------------

def particionar(vetor, indice_inicial, indice_final, imprimir=False):

  """ Particionador do Quick Sort

  Pega o elemento(pivô) do vetor cujo indice ou posição é dado por 'indice_inicial', ``vetor[indice_inicial]``,
  e encontra a posição correta do elemento e retorna o índice referente a posição correta.
  Chamaremos este elemento que estamos procurando sua posição simplesmente de 'elemento_pivo_investigado'.

  O algoritmo usa dois cursores para correr o vetor no intevalo dos índices [inicial ao final(inclusive)].
  O cursor da extremidade esquerda avança para a direita até encontrar o elemento subsequente que seja
  maior que o elemento_pivo_investigado. O cursor da direita recua para a esquerda até encontrar
  um elemento que seja menor que o elemento_pivo_investigado. Nessa situação, teremos:

      elemento_esquerda > elemento_pivo_investigado > elemento_direita, logo,
      elemento_esquerda > elemento_direita

  Isto é, está fora de ordem, e o algoritmo já os troca de posição ordenando-os, depois prossegue
  em direção ao centro até que os cursores volantes se encontrarem.

  A condição de parada ocorre quando o cursor da esquerda atropela o da direita, ficando com um valor maior.
  O cursor da direita estará com o índice da posição correta, sendo ele o valor retornado pela função.

  Sinteticamente expresso de uma maneira formal matemática seria:

      Recebe vetor v[p..r] com p < r. Rearranja os elementos do vetor e
      devolve j em p..r tal que v[p..j-1] <= v[j] < v[j+1..r].

  Args:
      vetor (list): Vetor que será ordenado e particionado logicamente.
      indice_inical(int): Índice inicial do intervalo de pesquisa e que determina o elemento pivô cuja posição
      correta será retornada como a posição do pivô do particionamento.
      indice_final(int): Índice final do intervalo de pesquisa.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    int: A posição do pivô do particionamento do vetor.
  """
  elemento_pivo = vetor[indice_inicial]    # ..a ser investigado qual o seu posicionamento correto no vetor
  cursor_esquerda = indice_inicial         # posicionado no primeiro elemento
  cursor_direita = indice_final            # posicionado no último elemento
  posicao_correta_pivo = -1

  # A missão primária é encontrar a #posicao_correta_pivo dentro do vetor, mas durante o processo
  # já vai ordenando, trocando de posição alguns elementos; os que são menores que o pivô e estejam
  # a direita são trocados com elementos que são maiores que o pivô, mas que estão do lado esquerdo.

  while cursor_esquerda <= cursor_direita:

    # Encontra o elemento subsequente que seja maior que o elemento pivô investigado
    if vetor[cursor_esquerda] <= elemento_pivo:
      cursor_esquerda += 1          # avança

    # Encontra, do fim para o começo, o elemento que seja menor que o elemento pivô investigado
    elif vetor[cursor_direita] > elemento_pivo:
      cursor_direita -= 1           # recua

    # |=> vetor[cursor_esquerda] >= elemento_pivo > vetor[cursor_direita] =>
    # logo, vetor[cursor_esquerda] > vetor[cursor_direita], isto é, está fora da ordem crescente,
    # o menor tem de ficar a esquerda e o maior a direita, vamos trocá-los de posição
    else:
      temp = vetor[cursor_esquerda]
      vetor[cursor_esquerda] = vetor[cursor_direita]
      vetor[cursor_direita] = temp
      # A posição correta do elemento pivô investigado está entre os cursores; continuando a procura
      cursor_esquerda += 1     # avança uma posição
      cursor_direita  -= 1     # recua  uma posição

  # A posição correta do elemento pivô é dado pelo cursor_direita
  posicao_correta_pivo = cursor_direita

  # Vamos trocar de posição, fazendo backup do elemento que está ocupando o lugar
  # do nosso elemento pivô investigado para o lugar do pivô (início do vetor)
  vetor[indice_inicial] = vetor[posicao_correta_pivo]
  vetor[posicao_correta_pivo] = elemento_pivo       # copia o elemento pivô para sua posição correta

  if imprimir: print('\nValor de V1: {}'.format(vetor))


  return posicao_correta_pivo


# Quick Sort Recursivo     -------------------------------------------------------------------------

def quick_sort(vetor, indice_inicial, indice_final, imprimir=False):

  """ Quick Sort - Recursão comum.

  Recebe um vetor vetor[indice_inicial..indice_final], com indice_inicial <= indice_final + 1,
  e rearranja o vetor em ordem crescente.

  Args:
      vetor (list): Vetor que será ordenado.
      indice_inicial(int): Índice inicial do intervalo de pesquisa.
      indice_final(int): Índice final do intervalo de pesquisa.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    None: Nada retorna. Opera diretamente com a referência do vetor.
  """
  posicao_pivo_particao = -1

  if indice_inicial < indice_final:    # indices
    posicao_pivo_particao = particionar(vetor, indice_inicial, indice_final, imprimir)

    # partição esquerda
    quick_sort(vetor, indice_inicial, posicao_pivo_particao - 1, imprimir)
    # partição direita
    quick_sort(vetor, posicao_pivo_particao + 1, indice_final, imprimir)


# Quick Sort Recursivo Caudal - Versão 1     -------------------------------------------------------

def quick_sort_tail_1(vetor, indice_inicial, indice_final, imprimir=False):

  """ Quick Sort Recursivo Caudal - Versão 1

  A recursividade ocorre em uma partição de cada vez.

  Args:
      vetor (list): Vetor que será ordenado.
      indice_inicial(int): Índice inicial do intervalo de pesquisa.
      indice_final(int): Índice final do intervalo de pesquisa.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    None: Nada retorna. Opera diretamente com a referência do vetor.
  """

  posicao_pivo_particao = -1

  while indice_inicial < indice_final:

    posicao_pivo_particao = particionar(vetor, indice_inicial, indice_final, imprimir)

    # Ordena SEPARADAMENTE elementos 'antes' e 'depois' da partição
    quick_sort_tail_1(vetor, indice_inicial, posicao_pivo_particao - 1, imprimir)

    indice_inicial = posicao_pivo_particao + 1


# Quick Sort Recursivo Caudal - Versão 2     -------------------------------------------------------

def quick_sort_tail_2(vetor, indice_inicial, indice_final, imprimir=False):

  """ Quick Sort Recursivo Caudal - Versão 2

  Melhor otimização em relação a memória, pois a recursividade ocorre em apenas uma partição, e esta
  partição é a menor, fazendo com que a profundidade das chamadas recursivas seja mais rasa.

  Requer espaço auxiliar O(Log n) no pior caso.

  Args:
      vetor (list): Vetor que será ordenado.
      indice_inicial(int): Índice inicial do intervalo de pesquisa.
      indice_final(int): Índice final do intervalo de pesquisa.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    None: Nada retorna. Opera diretamente com a referência do vetor.


  .. _See below link for complete running code:
      https://ide.geeksforgeeks.org/qrlM31
  """
  posicao_pivo_particao = -1

  while indice_inicial < indice_final:

    posicao_pivo_particao = particionar(vetor, indice_inicial, indice_final, imprimir)

    # Se parte à esquerda do pivô é menor, tratá-la recursivamente
    # e lidar com a parte à dreita iterativamente 'while'
    parte_esquerda_eh_menor = posicao_pivo_particao - indice_inicial < indice_final - posicao_pivo_particao

    if parte_esquerda_eh_menor:
      quick_sort_tail_2(vetor, indice_inicial, posicao_pivo_particao - 1, imprimir)

      indice_inicial = posicao_pivo_particao + 1

    # Caso contrário: recursão à direita, iteração à esquerda
    else:
      quick_sort_tail_2(vetor, posicao_pivo_particao + 1, indice_final, imprimir)

      indice_final = posicao_pivo_particao - 1



# ==================================================================================================
# Método main() - Execução de Testes     -----------------------------------------------------------

def main():

  """ Método main() - Execução de Testes """

  # Dicionário cujas chaves representam o método de ordenação escolhido
  metodos_ordenacao = {
    1 : 'BOLHA (esquerda->direita)',
    2 : 'BOLHA (direita->esquerda)',
    3 : 'INSERCAO',
    4 : 'SELECAO',
    5 : 'MESCLAGEM (nao recursivo)',
    6 : 'MESCLAGEM (recursivo)',
    7 : 'QUICKSORT (recursivo)',
    71: 'QUICK_RECURSIVO_CAUDAL_1',
    72: 'QUICK_RECURSIVO_CAUDAL_2'

  }
  # Dicionário cujas chaves representam o tipo de teste de carga escolhido
  tipos_teste = {
    1 : 'FIXO',
    2 : 'GRANDE_1_ALEATORIO',
    3 : 'GRANDE_2_ALEATORIO',
    4 : 'GRANDE_1_DECRESCENTE',
    5 : 'GRANDE_2_DECRESCENTE'
  }

  # Montando as opções dos menus
  opcoes_menu_metodo_ordenacao = dict(metodos_ordenacao)      # cópia
  opcoes_menu_metodo_ordenacao.update( { 9 : 'SAIR'} )

  opcoes_menu_tipo_teste = dict(tipos_teste)
  opcoes_menu_tipo_teste.update({
    8 : 'MUDAR_METODO',
    9 : 'SAIR'
  })


  # Armazenar as escolhas realizadas pelo usuário
  escolhas = {'metodo' : -1, 'teste' : -1}

  # OBS|=> Em Python 2.x não é possível atribuir novos valores a variáveis declaradas
  # em um escopo superior; ao tentar fazê-lo o que ocorre é declarar uma nova variável local
  # de mesmo nome. A forma que escolhemos para contornar o problema foi declarar um dicionário
  # e alterar o valor associado a chave. Escopo interno não pode fazer atribuição à variáveis
  # não locais, mas pode modificar objetos (dict) que variáveis não locais se referem.
  #
  # Os dicionários main#escolhas e o acionar_teste#vetor foram utilizados com essa finalidade.
  # Python 3 resolveu esse incoveniente sem ferir o princípio pythônico do -
  # "explicit is better than implicit", acrescentando as palavras chaves: global e nonlocal.


  # Executar Testes a Partir das Escolhas     --------------------------------------------------------

  # Esta função é utilizada meramente para fazer a interação com o usuário, logo,
  # pertence a camada de apresentação e não deve fazer parte do módulo.
  # Em função do exposto, evitamos a exposição da função #acionar_teste() fazendo
  # sua definição dentro do próprio contexto do método #main().

  def acionar_teste(escolhas):

    vetor = {'vetor': None}


    # Carregamento do Vetor ------------------------------------------------------------------------

    if escolhas['teste'] == FIXO:
      print('\nEscolheu teste VETOR {} !'.format( tipos_teste[escolhas['teste']] ))
      vetor['vetor'] = monta_vetor_FIXO( metodos_ordenacao[escolhas['metodo']] )

    elif escolhas['teste'] == GRANDE_1_ALEATORIO:
      print('\nEscolheu teste VETOR {} !'.format( tipos_teste[escolhas['teste']] ))
      vetor['vetor'] = monta_vetor_GRANDE_1_ALEATORIO( metodos_ordenacao[escolhas['metodo']] )

    elif escolhas['teste'] == GRANDE_1_DECRESCENTE:
      print('\nEscolheu teste VETOR {} !'.format( tipos_teste[escolhas['teste']] ))
      vetor['vetor'] = monta_vetor_GRANDE_1_DECRESCENTE( metodos_ordenacao[escolhas['metodo']] )

    elif escolhas['teste'] == GRANDE_2_ALEATORIO:
      print('\nEscolheu teste VETOR {} !'.format( tipos_teste[escolhas['teste']] ))
      vetor['vetor'] = monta_vetor_GRANDE_2_ALEATORIO( metodos_ordenacao[escolhas['metodo']] )

    elif escolhas['teste'] == GRANDE_2_DECRESCENTE:
      print('\nEscolheu teste VETOR {} !'.format( tipos_teste[escolhas['teste']] ))
      vetor['vetor'] = monta_vetor_GRANDE_2_DECRESCENTE( metodos_ordenacao[escolhas['metodo']] )


    # único teste que imprime os passos da ordenação
    eh_teste_fixo = escolhas['teste'] == FIXO


    # Executando os Testes - Benchmark  ------------------------------------------------------------

    # Ordenação por BOLHA (esquerda->direita)

    if escolhas['metodo'] == BOLHA_DIRETO:
      if eh_teste_fixo:
        bubble_sort(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        bubble_sort(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por BOLHA (direita->esquerda)

    elif escolhas['metodo'] == BOLHA_INVERTIDO:
      if eh_teste_fixo:
        bubble_sort_invertido(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        bubble_sort_invertido(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por INSERÇÃO

    elif escolhas['metodo'] == INSERCAO:
      if eh_teste_fixo:
        insertion_sort(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        insertion_sort(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por SELEÇÃO

    elif escolhas['metodo'] == SELECAO:
      if eh_teste_fixo:
        selection_sort(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        selection_sort(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por MESCLAGEM NÃO RECURSIVO

    elif escolhas['metodo'] == MESCLAGEM_NAO_RECURSIVO:
      if eh_teste_fixo:
        merge_sort_full_service(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        merge_sort_full_service(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por MESCLAGEM RECURSIVO

    elif escolhas['metodo'] == MESCLAGEM_RECURSIVO:
      if eh_teste_fixo:
        merge_sort_recursivo(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        merge_sort_recursivo(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por QUICKSORT (PARTIÇÃO)

    elif escolhas['metodo'] == QUICK_RECURSIVO:
      vetor = vetor['vetor']
      indice_inicial = 0
      indice_final = len(vetor) - 1
      if eh_teste_fixo:
        quick_sort(vetor, indice_inicial, indice_final, True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        quick_sort(vetor, indice_inicial, indice_final)
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por QUICKSORT CAUDAL 1 (PARTIÇÃO)

    elif escolhas['metodo'] == QUICK_RECURSIVO_CAUDAL_1:
      vetor = vetor['vetor']
      indice_inicial = 0
      indice_final = len(vetor) - 1
      if eh_teste_fixo:
        quick_sort_tail_1(vetor, indice_inicial, indice_final, True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        quick_sort_tail_1(vetor, indice_inicial, indice_final)
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por QUICKSORT CAUDAL 2 (PARTIÇÃO)

    elif escolhas['metodo'] == QUICK_RECURSIVO_CAUDAL_2:
      vetor = vetor['vetor']
      indice_inicial = 0
      indice_final = len(vetor) - 1
      if eh_teste_fixo:
        quick_sort_tail_2(vetor, indice_inicial, indice_final, True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        quick_sort_tail_2(vetor, indice_inicial, indice_final)
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )


  # Loop Principal do Programa    ------------------------------------------------------------------
  # Interação com o usuário através do menu de opções

  while True:
    # Escolha do método de ordenação a ser executado
    escolhas['metodo'] = escolhe_metodo()

    if escolhas['metodo'] not in opcoes_menu_metodo_ordenacao:
      print('\nOpção INVÁLIDA !  Tente novamente...\n')

    elif not escolhas['metodo'] == SAIR:
      while True:
        # Escolha do tipo de teste a ser executado
        escolhas['teste'] = escolhe_teste()

        if escolhas['teste'] == MUDAR_METODO:
          print('\nEscolheu voltar à escolha do método de ordenação !')
          break

        elif escolhas['teste'] == SAIR:
          print('\nEscolheu SAIR !\n\n')
          escolhas['metodo'] = SAIR   # deixar o loop externo saber da intenção
          break

        elif escolhas['teste'] not in opcoes_menu_tipo_teste:
          print('\nOpção INVÁLIDA !  Tente novamente...\n')

        else:
          acionar_teste(escolhas)

    # Se pediu pra sair no loop interno, obedeça e saia da execução do programa
    if escolhas['metodo'] == SAIR:
      break

# Boa educação
if __name__ == '__main__':
  main()

Functions

def bubble_sort(vetor, imprimir=False)

Método da BOLHA - Bubble Sort - Percurso Esquerda -> Direita.

Args

vetor : list
Vetor a ser ordenado.
n : int
Tamanho da porção do vetor que ainda não está ordenada.
imprimir : bool, optional
Chave para imprimir ou não os passos intermediários.

Defaults to False.

Returns

list
O vetor fornecido como parâmetro ordenado.
Expand source code
def bubble_sort(vetor, imprimir=False):

  """ Método da BOLHA - Bubble Sort - Percurso Esquerda -> Direita.

  Args:
      vetor (list): Vetor a ser ordenado.
      n (int): Tamanho da porção do vetor que ainda não está ordenada.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: O vetor fornecido como parâmetro ordenado.
  """
  # Auxiliar para interromper o loop - se não houver troca de posição o vetor está ordenado
  trocou = True        # inicia com true só para entrar no loop
  n = len(vetor)       # número de elementos no vetor
  cursor = n           # posição do cursor, inicialmente após o último elemento

  # loop de passos
  while trocou:
    # reinicializa porque se supõe que neste passo ainda não houve troca
    trocou = False
    # recua o cursor em uma posição
    cursor -= 1

    if imprimir: print('Passo {}'.format(n - cursor))

    # loop de trocas dentro do passo, até o cursor
    for i in range(cursor):

      if vetor[i] > vetor[i + 1]:

        # troca a posição dos dois elementos adjacentes
        temp = vetor[i]
        vetor[i] = vetor[i + 1]
        vetor[i + 1] = temp
        trocou = True

    if imprimir: print('Valor de V1: {}'.format(vetor))

  # retorna vetor ordenado
  return vetor
def bubble_sort_invertido(vetor, imprimir=False)

Método da BOLHA - Bubble Sort - Percurso Direita -> Esquerda.

Args

vetor : list
Vetor a ser ordenado.
n : int
Tamanho da porção do vetor que ainda não está ordenada.
imprimir : bool, optional
Chave para imprimir ou não os passos intermediários.

Defaults to False.

Returns

list
O vetor fornecido como parâmetro ordenado.
Expand source code
def bubble_sort_invertido(vetor, imprimir=False):

  """ Método da BOLHA - Bubble Sort - Percurso Direita -> Esquerda.

  Args:
      vetor (list): Vetor a ser ordenado.
      n (int): Tamanho da porção do vetor que ainda não está ordenada.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: O vetor fornecido como parâmetro ordenado.
  """
  # Auxiliar para interromper o loop - se não houver troca de posição o vetor está ordenado
  trocou = True       # inicia com true só para entrar no loop
  n = len(vetor)      # número de elementos no vetor
  cursor = -1          # posição do cursor, inicialmente antes do primeiro elemento

  # loop de passos
  while trocou:
   # reinicializa porque se supõe que neste passo ainda não houve troca
   trocou = False
   # avança o cursor em uma posição
   cursor += 1

   if imprimir: print('Passo {}'.format(cursor + 1))

   # loop de trocas dentro do passo, até o cursor
   for i in range(n - 1, cursor, -1):

     if vetor[i] < vetor[i - 1]:

       # troca a posição dos dois elementos adjacentes
       temp = vetor[i]
       vetor[i] = vetor[i - 1]
       vetor[i - 1] = temp
       trocou = True

   if imprimir: print('Valor de V1: {}'.format(vetor))

  # retorna vetor ordenado
  return vetor
def escolhe_metodo()

Retorna o método escolhido para o teste.

Apresenta uma menu de opções para que o usuário escolha o método de ordenação.

Opções

1 - Bolha direto (bubble sort) - esquerda->direita 2 - Bolha invertido (bubble sort) - direita->esquerda 3 - Inserção (insertion sort) 4 - Seleção (selection sort) 5 - Mesclagem (merge sort) - não recursivo 6 - Mesclagem (merge sort) - recursivo 7 - Partição (quick sort) - recursivo 71 - Partição (quick sort tail 1) - recursivo 72 - Partição (quick sort tail 2) - recursivo 9 - Sair do programa

Returns

int
A escolha do usuário.
Expand source code
def escolhe_metodo():

  """ Retorna o método escolhido para o teste.

  Apresenta uma menu de opções para que o usuário escolha o método de ordenação.

  Opções:
      1  - Bolha direto    (bubble sort)    - esquerda->direita
      2  - Bolha invertido (bubble sort)    - direita->esquerda
      3  - Inserção     (insertion sort)
      4  - Seleção      (selection sort)
      5  - Mesclagem    (merge sort)        - não recursivo
      6  - Mesclagem    (merge sort)        - recursivo
      7  - Partição     (quick sort)        - recursivo
      71 - Partição     (quick sort tail 1) - recursivo
      72 - Partição     (quick sort tail 2) - recursivo
      9  - Sair do programa

  Returns:
      int: A escolha do usuário.
  """

  escolha = None

  # Exibição das opções
  print('_{:-<47}'.format(''))
  print('{}'.format('Escolha do método de ordenação a ser executado:'))
  print('{:-<47}'.format(''))
  print('  1 - Bolha direto    (bubble sort)    - esquerda->direita')
  print('  2 - Bolha invertido (bubble sort)    - direita->esquerda')
  print('  3 - Inserção     (insertion sort)')
  print('  4 - Seleção      (selection sort)')
  print('  5 - Mesclagem    (merge sort)        - não recursivo')
  print('  6 - Mesclagem    (merge sort)        - recursivo')
  print('  7 - Partição     (quick sort)        - recursivo')
  print(' 71 - Partição     (quick sort tail 1) - recursivo')
  print(' 72 - Partição     (quick sort tail 2) - recursivo')
  print('  9 - Sair do programa')

  # Obtenção, via teclado, da escolha do usuário
  escolha = int(input('\nInforme a opção desejada: '))

  return escolha
def escolhe_teste()

Retorna o tipo de teste escolhido.

Apresenta uma menu de opções para que o usuário escolha do tipo de teste de carga para fazer análise de desempenho do método de ordenação escolhido.

Opões

1 - Vetor fixo para validação do método de ordenação 2 - Vetor grande 1 (20000 elementos) preenchido aleatoriamente 3 - Vetor grande 2 (40000 elementos) preenchido aleatoriamente 4 - Vetor grande 1 (20000 elementos) preenchido com valores decrescentes 5 - Vetor grande 2 (40000 elementos) preenchido com valores decrescentes 8 - Voltar à escolha do método de ordenação 9 - Sair do programa

Returns

int
A escolha do usuário.
Expand source code
def escolhe_teste():

  """ Retorna o tipo de teste escolhido.

  Apresenta uma menu de opções para que o usuário escolha do tipo de teste de carga
  para fazer análise de desempenho do método de ordenação escolhido.

  Opões:
      1 - Vetor fixo para validação do método de ordenação
      2 - Vetor grande 1 (20000 elementos) preenchido aleatoriamente
      3 - Vetor grande 2 (40000 elementos) preenchido aleatoriamente
      4 - Vetor grande 1 (20000 elementos) preenchido com valores decrescentes
      5 - Vetor grande 2 (40000 elementos) preenchido com valores decrescentes
      8 - Voltar à escolha do método de ordenação
      9 - Sair do programa

  Returns:
      int: A escolha do usuário.
  """
  escolha = None;

  # Exibição das opções
  print('\n{:-<41}'.format(''))
  print('{}'.format('Escolha do tipo de teste a ser executado:'))
  print('{:-<41}'.format(''))

  print(' 1 - Vetor fixo para validação do método de ordenação')
  print(' 2 - Vetor grande 1 (20000 elementos) preenchido aleatoriamente')
  print(' 3 - Vetor grande 2 (40000 elementos) preenchido aleatoriamente')
  print(' 4 - Vetor grande 1 (20000 elementos) preenchido com valores decrescentes')
  print(' 5 - Vetor grande 2 (40000 elementos) preenchido com valores decrescentes')
  print(' 8 - Voltar à escolha do método de ordenação')
  print(' 9 - Sair do programa')

  # Obtenção, via teclado, da escolha do usuário
  escolha = int(input('\nInforme a opção desejada:'))

  return escolha
def insertion_sort(vetor, imprimir=False)

Método da INSERÇÃO - Insertion Sort.

Args

vetor : list
Vetor a ser ordenado.
n : int
Tamanho da porção do vetor que ainda não está ordenada.
imprimir : bool, optional
Chave para imprimir ou não os passos intermediários.

Defaults to False.

Returns

list
O vetor fornecido como parâmetro ordenado.
Expand source code
def insertion_sort(vetor, imprimir=False):

  """ Método da INSERÇÃO - Insertion Sort.

  Args:
      vetor (list): Vetor a ser ordenado.
      n (int): Tamanho da porção do vetor que ainda não está ordenada.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: O vetor fornecido como parâmetro ordenado.
  """

  n = len(vetor)      # número de elementos no vetor
  cursor = 0           # posição do cursor, inicialmente no primeiro elemento

  # loop de passos
  for passo in range(1, n):

   if imprimir: print('Passo {}'.format(passo))

   # avança o cursor em uma posição
   cursor += 1;

   # Corro todos os elementos do fim para trás até a posição do cursor,
   # se o vetor[cursor] for menor que o antecessor, troco de posição e
   # comparo com anterior seguinte, levando o elemento (se menor) até a sua posição correta
   for i in range(cursor, 0, -1):
     # do cursor pra trás, todos os elementos já estão em ordem crescente,
     # se o elemento for maior que seu antecessor ele já está na posição correta,
     # posso interromper o loop e ir ao próximo elemento
     if vetor[i - 1] < vetor[i]:
       break

     # troca a posição dos dois elementos adjacentes
     temp = vetor[i]
     vetor[i] = vetor[i - 1]
     vetor[i - 1] = temp

   if imprimir: print('Valor de V1: {}'.format(vetor))

  # retorna vetor ordenado
  return vetor
def main()

Método main() - Execução de Testes

Expand source code
def main():

  """ Método main() - Execução de Testes """

  # Dicionário cujas chaves representam o método de ordenação escolhido
  metodos_ordenacao = {
    1 : 'BOLHA (esquerda->direita)',
    2 : 'BOLHA (direita->esquerda)',
    3 : 'INSERCAO',
    4 : 'SELECAO',
    5 : 'MESCLAGEM (nao recursivo)',
    6 : 'MESCLAGEM (recursivo)',
    7 : 'QUICKSORT (recursivo)',
    71: 'QUICK_RECURSIVO_CAUDAL_1',
    72: 'QUICK_RECURSIVO_CAUDAL_2'

  }
  # Dicionário cujas chaves representam o tipo de teste de carga escolhido
  tipos_teste = {
    1 : 'FIXO',
    2 : 'GRANDE_1_ALEATORIO',
    3 : 'GRANDE_2_ALEATORIO',
    4 : 'GRANDE_1_DECRESCENTE',
    5 : 'GRANDE_2_DECRESCENTE'
  }

  # Montando as opções dos menus
  opcoes_menu_metodo_ordenacao = dict(metodos_ordenacao)      # cópia
  opcoes_menu_metodo_ordenacao.update( { 9 : 'SAIR'} )

  opcoes_menu_tipo_teste = dict(tipos_teste)
  opcoes_menu_tipo_teste.update({
    8 : 'MUDAR_METODO',
    9 : 'SAIR'
  })


  # Armazenar as escolhas realizadas pelo usuário
  escolhas = {'metodo' : -1, 'teste' : -1}

  # OBS|=> Em Python 2.x não é possível atribuir novos valores a variáveis declaradas
  # em um escopo superior; ao tentar fazê-lo o que ocorre é declarar uma nova variável local
  # de mesmo nome. A forma que escolhemos para contornar o problema foi declarar um dicionário
  # e alterar o valor associado a chave. Escopo interno não pode fazer atribuição à variáveis
  # não locais, mas pode modificar objetos (dict) que variáveis não locais se referem.
  #
  # Os dicionários main#escolhas e o acionar_teste#vetor foram utilizados com essa finalidade.
  # Python 3 resolveu esse incoveniente sem ferir o princípio pythônico do -
  # "explicit is better than implicit", acrescentando as palavras chaves: global e nonlocal.


  # Executar Testes a Partir das Escolhas     --------------------------------------------------------

  # Esta função é utilizada meramente para fazer a interação com o usuário, logo,
  # pertence a camada de apresentação e não deve fazer parte do módulo.
  # Em função do exposto, evitamos a exposição da função #acionar_teste() fazendo
  # sua definição dentro do próprio contexto do método #main().

  def acionar_teste(escolhas):

    vetor = {'vetor': None}


    # Carregamento do Vetor ------------------------------------------------------------------------

    if escolhas['teste'] == FIXO:
      print('\nEscolheu teste VETOR {} !'.format( tipos_teste[escolhas['teste']] ))
      vetor['vetor'] = monta_vetor_FIXO( metodos_ordenacao[escolhas['metodo']] )

    elif escolhas['teste'] == GRANDE_1_ALEATORIO:
      print('\nEscolheu teste VETOR {} !'.format( tipos_teste[escolhas['teste']] ))
      vetor['vetor'] = monta_vetor_GRANDE_1_ALEATORIO( metodos_ordenacao[escolhas['metodo']] )

    elif escolhas['teste'] == GRANDE_1_DECRESCENTE:
      print('\nEscolheu teste VETOR {} !'.format( tipos_teste[escolhas['teste']] ))
      vetor['vetor'] = monta_vetor_GRANDE_1_DECRESCENTE( metodos_ordenacao[escolhas['metodo']] )

    elif escolhas['teste'] == GRANDE_2_ALEATORIO:
      print('\nEscolheu teste VETOR {} !'.format( tipos_teste[escolhas['teste']] ))
      vetor['vetor'] = monta_vetor_GRANDE_2_ALEATORIO( metodos_ordenacao[escolhas['metodo']] )

    elif escolhas['teste'] == GRANDE_2_DECRESCENTE:
      print('\nEscolheu teste VETOR {} !'.format( tipos_teste[escolhas['teste']] ))
      vetor['vetor'] = monta_vetor_GRANDE_2_DECRESCENTE( metodos_ordenacao[escolhas['metodo']] )


    # único teste que imprime os passos da ordenação
    eh_teste_fixo = escolhas['teste'] == FIXO


    # Executando os Testes - Benchmark  ------------------------------------------------------------

    # Ordenação por BOLHA (esquerda->direita)

    if escolhas['metodo'] == BOLHA_DIRETO:
      if eh_teste_fixo:
        bubble_sort(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        bubble_sort(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por BOLHA (direita->esquerda)

    elif escolhas['metodo'] == BOLHA_INVERTIDO:
      if eh_teste_fixo:
        bubble_sort_invertido(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        bubble_sort_invertido(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por INSERÇÃO

    elif escolhas['metodo'] == INSERCAO:
      if eh_teste_fixo:
        insertion_sort(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        insertion_sort(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por SELEÇÃO

    elif escolhas['metodo'] == SELECAO:
      if eh_teste_fixo:
        selection_sort(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        selection_sort(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por MESCLAGEM NÃO RECURSIVO

    elif escolhas['metodo'] == MESCLAGEM_NAO_RECURSIVO:
      if eh_teste_fixo:
        merge_sort_full_service(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        merge_sort_full_service(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por MESCLAGEM RECURSIVO

    elif escolhas['metodo'] == MESCLAGEM_RECURSIVO:
      if eh_teste_fixo:
        merge_sort_recursivo(vetor['vetor'], True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        merge_sort_recursivo(vetor['vetor'])
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por QUICKSORT (PARTIÇÃO)

    elif escolhas['metodo'] == QUICK_RECURSIVO:
      vetor = vetor['vetor']
      indice_inicial = 0
      indice_final = len(vetor) - 1
      if eh_teste_fixo:
        quick_sort(vetor, indice_inicial, indice_final, True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        quick_sort(vetor, indice_inicial, indice_final)
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por QUICKSORT CAUDAL 1 (PARTIÇÃO)

    elif escolhas['metodo'] == QUICK_RECURSIVO_CAUDAL_1:
      vetor = vetor['vetor']
      indice_inicial = 0
      indice_final = len(vetor) - 1
      if eh_teste_fixo:
        quick_sort_tail_1(vetor, indice_inicial, indice_final, True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        quick_sort_tail_1(vetor, indice_inicial, indice_final)
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )

    # Ordenação por QUICKSORT CAUDAL 2 (PARTIÇÃO)

    elif escolhas['metodo'] == QUICK_RECURSIVO_CAUDAL_2:
      vetor = vetor['vetor']
      indice_inicial = 0
      indice_final = len(vetor) - 1
      if eh_teste_fixo:
        quick_sort_tail_2(vetor, indice_inicial, indice_final, True)
      else:
        print('\nOrdenando...')
        abre_cronometro = datetime.now()
        # Aciona ordenação
        quick_sort_tail_2(vetor, indice_inicial, indice_final)
        fecha_cronometro = datetime.now()
        diferenca = fecha_cronometro - abre_cronometro
        print('            Levou {:.3f} segundos para calcular\n'.format(diferenca.total_seconds()) )


  # Loop Principal do Programa    ------------------------------------------------------------------
  # Interação com o usuário através do menu de opções

  while True:
    # Escolha do método de ordenação a ser executado
    escolhas['metodo'] = escolhe_metodo()

    if escolhas['metodo'] not in opcoes_menu_metodo_ordenacao:
      print('\nOpção INVÁLIDA !  Tente novamente...\n')

    elif not escolhas['metodo'] == SAIR:
      while True:
        # Escolha do tipo de teste a ser executado
        escolhas['teste'] = escolhe_teste()

        if escolhas['teste'] == MUDAR_METODO:
          print('\nEscolheu voltar à escolha do método de ordenação !')
          break

        elif escolhas['teste'] == SAIR:
          print('\nEscolheu SAIR !\n\n')
          escolhas['metodo'] = SAIR   # deixar o loop externo saber da intenção
          break

        elif escolhas['teste'] not in opcoes_menu_tipo_teste:
          print('\nOpção INVÁLIDA !  Tente novamente...\n')

        else:
          acionar_teste(escolhas)

    # Se pediu pra sair no loop interno, obedeça e saia da execução do programa
    if escolhas['metodo'] == SAIR:
      break
def merge_sort(metade_esquerda, metade_direita, imprimir=False)

Método da MESCLAGEM - Merge Sort.

Por não ser o recursivo e ordena as duas metades primeiramente para depois mesclar os elementos em ordem crescente.

Args

metade_esquerda : list
primeira metade
metade_direita : list
segunda metade
imprimir : bool, optional
Chave para imprimir ou não os passos intermediários.

Defaults to False.

Returns

list
Um vetor ordenado.
Expand source code
def merge_sort(metade_esquerda, metade_direita, imprimir=False):

  """ Método da MESCLAGEM - Merge Sort.

  Por não ser o recursivo e ordena as duas metades primeiramente para depois mesclar
  os elementos em ordem crescente.

  Args:
      metade_esquerda (list): primeira metade
      metade_direita (list): segunda metade
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: Um vetor ordenado.
  """
  i = j = passo = 0
  tamanho_metade_esquerda = len(metade_esquerda)
  tamanho_metade_direita  = len(metade_direita)

  # Para garantir que as duas metades estejam ordenadas, passamos os vetores pelo Bubble Sort.
  # Se estiverem previamente ordenadas, o método bubble sort vai descobrir em apenas uma
  # passada pelo vetor e retornará rapidamente.
  metade_esquerda = bubble_sort(metade_esquerda, imprimir)
  metade_direita  = bubble_sort(metade_direita,  imprimir)

  # Cria o vetor que receberá os elementos ordenados
  vetor = list(metade_esquerda)
  vetor.extend(metade_direita)


  # Compara as duas metades sequencialmentes e adiciona no vetor de forma crescente.
  # OBS IMPORTANTE|=> as duas metades precisam já estarem ordenadas

  while passo < (tamanho_metade_esquerda + tamanho_metade_direita):

    if imprimir: print('Passo {}'.format(passo + 1))

    # Comparo os elementos de ambos os lados
    if metade_esquerda[i] <= metade_direita[j]:
      # guardo o de menor valor que está do lado esquerdo
      vetor[passo] = metade_esquerda[i]
      # ando pra frente no vetor do lado esquerdo
      i += 1
      # se já terminei de fazer comparações com a primeira metade, do lado esquerdo
      if i == tamanho_metade_esquerda:
        # copio o resto dos elementos da segunda metade do lado direito
        while j < tamanho_metade_direita:
          passo += 1
          vetor[passo] = metade_direita[j]
          j += 1
    else:
      # guardo o de menor valor que está do lado direito
      vetor[passo] = metade_direita[j]
      # ando pra frente no vetor do lado direito
      j += 1
      # se já terminei a segunda metade, do lado direito
      if j == tamanho_metade_direita:
        # copio os elementos restantes da metade esquerda
        while i < tamanho_metade_esquerda:
          passo += 1
          vetor[passo] = metade_esquerda[i]
          i += 1

    passo += 1

    if imprimir: print('Valor de V1: {}'.format(vetor))

  # retorna vetor ordenado
  return vetor
def merge_sort_full_service(vetor, imprimir=False)

Método da MESCLAGEM - Merge Sort.

Divide o vetor em duas metades e invoca o #merge_sort() para fazer efetivamente a ordenação.

Args

vetor : list
Vetor que será ordenado.
imprimir : bool, optional
Chave para imprimir ou não os passos intermediários.

Defaults to False.

Returns

list
Um novo vetor ordenado.
Expand source code
def merge_sort_full_service(vetor, imprimir=False):

  """ Método da MESCLAGEM - Merge Sort.

  Divide o vetor em duas metades e invoca o #merge_sort() para fazer efetivamente a ordenação.

  Args:
      vetor (list): Vetor que será ordenado.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: Um novo vetor ordenado.
  """
  tamanho = len(vetor)
  meio = int(tamanho / 2)
  metade_esquerda = vetor[:meio]
  metade_direita  = vetor[meio:tamanho]

  if imprimir:
    print('metade_esquerda: {}'.format(metade_esquerda))
    print('metade_direita:  {}'.format(metade_direita))

  # retorna um novo vetor ordenado
  return merge_sort(metade_esquerda, metade_direita, imprimir)
def merge_sort_recursivo(vetor, imprimir=False)

Método da MESCLAGEM - Merge Sort - Versão Recursiva.

Divide o vetor em duas metades e invoca o merge_sort recursivamente para fazer efetivamente a ordenação.

Args

vetor : list
Vetor que será ordenado.
imprimir : bool, optional
Chave para imprimir ou não os passos intermediários.

Defaults to False.

Returns

list
O vetor ordenado.
Expand source code
def merge_sort_recursivo(vetor, imprimir=False):

  """ Método da MESCLAGEM - Merge Sort - Versão Recursiva.

  Divide o vetor em duas metades e invoca o merge_sort recursivamente para fazer efetivamente a ordenação.

  Args:
      vetor (list): Vetor que será ordenado.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: O vetor ordenado.
  """

  if imprimir: print('ENTRADA - Elementos do vetor: {}'.format(vetor))

  tamanho = len(vetor)

  # CASO BASE: tamanho == 1 (nada a fazer)
  if tamanho == 1:
    return vetor

  # RECURSÃO

  meio = int(tamanho / 2)
  metade_esquerda = vetor[:meio]
  metade_direita  = vetor[meio:tamanho]

  tamanho_metade_esquerda = len(metade_esquerda)
  tamanho_metade_direita = len(metade_direita)


  if imprimir:
    print('ENTRADA - Elementos da metade esquerda: {}'.format(metade_esquerda))
    print('ENTRADA - Elementos da metade direita:  {}\n'.format(metade_direita))


  # Recursividade - Aciona a ordenação recursiva para cada metade

  metade_esquerda = merge_sort_recursivo(metade_esquerda, imprimir)
  metade_direita = merge_sort_recursivo(metade_direita, imprimir)


  # Mescla as metades, reconstruindo o vetor ordenadamente

  i = j = passo = 0

  while passo < tamanho:

    if imprimir: print('Passo {}'.format(passo + 1))

    # Comparo os elementos de ambos os lados
    if metade_esquerda[i] <= metade_direita[j]:
      # guardo o de menor valor que está do lado esquerdo
      vetor[passo] = metade_esquerda[i]
      # ando pra frente no vetor do lado esquerdo
      i += 1
      # se já terminei de fazer comparações com a primeira metade do lado esquerdo
      if i == tamanho_metade_esquerda:
        # copio o resto dos elementos da segunda metade do lado direito que já estão ordenados
        while j < tamanho_metade_direita:
          passo += 1
          vetor[passo] = metade_direita[j]
          j += 1
    else:
      # guardo o de menor valor que está do lado direito
      vetor[passo] = metade_direita[j]
      # ando pra frente no vetor do lado direito
      j += 1
      # Se já terminei a segunda metade, é porque já copie todos os elementos do lado direito
      # para o vetor final ordenado e todos os demais restantes do lado esquerdo são valores maiores.
      # Não há mais nenhum elemento do lado direito que seja menor que do lado esquerdo.
      if j == tamanho_metade_direita:
        # copio os elementos restantes da metade esquerda
        while i < tamanho_metade_esquerda:
          passo += 1
          vetor[passo] = metade_esquerda[i]
          i += 1

    passo += 1

  if imprimir: print('\nSAÍDA - Elementos do vetor: {}\n'.format(vetor))

  # retorna o vetor ordenado
  return vetor
def monta_vetor_FIXO(metodo_ordenacao)

Procedimento que monta o VETOR FIXO e o imprime antes da ordenação.

Args

metodo_ordenacao : str
O nome do método.

Returns

list
Um vetor pequeno para validação dos métodos.
Expand source code
def monta_vetor_FIXO(metodo_ordenacao):

  """ Procedimento que monta o VETOR FIXO e o imprime antes da ordenação.

  Args:
      metodo_ordenacao (str): O nome do método.

  Returns:
    list: Um vetor pequeno para validação dos métodos.
  """

  print('\n------------------------------------')
  print('Inicializando dados do vetor V1 para: {}'.format(metodo_ordenacao))
  print('------------------------------------')

  V1 = [89, 44, 75, 66, 11, 38, 93, 56]

  #V1 = [3,0,1,8,7,2,5,4,9,6]  # vetor da dança húngara: https://www.youtube.com/watch?v=ywWBy6J5gz8

  print('Valor de V1: {}'.format(V1))

  return V1
def monta_vetor_GRANDE_1_ALEATORIO(metodo_ordenacao)

Procedimento que monta o VETOR GRANDE 1 e o preenche com números aleatórios.

Args

metodo_ordenacao : str
O nome do método.

Returns

list
Um vetor de 20.000 elementos aleatórios.
Expand source code
def monta_vetor_GRANDE_1_ALEATORIO(metodo_ordenacao):

  """ Procedimento que monta o VETOR GRANDE 1 e o preenche com números aleatórios.

  Args:
      metodo_ordenacao (str): O nome do método.

  Returns:
    list: Um vetor de 20.000 elementos aleatórios.
  """
  print('\n------------------------------------')
  print('Inicializando dados do vetor T1 para: {}'.format(metodo_ordenacao))
  print('------------------------------------')

  T1=[]
  for i in range(20000):
    T1.append( random.randint(1, 20000) )

  print('\nVocê escolheu gerar vetores com números aleatórios !')
  print('Primeiro elemento de T1: {}'.format( T1[0] ))
  print('Último   elemento de T1: {}'.format( T1[19999] ))

  return T1
def monta_vetor_GRANDE_1_DECRESCENTE(metodo_ordenacao)

Procedimento que monta o VETOR GRANDE 1 e o preenche com números decrescentes.

Args

metodo_ordenacao : str
O nome do método.

Returns

list
Um vetor de 20.000 elementos em ordem descrescente.
Expand source code
def monta_vetor_GRANDE_1_DECRESCENTE(metodo_ordenacao):

  """ Procedimento que monta o VETOR GRANDE 1 e o preenche com números decrescentes.

  Args:
      metodo_ordenacao (str): O nome do método.

  Returns:
    list: Um vetor de 20.000 elementos em ordem descrescente.
  """

  print('\n------------------------------------')
  print('Inicializando dados do vetor T1 para: {}'.format(metodo_ordenacao))
  print('------------------------------------')

  T1=[]
  for i in range(20000):
    T1.append(20000 - i)

  print('\nVocê escolheu gerar vetores com números decrescentes !')
  print('Primeiro elemento de T1: {}'.format( T1[0] ))
  print('Último   elemento de T1: {}'.format( T1[19999] ))

  return T1
def monta_vetor_GRANDE_2_ALEATORIO(metodo_ordenacao)

Procedimento que monta o VETOR GRANDE 2 e o preenche com números aleatórios.

Args

metodo_ordenacao : str
O nome do método.

Returns

list
Um vetor de 40.000 elementos aleatórios.
Expand source code
def monta_vetor_GRANDE_2_ALEATORIO(metodo_ordenacao):

  """ Procedimento que monta o VETOR GRANDE 2 e o preenche com números aleatórios.

  Args:
      metodo_ordenacao (str): O nome do método.

  Returns:
    list: Um vetor de 40.000 elementos aleatórios.
  """
  print('\n------------------------------------')
  print('Inicializando dados do vetor T2 para: {}'.format(metodo_ordenacao))
  print('------------------------------------')

  T2=[]
  for i in range(40000):
    T2.append( random.randint(1, 40000) )

  print('\nVocê escolheu gerar vetores com números aleatórios !')
  print('Primeiro elemento de T2: {}'.format( T2[0] ))
  print('Último   elemento de T2: {}'.format( T2[39999] ))

  return T2
def monta_vetor_GRANDE_2_DECRESCENTE(metodo_ordenacao)

Procedimento que monta o VETOR GRANDE 2 e o preenche com números decrescentes.

Args

metodo_ordenacao : str
O nome do método.

Returns

list
Um vetor de 40.000 elementos em ordem descrescente.
Expand source code
def monta_vetor_GRANDE_2_DECRESCENTE(metodo_ordenacao):

  """ Procedimento que monta o VETOR GRANDE 2 e o preenche com números decrescentes.

  Args:
      metodo_ordenacao (str): O nome do método.

  Returns:
    list: Um vetor de 40.000 elementos em ordem descrescente.
  """
  print('\n------------------------------------')
  print('Inicializando dados do vetor T2 para: {}'.format(metodo_ordenacao))
  print('------------------------------------')

  T2=[]
  for i in range(40000):
    T2.append(40000 - i)

  print('\nVocê escolheu gerar vetores com números decrescentes !')
  print('Primeiro elemento de T2: {}'.format( T2[0] ))
  print('Último   elemento de T2: {}'.format( T2[39999] ))

  return T2
def particionar(vetor, indice_inicial, indice_final, imprimir=False)

Particionador do Quick Sort

Pega o elemento(pivô) do vetor cujo indice ou posição é dado por 'indice_inicial', vetor[indice_inicial], e encontra a posição correta do elemento e retorna o índice referente a posição correta. Chamaremos este elemento que estamos procurando sua posição simplesmente de 'elemento_pivo_investigado'.

O algoritmo usa dois cursores para correr o vetor no intevalo dos índices [inicial ao final(inclusive)]. O cursor da extremidade esquerda avança para a direita até encontrar o elemento subsequente que seja maior que o elemento_pivo_investigado. O cursor da direita recua para a esquerda até encontrar um elemento que seja menor que o elemento_pivo_investigado. Nessa situação, teremos:

elemento_esquerda > elemento_pivo_investigado > elemento_direita, logo,
elemento_esquerda > elemento_direita

Isto é, está fora de ordem, e o algoritmo já os troca de posição ordenando-os, depois prossegue em direção ao centro até que os cursores volantes se encontrarem.

A condição de parada ocorre quando o cursor da esquerda atropela o da direita, ficando com um valor maior. O cursor da direita estará com o índice da posição correta, sendo ele o valor retornado pela função.

Sinteticamente expresso de uma maneira formal matemática seria:

Recebe vetor v[p..r] com p < r. Rearranja os elementos do vetor e
devolve j em p..r tal que v[p..j-1] <= v[j] < v[j+1..r].

Args

vetor : list
Vetor que será ordenado e particionado logicamente.
indice_inical(int): Índice inicial do intervalo de pesquisa e que determina o elemento pivô cuja posição
correta será retornada como a posição do pivô do particionamento.
indice_final(int): Índice final do intervalo de pesquisa.
imprimir : bool, optional
Chave para imprimir ou não os passos intermediários.

Defaults to False.

Returns

int
A posição do pivô do particionamento do vetor.
Expand source code
def particionar(vetor, indice_inicial, indice_final, imprimir=False):

  """ Particionador do Quick Sort

  Pega o elemento(pivô) do vetor cujo indice ou posição é dado por 'indice_inicial', ``vetor[indice_inicial]``,
  e encontra a posição correta do elemento e retorna o índice referente a posição correta.
  Chamaremos este elemento que estamos procurando sua posição simplesmente de 'elemento_pivo_investigado'.

  O algoritmo usa dois cursores para correr o vetor no intevalo dos índices [inicial ao final(inclusive)].
  O cursor da extremidade esquerda avança para a direita até encontrar o elemento subsequente que seja
  maior que o elemento_pivo_investigado. O cursor da direita recua para a esquerda até encontrar
  um elemento que seja menor que o elemento_pivo_investigado. Nessa situação, teremos:

      elemento_esquerda > elemento_pivo_investigado > elemento_direita, logo,
      elemento_esquerda > elemento_direita

  Isto é, está fora de ordem, e o algoritmo já os troca de posição ordenando-os, depois prossegue
  em direção ao centro até que os cursores volantes se encontrarem.

  A condição de parada ocorre quando o cursor da esquerda atropela o da direita, ficando com um valor maior.
  O cursor da direita estará com o índice da posição correta, sendo ele o valor retornado pela função.

  Sinteticamente expresso de uma maneira formal matemática seria:

      Recebe vetor v[p..r] com p < r. Rearranja os elementos do vetor e
      devolve j em p..r tal que v[p..j-1] <= v[j] < v[j+1..r].

  Args:
      vetor (list): Vetor que será ordenado e particionado logicamente.
      indice_inical(int): Índice inicial do intervalo de pesquisa e que determina o elemento pivô cuja posição
      correta será retornada como a posição do pivô do particionamento.
      indice_final(int): Índice final do intervalo de pesquisa.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    int: A posição do pivô do particionamento do vetor.
  """
  elemento_pivo = vetor[indice_inicial]    # ..a ser investigado qual o seu posicionamento correto no vetor
  cursor_esquerda = indice_inicial         # posicionado no primeiro elemento
  cursor_direita = indice_final            # posicionado no último elemento
  posicao_correta_pivo = -1

  # A missão primária é encontrar a #posicao_correta_pivo dentro do vetor, mas durante o processo
  # já vai ordenando, trocando de posição alguns elementos; os que são menores que o pivô e estejam
  # a direita são trocados com elementos que são maiores que o pivô, mas que estão do lado esquerdo.

  while cursor_esquerda <= cursor_direita:

    # Encontra o elemento subsequente que seja maior que o elemento pivô investigado
    if vetor[cursor_esquerda] <= elemento_pivo:
      cursor_esquerda += 1          # avança

    # Encontra, do fim para o começo, o elemento que seja menor que o elemento pivô investigado
    elif vetor[cursor_direita] > elemento_pivo:
      cursor_direita -= 1           # recua

    # |=> vetor[cursor_esquerda] >= elemento_pivo > vetor[cursor_direita] =>
    # logo, vetor[cursor_esquerda] > vetor[cursor_direita], isto é, está fora da ordem crescente,
    # o menor tem de ficar a esquerda e o maior a direita, vamos trocá-los de posição
    else:
      temp = vetor[cursor_esquerda]
      vetor[cursor_esquerda] = vetor[cursor_direita]
      vetor[cursor_direita] = temp
      # A posição correta do elemento pivô investigado está entre os cursores; continuando a procura
      cursor_esquerda += 1     # avança uma posição
      cursor_direita  -= 1     # recua  uma posição

  # A posição correta do elemento pivô é dado pelo cursor_direita
  posicao_correta_pivo = cursor_direita

  # Vamos trocar de posição, fazendo backup do elemento que está ocupando o lugar
  # do nosso elemento pivô investigado para o lugar do pivô (início do vetor)
  vetor[indice_inicial] = vetor[posicao_correta_pivo]
  vetor[posicao_correta_pivo] = elemento_pivo       # copia o elemento pivô para sua posição correta

  if imprimir: print('\nValor de V1: {}'.format(vetor))


  return posicao_correta_pivo
def quick_sort(vetor, indice_inicial, indice_final, imprimir=False)

Quick Sort - Recursão comum.

Recebe um vetor vetor[indice_inicial..indice_final], com indice_inicial <= indice_final + 1, e rearranja o vetor em ordem crescente.

Args

vetor : list
Vetor que será ordenado.
indice_inicial(int): Índice inicial do intervalo de pesquisa.
indice_final(int): Índice final do intervalo de pesquisa.
imprimir : bool, optional
Chave para imprimir ou não os passos intermediários.

Defaults to False.

Returns

None
Nada retorna. Opera diretamente com a referência do vetor.
Expand source code
def quick_sort(vetor, indice_inicial, indice_final, imprimir=False):

  """ Quick Sort - Recursão comum.

  Recebe um vetor vetor[indice_inicial..indice_final], com indice_inicial <= indice_final + 1,
  e rearranja o vetor em ordem crescente.

  Args:
      vetor (list): Vetor que será ordenado.
      indice_inicial(int): Índice inicial do intervalo de pesquisa.
      indice_final(int): Índice final do intervalo de pesquisa.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    None: Nada retorna. Opera diretamente com a referência do vetor.
  """
  posicao_pivo_particao = -1

  if indice_inicial < indice_final:    # indices
    posicao_pivo_particao = particionar(vetor, indice_inicial, indice_final, imprimir)

    # partição esquerda
    quick_sort(vetor, indice_inicial, posicao_pivo_particao - 1, imprimir)
    # partição direita
    quick_sort(vetor, posicao_pivo_particao + 1, indice_final, imprimir)
def quick_sort_tail_1(vetor, indice_inicial, indice_final, imprimir=False)

Quick Sort Recursivo Caudal - Versão 1

A recursividade ocorre em uma partição de cada vez.

Args

vetor : list
Vetor que será ordenado.
indice_inicial(int): Índice inicial do intervalo de pesquisa.
indice_final(int): Índice final do intervalo de pesquisa.
imprimir : bool, optional
Chave para imprimir ou não os passos intermediários.

Defaults to False.

Returns

None
Nada retorna. Opera diretamente com a referência do vetor.
Expand source code
def quick_sort_tail_1(vetor, indice_inicial, indice_final, imprimir=False):

  """ Quick Sort Recursivo Caudal - Versão 1

  A recursividade ocorre em uma partição de cada vez.

  Args:
      vetor (list): Vetor que será ordenado.
      indice_inicial(int): Índice inicial do intervalo de pesquisa.
      indice_final(int): Índice final do intervalo de pesquisa.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    None: Nada retorna. Opera diretamente com a referência do vetor.
  """

  posicao_pivo_particao = -1

  while indice_inicial < indice_final:

    posicao_pivo_particao = particionar(vetor, indice_inicial, indice_final, imprimir)

    # Ordena SEPARADAMENTE elementos 'antes' e 'depois' da partição
    quick_sort_tail_1(vetor, indice_inicial, posicao_pivo_particao - 1, imprimir)

    indice_inicial = posicao_pivo_particao + 1
def quick_sort_tail_2(vetor, indice_inicial, indice_final, imprimir=False)

Quick Sort Recursivo Caudal - Versão 2

Melhor otimização em relação a memória, pois a recursividade ocorre em apenas uma partição, e esta partição é a menor, fazendo com que a profundidade das chamadas recursivas seja mais rasa.

Requer espaço auxiliar O(Log n) no pior caso.

Args

vetor : list
Vetor que será ordenado.
indice_inicial(int): Índice inicial do intervalo de pesquisa.
indice_final(int): Índice final do intervalo de pesquisa.
imprimir : bool, optional
Chave para imprimir ou não os passos intermediários.

Defaults to False.

Returns

None
Nada retorna. Opera diretamente com a referência do vetor.

.. _See below link for complete running code: https://ide.geeksforgeeks.org/qrlM31

Expand source code
def quick_sort_tail_2(vetor, indice_inicial, indice_final, imprimir=False):

  """ Quick Sort Recursivo Caudal - Versão 2

  Melhor otimização em relação a memória, pois a recursividade ocorre em apenas uma partição, e esta
  partição é a menor, fazendo com que a profundidade das chamadas recursivas seja mais rasa.

  Requer espaço auxiliar O(Log n) no pior caso.

  Args:
      vetor (list): Vetor que será ordenado.
      indice_inicial(int): Índice inicial do intervalo de pesquisa.
      indice_final(int): Índice final do intervalo de pesquisa.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    None: Nada retorna. Opera diretamente com a referência do vetor.


  .. _See below link for complete running code:
      https://ide.geeksforgeeks.org/qrlM31
  """
  posicao_pivo_particao = -1

  while indice_inicial < indice_final:

    posicao_pivo_particao = particionar(vetor, indice_inicial, indice_final, imprimir)

    # Se parte à esquerda do pivô é menor, tratá-la recursivamente
    # e lidar com a parte à dreita iterativamente 'while'
    parte_esquerda_eh_menor = posicao_pivo_particao - indice_inicial < indice_final - posicao_pivo_particao

    if parte_esquerda_eh_menor:
      quick_sort_tail_2(vetor, indice_inicial, posicao_pivo_particao - 1, imprimir)

      indice_inicial = posicao_pivo_particao + 1

    # Caso contrário: recursão à direita, iteração à esquerda
    else:
      quick_sort_tail_2(vetor, posicao_pivo_particao + 1, indice_final, imprimir)

      indice_final = posicao_pivo_particao - 1
def selection_sort(vetor, imprimir=False)

Método da SELEÇÃO - Selection Sort.

Args

vetor : list
Vetor a ser ordenado.
n : int
Tamanho da porção do vetor que ainda não está ordenada.
imprimir : bool, optional
Chave para imprimir ou não os passos intermediários.

Defaults to False.

Returns

list
O vetor fornecido como parâmetro ordenado.
Expand source code
def selection_sort(vetor, imprimir=False):

  """ Método da SELEÇÃO - Selection Sort.

  Args:
      vetor (list): Vetor a ser ordenado.
      n (int): Tamanho da porção do vetor que ainda não está ordenada.
      imprimir (bool, optional): Chave para imprimir ou não os passos intermediários.
      Defaults to False.

  Returns:
    list: O vetor fornecido como parâmetro ordenado.
  """
  # Variáveis auxiliares
  indice_menor = -1
  n = len(vetor)      # número de elementos no vetor
  cursor = -1          # posição do cursor, inicialmente antes do primeiro elemento


  # loop de passos
  for passo in range(1, n):

    if imprimir: print('Passo {}'.format(passo))

    # avança o cursor em uma posição
    cursor += 1;
    # encontra o menor valor dentre os elementos não ordenados
    menor = vetor[cursor]
    indice_menor = cursor
    for i in range(cursor + 1, n):
      if vetor[i] < menor:
        menor = vetor[i]
        indice_menor = i

    # troca o menor com o elemento ao lado direito do cursor
    vetor[indice_menor] = vetor[cursor]
    vetor[cursor] = menor

    if imprimir: print('Valor de V1: {}'.format(vetor))

  # retorna vetor ordenado
  return vetor