Executando verificação de segurança...
7

Quando o ChatGPT me ofereceu uma solução completamente errada

Resumo rápido

Brincando com o ChatGPT, questionei sobre uma implementação utilizando listas em Scala e a resposta oferecida foi totalmente errada. Não só isso, mas a ferramenta defendeu seu uso demosntrando falta de conhecimento (ok... eu sei que é uma ferramenta e não uma pessoa, e vou chegar nesse ponto ao longo do texto... confere aí).

O que é o ChatGPT?

O ChatGPT é uma ferramenta que permite que você converse com um modelo de linguagem natural treinado pela OpenAI. Entre outras coisas, ele é capaz de entender e escrever código em diversas linguagens.

O que aconteceu?

Bom, eu estava brincando com o ChatGPT e quis testar a capacidade dele de entender e escrever código. Como vejo uma situação muito comum em Scala no tratamento de List, tive essa "conversa":

ricardovm: Em Scala, considerando que eu tenho um List (que é imutável) e eu quero acrescentar um elemento... é mais performático acrescentar no início com o operador :: ou no final da List com :+?

ChatGPT: Em Scala, acrescentar um elemento ao final de uma List é mais performático do que acrescentar no início da lista. Isso ocorre porque as operações de adição no final da lista são mais eficientes do que as operações de adição no início da lista. Para acrescentar um elemento ao final da lista, você pode usar a operação :+:

(código exemplo omitido)

ChatGPT: Ao contrário, para acrescentar um elemento no início da lista, você pode usar a operação :::

(código exemplo omitido)

No entanto, é importante lembrar que as operações de adição no início da lista são mais lentas do que as operações de adição no final da lista, portanto, se você precisar adicionar muitos elementos a uma lista e precisar de alta performance, é recomendável usar uma estrutura de dados diferente, como um ArrayBuffer.

O que está errado?

