sábado, 7 de julho de 2012

Particionando em pares e ímpares

Agora, vamos utilizar indução para resolver um problema fácil, que com indução ficou mais fácil ainda.
Problema:
  • Dada uma sequência de n valores inteiros, rearranjar os elementos de forma que os valores pares apareçam antes dos ímpares.
Idéia indutiva:
  • Caso Base: uma lista com 1 elemento já está particionada em pares e ímpares.
  • Hipótese de Indução: podemos assumir que o problema já foi resolvido para n-1 elementos, e que sabemos onde termina a lista de pares.
  • Caso Geral: basta verificar se o último elemento é par ou ímpar, se ele for par basta inseri-lo no final da lista de pares, caso contrário, não fazemos nada.  
Agora, vamos ilustrar o problema com um exemplo:
  • Situação inicial: Um vetor com valores pares e ímpares misturados.
  • Hipótese de indução: agora vamos "imaginar" que o problema já foi resolvido para os primeiros n - 1 elementos, e que sabemos onde termina a lista de pares.


  • Caso Geral: Pronto! Agora ficou muito fácil, podemos perceber que o último elemento é par, então vamos incrementar o final da lista de pares e trocar o último  elemento da lista de pares com o último elemento do vetor.


  • E assim chegamos a solução do problema.


O algoritmo segue exatamente a idéia apresentada acima. Vamos assumir que o problema já foi resolvido para os primeiros n - 1 elementos, e dentro do loop só escreveremos código para trocar o último elemento do vetor (caso ele seja par) com o último elemento da lista de pares. E não podemos esquecer do caso base!
  • Implementação em C++ (versão iterativa)
fimPares = -1; // caso base
for(int i = 0; i < tamVetor; i++)
  if(vetor[i] % 2 == 0){  // passo indutivo
    fimPares++;
    troca(vetor[i], vetor[fimPares]);
  }

Complexidade do algoritmo: A relação de recorrência da nossa solução é T(n) = T(n - 1) + 1, ou seja, o tempo gasto pela hipótese de indução para separar os n - 1 elementos mais o tempo gasto para colocar o último elemento no final da lista de pares. Derivando T(n) temos:

$T(n) = T(n - 1) + 1$
$T(n) = T(n - 2) + 1 + 1$
$T(n) = T(n - 3) + 1 + 1 + 1$
$T(n) = T(n - 4) + 1 + 1 +1 +1$
...
$T(n) = 1 + 1 + 1 + 1 + 1 + 1 + ... + 1 + 1$
$T(n) = O(n)$

Logo, concluímos que o nossa solução tem complexidade linear.

2 comentários:

  1. Muito bom o Post (assim como os anteriores), mas só gostaria de corrigir a implementação em C++.

    Faltou incrementar o valor de 'fimPares', se executar dessa forma os elementos serão trocados sempre com os primeiros, dando um resultado estranho, basta colocar um fimPares++ depois ou até trocar o "vetor[fimPares + 1]" por "vetor[(fimPares++) + 1]"

    Também fiz uma versão recursiva, também em C++, e no espírito do blog até o imprimeVetor está indutivo :D

    http://pastebin.com/7b27bJfD

    ResponderExcluir
  2. Oi amigo, é verdade. O erro é óbvio nessa implementação, inclusive ela está diferente da que eu tenho implementada kkkk. Ao invés de copiar e colar eu digitei de cabeça e não verifiquei. Obrigado pela observação.
    Att,
    @inductioncode

    ResponderExcluir