<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: William Spader</title>
    <description>The latest articles on Forem by William Spader (@williamspader).</description>
    <link>https://forem.com/williamspader</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F808230%2F3ca45c9b-cb49-4553-8164-b66e0ec19912.jpeg</url>
      <title>Forem: William Spader</title>
      <link>https://forem.com/williamspader</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/williamspader"/>
    <language>en</language>
    <item>
      <title>Go Channels: Creating the Simplest TCP Chat only w/ STD</title>
      <dc:creator>William Spader</dc:creator>
      <pubDate>Tue, 12 Dec 2023 17:02:21 +0000</pubDate>
      <link>https://forem.com/williamspader/go-channels-creating-the-simplest-tcp-chat-only-w-std-3ef0</link>
      <guid>https://forem.com/williamspader/go-channels-creating-the-simplest-tcp-chat-only-w-std-3ef0</guid>
      <description>&lt;p&gt;I strongly believe that the best way to learn a new programming language is by doing side projects.&lt;/p&gt;

&lt;p&gt;In this article we will write the simplest TCP Chat only using STD in Go, after that you can improve and add features as you want.&lt;/p&gt;

&lt;p&gt;We can start talking about the imports needed for this project&lt;/p&gt;

&lt;h2&gt;
  
  
  imports, types and structs
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import (
    "log"
    "net"
    "time"
)

type MessageType int

type Message struct {
    Conn net.Conn
    Type MessageType
    Text []byte
}