O fato é que a informação gerada pelo ChatGPT está incorreta (ignorando o fato dele tentar mudar para uma implementação mutável como ArrayBuffer, que ainda assim seria mais lenta.

As List em Scala funcionam como uma lista ligada imutável, ou seja, cada elemento da lista aponta para o próximo elemento da lista.

Grosso modo, uma List de um item em Scala é basicamente isso:

// só uma representação simplificada... na real isso geraria uma 
// lista com dois elementos: 3 e Nil
List(3, Nil)

Imagine que a lista é composta pelo número 3 e aponta para a lista vazia Nil... uma lista de um elemento. Como List é imutável, a forma de eu acrescentar um novo item a ela seria criando uma nova lista com o novo elemento. Então imagine essa representação (mais uma vez, não é assim que se instancia uma lista... é apenas uma representação):

List(2, List(3, Nil))

Voilá! Agora temos uma lista com dois elementos: 2 e 3. E se eu quiser acrescentar um novo elemento? Novamente, eu preciso criar uma nova lista. Então, a representação dessa lista ficaria assim:

List(1, List(2, List(3, Nil)))

Sempre acrescentando ao início da lista. Veja que a lista original nunca é alterada, mas serve de continuação para a nova lista. Cada item acrescentado é um head e o tail é apontado para a lista anterior, sem alterar ou apagar nada.

E se eu quiser acrescentar um item ao final da lista? Como ela é imutável, eu não poderia simplesmente trocar o tail do último elemento para apontar para a nova lista ou item.

List(1, Nil)

Não pode ser alterado. Então eu teria que criar uma nova lista:

List(1, List(2, Nil))

E assim por diante. O que significa que, para acrescentar um item ao final da lista, eu
preciso recriar cada elemento da lista até o final para poder apontar para o novo item...

Aquela primeira lista ainda existe... está lá na memória e em algum momento vai ser eliminada pelo garbage collector, mas ainda está lá e não pôde ser aproveitada.

Dá até um nó só de explicar, mas se você imaginas o trabalho que dá para a máquina criar uma lista de 100.000 elementos acrescentando os itens no final dela, você vai entender o tamanho do problema.

Aprofundando ainda mais o problema

Como isso ficou me perturbando, no dia seguinte resolvi ser mais claro com o ChatGPT e
perguntei:

ricardovm: Qual destes dois códigos em Scala é mais performático ?

object Main {
  def main(args: Array[String]): Unit = {
    val inicio = System.currentTimeMillis()

    val lista = (1 to 100000)
      .foldLeft(List.empty[Int]) { (acc, x) =>
        x :: acc
      }

    val resultado = lista.reverse

    val fim = System.currentTimeMillis()
    println(s"Tempo: ${fim - inicio} ms")

    println(resultado.length)
  }
}

ou

object Main {
  def main(args: Array[String]): Unit = {
    val inicio = System.currentTimeMillis()

    val lista = (1 to 100000)
      .foldLeft(List.empty[Int]) { (acc, x) =>
        acc :+ x
      }

    val resultado = lista

    val fim = System.currentTimeMillis()
    println(s"Tempo: ${fim - inicio} ms")

    println(resultado.length)
  }
}

Dá uma olhada na resposta:

ChatGPT: Ambos os códigos criam uma lista de 100000 inteiros. No primeiro código, a lista é construída usando o método foldLeft, adicionando os novos elementos no início da lista (com o operador ::). No segundo código, a lista é construída usando o mesmo método foldLeft, adicionando os novos elementos no final da lista (com o operador :+).

Impressionante como ele conseguiu interpretar o código! Mas segue o restante da resposta:

ChatGPT: O desempenho dos dois códigos dependerá do tamanho da lista e da capacidade de processamento do computador em que o código estiver sendo executado. No entanto, em geral, espera-se que o segundo código seja mais performático, pois a adição de elementos no final de uma lista é geralmente mais eficiente do que a adição no início. Isso se deve ao fato de que, ao adicionar um elemento no início de uma lista, todos os outros elementos da lista precisam ser deslocados para a direita para fazer lugar para o novo elemento. Já ao adicionar um elemento no final da lista, esse deslocamento não é necessário.

Para ter uma ideia mais precisa do desempenho dos dois códigos, você pode medir o tempo de execução de cada um e comparar os resultados. Além disso, é sempre importante lembrar que o desempenho de um código pode ser afetado por muitos fatores diferentes, como a implementação de linguagem, a configuração do sistema e a carga de trabalho atual do computador. Portanto, é importante avaliar o desempenho de um código em contexto e levar em consideração esses fatores ao avaliar a eficiência de uma implementação.

Resposta interessante e até que parece bem embasada, mas que está fundamentalmente errada!

Provando que o ChatGPT está errado

Se você rodar os dois códigos, vai ver que o primeiro é mais performático, acrescentando itens ao início da linha e invetendo ela no final é muito mais rápido e performático em todos os sentidos.

CódigoTempoOperações de GC
Acrecentando no início83 milissegundos326
Acrescentando no final19 segundos400.000

Teimando com o ChatGPT

Com esses resultados, resolvi cutucar o nosso amigo ChatGPT novamente:

ricardovm: O segundo código rodou em 18 segundos e o primeiro rodou em 83 milissegundos.

E a resposta:

ChatGPT: Os resultados que você obteve são bastante diferentes do que eu havia previsto. Isso pode ser explicado por diferenças nas configurações do sistema ou na carga de trabalho atual do computador, ou por outros fatores que podem afetar o desempenho dos códigos.

Apesar disso, é importante observar que o segundo código demorou muito mais tempo do que o primeiro para executar. Isso pode ser devido a alguma otimização que o compilador ou o interpretador da linguagem estejam fazendo no primeiro código, ou a algum outro fator.

Uma maneira de avaliar o desempenho de um código é medir o tempo de execução de diferentes implementações e comparar os resultados. Isso pode ajudar a identificar quais trechos de código estão consumindo mais tempo e onde é possível fazer otimizações. Além disso, é sempre importante lembrar que o desempenho de um código pode ser afetado por muitos fatores diferentes e que é preciso levar esses fatores em consideração ao avaliar a eficiência de uma implementação.

Continua errando...

Conclusão

Muito se fala de as ferramentas de inteligência artificial serem capazes de substituir programadores. Eu as vejo como... ferramentas que podem (e devem) ser usadas por nós, desenvolvedores. Mas nunca poderemos abrir mão do conhecimento.

Fico imaginando alguém que procura uma resposta em uma IA como essas e sai usando o resultado, sem sequer conferir se está, de fato, correto. É capaz de querer dicutir com quem discorda. Fico imaginando pessoas abrindo mão do conhecimento
e delegando isso a IAs.

No fundo, acho que ainda vão continuar existindo dois tipos de desenvolvedores. Hoje mesmo, muitos abrem mão do conhecimento e vivem de copiar e colar do Google/Stackoverflow. Esses são os que vão conseguir melhorar seus códigos usando IA, mas ainda serão os mesmos copiadores de código. E tem aquele que usa Google e Stackoverflow para entender melhor um problema ou para buscar uma ajuda pontual em algo que não conhece ou mesmo não lembra naquele momento, mas que tem o conhecimento teórico necessário para desenvolver bem, e que vai saber aproveitar ainda mais os resultados gerados por IA. IA é uma ferramente e, como tal, deve ser vista... está a serviço de pessoas, não o contrário. Mas, sem dúvida, uma ferramenta com um enorme potencial.

Carregando publicação patrocinada...
2

Interessante! De onde será que o ChatGPT tirou essa informação do "deslocamento para direita"? Parece que a IA está tentando usar conhecimentos de outras linguagens/estruturas de dados pra responder sobre essa específica. Pode ser um caso mais sofisticado de overfitting.

1
1

Um desses dias, para testar, eu fiz um teste com o ChatGPT.
Pedi para ele resolver uma equação do segundo grau utilizando as fórmulas de soma e produto.
Resultado: ele utilizou as fórmulas mas parou do nada e começou a usar bhaskara, além disso apresentou resultados erradoskkkkk

1

Comigo aconteceu uma coisa bem parecida agora pouco, precisava de uma simples função de gerar um codigo de barras onde o codigo inicia com 789 + o input com até 9 digitos + digito verificador e ele gerou um codigo "correto" só que o digito verificado está dando errado, solicitei sempre um exemplo dele caso eu enviasse numeros xxx qual seria o resultado e ele sempre calculava o digito verificador errado, mandei tb toda a explicação de como calcular o digito verificador e mesmo assim ele insistia no erro rsrsrs no final deixei de lado e eu mesmo criei o codigo hauhauhauahua.

1

Ficção científica achar que IA vai substituir Devs. Se depender do ChatGPT ainda vamos ter bons séculos pela frente pra trabalhar sem ser substituído. Hehehehe

1
1
1

Excelente post ricardovm!

Passei por uma situação muito similar que ele dava respostas que simplesmente não cabiam...

Pedi para fazer uma contagem no Python e no final me dava um sum 😂

Acredito que para problemas simples ele está avançando, mas ainda existem muitas coisas para melhorar.

1

já imaginei que isso iria acontecer, como é um aprendizado de máquina, e nem tudo está na internet, e como existe explicações erradas na internet, isso acaba influenciando a IA a tomar decisões erradas.

1

Oi ricardovm

Onde posso aprender mais sobre eficiência em aplicações (softwares, apps, webapp, e BD)?
Tem algum livro que indique? Gosto muito da ideia de produzir aplicações com o menor custo de hardware possível.

Quando você citou os dois tempos, imaginei que conhece algo que possa indicar!
Se alguem que estiver lendo souber também, e quiser responder irei ficar muito grato!

2

Olá @itslevictor!

Gosto muito dessa área, ainda mais que na época que eu comecei a gente tinha que se virar com hardware mais lento.

Um livro que gostei muito, que explica de forma dem simples muitas dessas coisas é o Entendendo Algoritmos, de Aditya Y. Bhargava. Imagino que pegar de início o Introduction do Algorithms do Cormen seja muito pesado.

Mas o mais legal é você ter curiosidade: vai usar uma estrutura nova, dá uma olhada como ela é implementada. Tem aquela classe que todo mundo usa e ninguém sabe o motivo, só usa porque todo mundo usa, dá uma olhada nos fontes e na doc para entender o motivo (para os mais novos, mesmo que não consiga entender de imediato, mas tenta pegar o espírito da coisa).

Fazer experimentos também é uma boa. No caso desse exemplo do List do texto, eu já tinha lido sobre a estrutura e feito experimentos. E, de bobeira, resolvi ver que resposta o ChatGPT dava e, boom, achei esse furo.

1

Esse Entendendo Algoritmos de Aditya penei e não achei o pdf. Já o introduction to algorithms encontrei! Conhece alguem que possa compartilhar o pdf do primeiro?

1

Dia desses fiz um teste, pedi um código pra atualizar meu Arch e ele passou uma sintaxe completamente incorreta. Questionei e felizmente ele reconheceu e passou o código correto em seguida. Não dá pra confiar 100%, mas a tecnologia é interessante.

1

Realmente, o ChatGPT ainda tem suas falhas, pode ser que chegue um momento que ele possa substituir nós Dev's mas por enquanto, ainda tem muitos buracos a tapar

1

Mais mesmo ela tendo suas falhas está sendo muito util pra mim, claro que as pessoas que usam tem que saber dosa o uso e ela nunca vai ser a bala de prata pra todos os problemas, mais que ajuda bastante ela ajuda, um detalhe que me preocupa muito é ela na mão de pessoas menos experientes em desenvolvimento principalmente, vai começa usar ela, achando que é o certo absoluto e pode acabar transformando o codigo em um problema muito dificil até mesmo de encontrar.

1

Acredito que precisa ser muito moleque imaturo para deixar uma IA fazer o trampo do cara e ainda achar que estou no norte, minha opinião tá.
Como já foi dito aqui pequenos códigos e coisa besta a IA faz, mas de vdd alguém deixar a responsa do trampo do cara para a IA fazer é coisa de estagiário total.

O Fábio Akita deu a opinião dele sobre tudo isso e eu concordo. Existe um custo elevadissimo para manter isso funcionando do jeito que está hoje, imagine fazer isso trazer soluções que seja relevante para os negócios... em termos de custo não vale a pena.