const (
    NewClient MessageType = iota
    DisconnectedClient
    NewTextClient
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;log:&lt;/strong&gt; add time information for each printed log and writes to os.Stderr by default.&lt;br&gt;
&lt;strong&gt;net:&lt;/strong&gt; will help us to listen to TCP protocol and to stablish a connnection.&lt;br&gt;
&lt;strong&gt;time:&lt;/strong&gt; for throttling implementation, don't need to worry right now.&lt;/p&gt;
&lt;h2&gt;
  
  
  main()
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func main() {
    ln := startServer()

    channel := make(chan Message)
    go Chat(channel)

    for {
        conn, err := ln.Accept()
        if err != nil {
            log.Println("Could not accept connection. ", err)
        }

        channel &amp;lt;- NewMessage(conn, NewClient, nil)
        log.Println("connection accepted. ", conn.RemoteAddr())

        go HandleConnection(conn, channel)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we can talk about each line to better understand and adding code as needed.&lt;br&gt;
The line &lt;code&gt;ln := startServer()&lt;/code&gt; calls a method that returns a TCP listener.&lt;/p&gt;
&lt;h2&gt;
  
  
  startServer()
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func startServer() net.Listener {
    ln, err := net.Listen("tcp", ":9595")
    if err != nil {
        log.Fatalf("Could not listen on port %s. Shutting down ...\n", Port)
    }

    log.Printf("Listening on port %s\n", Port)
    return ln
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We call &lt;code&gt;net.Listen("tcp", ":9595")&lt;/code&gt; to create a TCP listener on port 9595. Then, if something goes wrong there isn't much we can do, so we log and exit the app.&lt;br&gt;
&lt;code&gt;log.Fatalf()&lt;/code&gt; writes to stderr and exit the app.&lt;br&gt;
If the listener worked, we return to &lt;code&gt;main()&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  go Chat(channel)
&lt;/h2&gt;

&lt;p&gt;Our application will have 1 go routine for each connected user, so we need a channel to communicate between go routines. When a user sends a message, we need to send that message to all users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func Chat(broadcastChan chan Message) {
    clients := make(map[string]net.Conn)
    lastNewTextClient := make(map[string]time.Time)
    for {
        msg := &amp;lt;-broadcastChan
        if msg.Type == NewClient {
            clients[msg.Conn.RemoteAddr().String()] = msg.Conn
            log.Println("New client = ", msg.Conn.RemoteAddr().String())
        } else if msg.Type == DisconnectedClient {
            delete(clients, msg.Conn.RemoteAddr().String())
            msg.Conn.Close()
            log.Println("Client disconnected. Connection closed.")
        } else if msg.Type == NewTextClient {
            lastTime := lastNewTextClient[msg.Conn.RemoteAddr().String()]
            if !lastTime.IsZero() &amp;amp;&amp;amp; lastTime.After(time.Now().Add(-time.Second*5)) {
                msg.Conn.Write([]byte("The time elapse between messages is 5 seconds."))
            } else {
                lastNewTextClient[msg.Conn.RemoteAddr().String()] = time.Now()
                for _, conn := range clients {
                    if conn.RemoteAddr().String() == msg.Conn.RemoteAddr().String() {
                        continue
                    }
                    conn.Write(msg.Text)
                }
            }
        } else {
            log.Println("Unknown message type received = ", msg.Type)
        }
    }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function has another infinite for-loop, so we can keep the connection alive with the user.&lt;br&gt;
We create a map of users to add and remove users from the app as needed.&lt;br&gt;
We also create a map to keep track of the last message from a user, so each user can only send a new message after 5 seconds.&lt;br&gt;
The line &lt;code&gt;msg := &amp;lt;-broadcastChan&lt;/code&gt; await for the next message from the channel.&lt;br&gt;
If it is a &lt;code&gt;NewClient&lt;/code&gt;, then add this client to the map of users.&lt;br&gt;
If it is a &lt;code&gt;DisconnectedClient&lt;/code&gt;, then remove this client from the map of users and close the connection.&lt;br&gt;
If it is a &lt;code&gt;NewTextClient&lt;/code&gt;, then we iterate over the users and send the message to all other users except the one who sent it.&lt;/p&gt;
&lt;h2&gt;
  
  
  infinite for-loop
&lt;/h2&gt;

&lt;p&gt;We open a infinite for-loop so the server stay alive indefinitely. Inside the for-loop we call &lt;code&gt;ln.Accept()&lt;/code&gt;, this function blocks the routine until a new connection arrives and return this connection to us i.e. the &lt;code&gt;conn&lt;/code&gt; variable&lt;/p&gt;
&lt;h2&gt;
  
  
  channel &amp;lt;- NewMessage(conn, NewClient, nil)
&lt;/h2&gt;

&lt;p&gt;If the &lt;code&gt;ln.Accept()&lt;/code&gt; worked, we send a message to the channel to inform that a new user has arrived.&lt;br&gt;
Now, the NewMessage function is defined as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func NewMessage(conn net.Conn, msgType MessageType, buffer []byte) Message {
    if msgType == NewClient {
        return Message{Conn: conn, Type: NewClient}
    } else if msgType == DisconnectedClient {
        return Message{Conn: conn, Type: DisconnectedClient}
    } else if msgType == NewTextClient {
        return Message{Conn: conn, Type: NewTextClient, Text: buffer}
    } else {
        return Message{Conn: conn}
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  go HandleConnection(conn, channel)
&lt;/h2&gt;

&lt;p&gt;Finally, we have the implementation of the last function from &lt;code&gt;main()&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func HandleConnection(conn net.Conn, channel chan Message) {
    for {
        buffer := make([]byte, 512)
        _, err := conn.Read(buffer)
        if err != nil {
            channel &amp;lt;- NewMessage(conn, DisconnectedClient, nil)
            break
        }

        channel &amp;lt;- NewMessage(conn, NewTextClient, buffer)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there is any errors to read the user message, we disconnect the client and break the connection after close it.&lt;br&gt;
If we successfully read the message, we send the message to the channel.&lt;br&gt;
Don'f forget, all messages sent to the channel will be handled by the &lt;code&gt;Chat(channel)&lt;/code&gt; function, as is the only moment in the app that read from the channel.&lt;/p&gt;

&lt;p&gt;Now, you can improve this code and add new features. This app has only one chat for all users, so one idea can be to add users to groups.&lt;/p&gt;

&lt;p&gt;Hope this article helps to better understand the usage of channels in practice!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>go</category>
      <category>channel</category>
    </item>
    <item>
      <title>Spring Data &amp; MongoDB — Pagination+Sorting w/ Aggregation and Lookup</title>
      <dc:creator>William Spader</dc:creator>
      <pubDate>Tue, 12 Dec 2023 15:56:08 +0000</pubDate>
      <link>https://forem.com/williamspader/spring-data-mongodb-paginationsorting-w-aggregation-and-lookup-4gg1</link>
      <guid>https://forem.com/williamspader/spring-data-mongodb-paginationsorting-w-aggregation-and-lookup-4gg1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Need to perform pagination+sorting with nullable filters and properties from two collections.&lt;/p&gt;

&lt;p&gt;One collection holds the ID of the document from another collection that has the property we need to retrieve.&lt;/p&gt;

&lt;p&gt;For those using reactive mongodb, the ReactiveMongoRepository only extends from ReactiveSortingRepository and we also want the following behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a: { otherCollectionId: 1, info: 2}
b: { otherCollectionId: 1, info: 3}
c: { otherCollectionId: 2, info: 1}
findByOtherCollectionIdAndInfo(1, 2) //returns document a
findByOtherCollectionIdAndInfo(1, null) //returns documents a and b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider you have the following two collections where collection A has the id from collection B through otherCollectionId property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Collection A
{
  "_id": ObjectId("..."),
  "otherCollectionId": "...",
  "info": "Some info",
  "anotherInfo": "Some other info"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Collection B
{
  "_id": ObjectId("..."),
  "label": "Some label"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your API needs to respond to the client containingcollectionA infos + the label property from collectionB, and it must be paginated with the possibility of sorting through the label property.&lt;/p&gt;

&lt;p&gt;To get the chunk of data, we can use the following code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mongoTemplate.aggregate(aggregation, "collectionA", Result.class)
return Aggregation.newAggregation(
  Aggregation.match(getCriteria(filter)),
  projectionOperation,
  getLookupOperation(),
  getUnwindOperation(),
  addLabelField(),
  projectionOperationWithLabel,
  Aggregation.sort(pageable.getSort()),
  Aggregation.skip((long) pageable.getPageNumber() * pageable.getPageSize()),
  Aggregation.limit(pageable.getPageSize())
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The getCriteria(filter) method may be&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private Criteria getCriteria(BusinessObject.Filter filter) {
  List&amp;lt;Criteria&amp;gt; criterias = new ArrayList&amp;lt;&amp;gt;();

  Optional.ofNullable(filter.getInfo())
    .ifPresent(info-&amp;gt; criterias.add(Criteria.where("info").is(info)));

  Optional.ofNullable(filter.getOtherCollectionId())
    .ifPresent(otherCollectionId -&amp;gt; criterias.add(Criteria.where("otherCollectionId").is(otherCollectionId)));

  return new Criteria().andOperator(
    criterias.toArray(Criteria[]::new)
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The projectionOperation may be&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ProjectionOperation projectionOperation = Aggregation.project(
  "info", "anotherInfo"
).and(ConvertOperators.ToObjectId.toObjectId("$otherCollectionId")).as("convertedId");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the project operation above, we need to convert otherCollectionId property to ObjectId before the lookup, so we can compare same data type.&lt;/p&gt;

&lt;p&gt;Then, getLookupOperation() method may be&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private LookupOperation getLookupOperation() {
  return LookupOperation.newLookup()
          .from("collectionB")
          .localField("convertedId")
          .foreignField("_id")
          .as("joinedData");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The convertedId property is the otherCollectionId but as ObjectId.&lt;/p&gt;

&lt;p&gt;Then, depending on the situation, you can flat the resulting array from the lookup operation like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private UnwindOperation getUnwindOperation() {
  return Aggregation.unwind("$joinedData");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we add the label property from the collectionB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private AddFieldsOperation addLabelField() {
  return AddFieldsOperation.addField("label").withValue("$joinedData.label").build();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can perform another project operation containing the label property and remove the joinedData to get only the data we need. We also perform another conversion to get otherCollectionId property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ProjectionOperation projectionOperation = Aggregation.project(
  "info", "anotherInfo", "label"
).and(ConvertOperators.ToObjectId.toObjectId("$convertedId")).as("otherCollectionId");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, if you need the total count to return something like PageImpl from the API, you can perform a count query on the collectionA.&lt;/p&gt;

&lt;p&gt;Then, you’ll have something like this to return from the repository, where it.getT1() is the list of items and it.getT2() is the total of elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return new PageImpl&amp;lt;&amp;gt;(it.getT1(), pageable, it.getT2())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hope this article helps!&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>mongodb</category>
      <category>springdata</category>
    </item>
    <item>
      <title>HashTable — A Ciência da Estrutura de Dados Key-Value</title>
      <dc:creator>William Spader</dc:creator>
      <pubDate>Mon, 14 Feb 2022 01:47:03 +0000</pubDate>
      <link>https://forem.com/williamspader/hashtable-a-ciencia-da-estrutura-de-dados-key-value-379o</link>
      <guid>https://forem.com/williamspader/hashtable-a-ciencia-da-estrutura-de-dados-key-value-379o</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Nnc-GZVH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/whlsn43n2uc0d856n6bk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Nnc-GZVH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/whlsn43n2uc0d856n6bk.png" alt="Fluxo do processo de hashing em uma estrutura hashtable" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Uma das coisas mais comuns em programação é realizar uma pesquisa por um item que pode estar armazenado em alguma estrutura.&lt;/p&gt;

&lt;p&gt;Será apresentada a ideia da implementação interna de estruturas como Dict em Python, HashMap em Java, Map ou Object em JavaScript, entre outras.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arrays de Acesso Direto - arr[i]
&lt;/h2&gt;

&lt;p&gt;Arrays estáticos de acesso direto possuem complexidade de busca O(1), então se meu usuário precisa pesquisar por uma chave que representa um número inteiro, basta pesquisar diretamente arr[chave].&lt;/p&gt;

&lt;p&gt;O que acontece quando o usuário precisa armazenar 10^9 itens? Vamos alocar um array com tamanho 10^9? Seria um uso exacerbado de memória.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hashing &amp;amp; HashTable
&lt;/h2&gt;

&lt;p&gt;É possível manter a busca em O(1) usando menos espaço em memória? Podemos armazenar itens em um array menor para continuarmos com os benefícios do array estático, mas precisamos de uma técnica de mapeamento de chaves para pesquisar em um índice diferente nesse array. Essa técnica é chamada de &lt;strong&gt;hashing&lt;/strong&gt; e o array menor é chamado de &lt;strong&gt;hashtable&lt;/strong&gt;. Esse array menor pode diminuir ou aumentar de tamanho conforme necessidade, por exemplo em muitas operações de inserção e deleção.&lt;/p&gt;

&lt;p&gt;Quando recebermos a chave de um item, passaremos ela por uma função chamada de &lt;strong&gt;hash function&lt;/strong&gt;. O resultado dessa função de hash será o índice que devemos acessar na hashtable. Porém, essa função de hash pode causar um problema conhecido como &lt;strong&gt;colisão&lt;/strong&gt;, que é quando duas chaves possuem o mesmo resultado após passar pela função de hash.&lt;/p&gt;

&lt;p&gt;No caso abaixo, as chaves são nomes de pessoas. Os nomes Rose e Zig após passarem pela função de hash resultaram no mesmo índice da hashtable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6hXI8j_Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8iyh6ljbec1gm3hzbkx7.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6hXI8j_Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8iyh6ljbec1gm3hzbkx7.jpeg" alt="colisão de chaves" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se cada posição da hashtable pode armazenar apenas um item, como resolveremos isso?&lt;/p&gt;

&lt;h2&gt;
  
  
  Chaining
&lt;/h2&gt;

&lt;p&gt;Chaining é uma estratégia onde as posições da hashtable armazenam um ponteiro para uma estrutura de dado sequencial, essa estrutura sequencial pode ser um array dinâmico ou uma lista encadeada.&lt;/p&gt;

&lt;p&gt;Após a chave passar pela hash function, é necessário efetuar uma busca linear na estrutura sequencial daquele índice da hashtable e comparar as chaves que estão na lista.&lt;/p&gt;

&lt;p&gt;Essa busca linear não é um problema desde que a lista encadeada permaneça muito pequena. Para isso, a função de hash precisa evitar ao máximo as colisões.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jy62cAP1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kz30mgz5oygrnngrkcf3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jy62cAP1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kz30mgz5oygrnngrkcf3.png" alt="ponteiros para listas encadeadas no método de chaining" width="572" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Addressing
&lt;/h2&gt;

&lt;p&gt;Não considero uma solução muito adequada, pois precisamos garantir que todas chaves terão um lugar próprio na hashtable. Isso significa que se o usuário necessitar de 10^9 chaves, nossa hashtable terá 10^9 índices.&lt;/p&gt;

&lt;p&gt;Mas como se resolve o problema de colisões em open addressing? Uma das técnicas é chamada de &lt;strong&gt;linear probing&lt;/strong&gt;. Se o retorno da função de hash é de um índice já utilizado, simplesmente executamos a função até que um índice vazio seja encontrado.&lt;/p&gt;

&lt;p&gt;A próxima seção será especificamente sobre hash functions, mas por agora considere M como o tamanho atual da hashtable e hash(chave) como sendo a chamada da função de hash.&lt;/p&gt;

&lt;p&gt;Sendo assim, para buscar um índice vazio utilizando linear probing podemos fazer:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Se hash(chave) % M não está vazio, tentamos (hash(chave) + 1) % M.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Se (hash(chave) + 1) % M não está vazio, tentamos (hash(chave) + 2) % M, e assim por diante.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Existem outras estratégias para lidar com o problema de colisões em open addressing, algumas delas podem ser encontradas nas referências. Geralmente não é uma técnica utilizada pela dificuldade de implementação de acordo com a técnica para escapar de colisões e por quase sempre não conhecermos previamente o total de chaves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hash Functions
&lt;/h2&gt;

&lt;p&gt;Após visto duas técnicas para implementação de hashtable, precisamos saber como implementar efetivamente a função de hash. Afinal, essa implementação é o coração da estrutura de dado, pois precisa praticamente evitar colisões distribuindo as chaves pela hashtable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Método da Divisão — Hash Function
&lt;/h2&gt;

&lt;p&gt;É o método mais simples para mapear um inteiro que pertence a um domínio de dados grande para um domínio menor.&lt;/p&gt;

&lt;p&gt;h(k) = k mod m — (k % m) — , onde k é a chave original que está sendo buscada ou inserida e m é o tamanho atual da hashtable. Sendo assim, todo inteiro k será mapeado para algum índice dentro do domínio da nossa tabela hash.&lt;/p&gt;

&lt;p&gt;Porém, teremos muitas colisões se muitas chaves por mod m começarem a ter o mesmo resto de divisão.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hash Universal
&lt;/h2&gt;

&lt;p&gt;Se quisermos que a performance da estrutura seja independente das chaves. O problema mencionado no método da divisão pode ocorrer com qualquer função de hash escolhida, mas há uma fórmula que gera uma família de funções de hash boas o suficiente para grande parte.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;h(k) = (((ak + b) mod p) mod m)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A fórmula acima é uma geradora de funções de hash. Os valores a e b são escolhidos de forma aleatória, desde que a seja diferente de zero. O valor p é um número primo maior do que m. Por fim, m é o tamanho atual da hashtable.&lt;/p&gt;

&lt;p&gt;Os valores acima não mudam a cada chave que vamos mapear na hashtable, eles são escolhidos e utilizados de forma fixa para distribuir os dados na estrutura. O único momento que esses valores mudam é quando precisamos realocar o tamanho da hashtable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Realocação da Tabela de Hash
&lt;/h2&gt;

&lt;p&gt;Pode ser necessário quando há muito espaço livre na tabela, e para economizar espaço resolvemos diminuí-la ou quando a quantidade de chaves está perto do tamanho total da tabela, nesse caso precisamos aumentá-la.&lt;/p&gt;

&lt;p&gt;Além de escolher uma nova função de hash a partir da fórmula geradora, também precisamos pegar todos os itens que estavam na tabela antiga e passar eles pela nova função da hash para inserirmos na nova tabela. Essa operação custa O(n) tempo e é comum em estruturas que encapsulam arrays estáticos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-spring-2020/lecture-videos/lecture-4-hashing/"&gt;MIT OCW 6.006 - Hashing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.geeksforgeeks.org/hashing-set-3-open-addressing/"&gt;Open Addressing - GeeksForGeeks&lt;/a&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>computerscience</category>
      <category>braziliandevs</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Complexidade de Algoritmos — Big O</title>
      <dc:creator>William Spader</dc:creator>
      <pubDate>Wed, 02 Feb 2022 18:43:11 +0000</pubDate>
      <link>https://forem.com/williamspader/complexidade-de-algoritmos-big-o-59m5</link>
      <guid>https://forem.com/williamspader/complexidade-de-algoritmos-big-o-59m5</guid>
      <description>&lt;p&gt;Determinar a complexidade de um algoritmo é importante para conhecermos a performance de um código. Em Ciência da Computação, é utilizado o método de notação assintótica para definirmos a eficiência dos algoritmos.&lt;/p&gt;

&lt;p&gt;Será abordado a notação Big O com a liberdade de retirar formalismos matemáticos, para que o assunto seja abordado e entendido com maior facilidade intuitiva. Python é a linguagem de programação dos exemplos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Big O
&lt;/h2&gt;

&lt;p&gt;É uma notação assintótica para analisar a eficiência de um algoritmo conforme os valores de entrada crescem, considerando sempre o pior cenário. Em outras palavras, quão rápido cresce o tempo que meu algoritmo demora para resolver o problema em relação ao tamanho do input recebido?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flud8l83njjev93j9jelh.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flud8l83njjev93j9jelh.jpeg" alt="gráfico das principais grandezas assintóticas em big-o"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  O(1)
&lt;/h3&gt;

&lt;p&gt;Chamado de tempo constante, é o menor poder computacional gasto.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ii8xgkni37kowkxqun7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ii8xgkni37kowkxqun7.png" alt="código com cálculos aritméticos e atribuições de valores à variáveis"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como na figura acima, atribuições à variáveis e cálculos aritméticos são exemplos de O(1).&lt;/p&gt;

&lt;h3&gt;
  
  
  O(n)
&lt;/h3&gt;

&lt;p&gt;Significa que o código cresce de forma linear.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxqn8bej8wqwnrr5rcwjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxqn8bej8wqwnrr5rcwjp.png" alt="código contendo um laço for"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A função linear_time é O(n), pois realiza uma iteração em um vetor e as operações dentro do for são de tempo constante O(1). Sempre teremos nos nossos códigos várias notações para cada bloco ou linha, nesse caso sempre levamos em consideração a notação com a maior grandeza.&lt;/p&gt;

&lt;p&gt;Portanto, embora a linha 4 realize uma operação O(1), iremos desconsiderá-la no cálculo pelo fato de possuir impacto muito menor comparada a notação O(n). Caso queira ser um pouco mais preciosista, não há erro em dizer que a função linear_time cresce na grandeza O(n) + O(1) + O(1) + O(1), agora estamos considerando cada operação computacional para determinar a grandeza da função.&lt;/p&gt;

&lt;h3&gt;
  
  
  O(n^2)
&lt;/h3&gt;

&lt;p&gt;Como você ja sabe que um código O(n) é como um loop contendo operações constantes dentro, então O(n^2) são dois loops aninhados com operações constantes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6fn9qbjg0lwzxsycbpwa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6fn9qbjg0lwzxsycbpwa.png" alt="código contendo dois laços for aninhados"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Portanto, sempre que há dois loops aninhados é como se estivéssemos percorrendo uma matriz. No exemplo acima é uma matriz 3x3 (3^2), ou seja, 9 elementos. Logo, O(n^2).&lt;/p&gt;

&lt;h3&gt;
  
  
  O(log(n))
&lt;/h3&gt;

&lt;p&gt;Nesse contexto, considere log na base 2. A partir dessa definição, dizemos que um código é O(log(n)) quando divide pela metade o tamanho do problema a cada etapa.&lt;/p&gt;

&lt;p&gt;A busca binária é um algoritmo bastante conhecido e possui como notação assintótica O(log(n)). Considere um vetor com 16 elementos, e agora vamos aplicar a busca binária nesse vetor para verificar/encontrar um número.&lt;/p&gt;

&lt;p&gt;Se o algoritmo divide o problema pela metade a cada etapa, significa que na primeira execução teremos o vetor com 16 elementos, na segunda 8 elementos, na terceira 4 elementos e assim por diante.&lt;/p&gt;

&lt;p&gt;Considerando log na base 2, temos Log(16) = 2 * 2 * 2 * 2 = 2^4. Isso significa que para um vetor de 16 elementos, a função demorará 4 etapas para cumprir com o objetivo proposto.&lt;/p&gt;

&lt;h3&gt;
  
  
  O(2^n)
&lt;/h3&gt;

&lt;p&gt;Com certeza uma das piores complexidades que nossos algoritmos podem ter, pois cresce exponencialmente baseado na entrada.&lt;/p&gt;

&lt;p&gt;A função de fibonacci é um exemplo que cresce nessa grandeza, abaixo está a árvore de fibonacci criada quando utilizamos a ingênua fórmula F(n + 2) = F(n + 1) + F(n) como algoritmo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvelkgpilesw7p44vacbx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvelkgpilesw7p44vacbx.png" alt="ilustração da árvore de fibonacci"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A árvore acima é criada quando tentamos calcular o quinto número de fibonacci. Percebe que tivemos que calcular o terceiro número de fibonacci duas vezes? Agora pense, quão ruim ia ficar essa árvore se estivéssemos calculando o sexto número de fibonacci? E o sétimo?&lt;/p&gt;

&lt;p&gt;Cada vez que vamos aumentando em apenas 1 número a nossa entrada, o número de operações computacionais cresce exponencialmente.&lt;/p&gt;

&lt;p&gt;Claro que estamos considerando calcular fibonacci de forma recursiva, utilizando a fórmula apresentada anteriormente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Considerações
&lt;/h3&gt;

&lt;p&gt;Sempre realize o exercício de descobrir quão eficiente é o algoritmo que você acabou de criar para solucionar um problema.&lt;/p&gt;

&lt;p&gt;Em talvez contrapartida, tome cuidado para não demorar muito para entregar suas tarefas sendo perfeccionista e sempre buscando a melhor performance possível. Se estiver com dificuldade em visualizar uma forma eficiente, resolva o problema de um jeito simples e depois procure por formas de otimizá-lo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Referências e Links Úteis
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://mitpress.mit.edu/books/introduction-algorithms-third-edition" rel="noopener noreferrer"&gt;Introduction to Algorithms&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.geeksforgeeks.org/analysis-algorithms-big-o-analysis/" rel="noopener noreferrer"&gt;Geeks for Geeks - Analysis of Algorithms&lt;/a&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>computerscience</category>
      <category>complexity</category>
      <category>braziliandevs</category>
    </item>
  </channel>
</rss>
