<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="jjginga.com/feed.xml" rel="self" type="application/atom+xml"/><link href="jjginga.com/" rel="alternate" type="text/html" hreflang="en"/><updated>2025-06-29T17:38:07+00:00</updated><id>jjginga.com/feed.xml</id><title type="html">blank</title><subtitle>Code whisperer by day, bug hunter by night, crafting digital magic with a sprinkle of caffeine and a dash of humor. </subtitle><entry><title type="html">anomie in the workplace - understanding the modern drift</title><link href="jjginga.com/blog/2025/anomie_in_the_workplace/" rel="alternate" type="text/html" title="anomie in the workplace - understanding the modern drift"/><published>2025-06-10T00:00:00+00:00</published><updated>2025-06-10T00:00:00+00:00</updated><id>jjginga.com/blog/2025/anomie_in_the_workplace</id><content type="html" xml:base="jjginga.com/blog/2025/anomie_in_the_workplace/"><![CDATA[<div class="row mt-3"> <div class="col-sm mt-3 mt-md-0"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/desk-480.webp 480w,/assets/img/desk-800.webp 800w,/assets/img/desk-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/desk.jpg" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>We’re constantly coining new terms for the same underlying issue: “quiet quitting,” “boreout,” and a host of others. These aren’t just fads; they’re symptoms of a deeper problem: <strong>anomie</strong>.</p> <p>It’s tempting to blame external factors or specific environments, and they certainly play a role. However, the root cause is often the profound social shifts we’re navigating. For decades, work was a core pillar of identity. You <em>were</em> a doctor, an engineer, a factory worker. Your career path was often linear, offering a clear connection between education, profession, and self-definition.</p> <p>Today, that stability is gone. Social structures are fluid, change is constant, and professional identities are no longer fixed. The result? A significant loss of clarity and stability. This leads directly to that pervasive feeling of being adrift—what sociology calls anomie: a lack of clear direction, purpose, or defined rules.</p> <p>Consider the recent discussions about Gen Z’s approach to leadership, sparked by a recent newspaper article. This debate, while important, risks staying superficial. The real issue isn’t generational; it’s structural. The “liquid identities” of today’s workforce clash with outdated organizational frameworks. We’ve moved past fixed careers and clear, vertical hierarchical structures, yet many companies operate as if nothing has changed.</p> <p>This is where the recruitment process itself often falls short. Many companies still approach hiring by seeking “parts for a machine that no longer exists,” rather than focusing on the agility and transversal skills that are truly needed. They persist in crystallized job profiles, disconnected from the current and future reality of work. This often reflects a lack of strategic thinking about competencies, leading to CV screening processes that are reductive and blind to the very transferable skills that could bring immense value. Without adequate training or strategic vision, those screening CVs often lack the capacity to identify these “liquid identity” talents — precisely the agility and innovation capacity that organizations so desperately need in this new era.</p> <p>The arrival of Artificial Intelligence is absolutely crucial here. AI can be a brutal accelerator for the need for reformulation. However, in companies that haven’t reflected on their own architecture, the advent of AI risks widening the gap and increasing anomie, instead of becoming a “companion.” Its adoption will demand a profound effort of reformulation and rethinking from companies — not just at the process level, but also in the very definition of roles, responsibilities, and collaboration models. For many organizations, especially SMEs and even larger ones, this is a major challenge, as many still lack a structured reflection on their organizational architecture or strategic competency development. In practice, they are light-years away from the maturity needed to effectively integrate these new technologies as true “companions” in their daily business.</p> <p><strong>The challenge is clear: most organizational structures haven’t adapted to this new social reality. The old paradigm—where individuals were expected to conform to the organization—is obsolete. The path forward demands an inversion: organizations must adapt to individuals, embracing their evolving identities and aspirations.</strong></p> <p>True readiness for the future means building systems where people and organizations collaborate, challenging traditional leadership hierarchies, and collectively pursuing objectives. Only then can we bridge the gap created by this modern anomie.</p> <hr/> <h2 id="articles-that-prompted-this-reflection-in-portuguese">Articles that Prompted this Reflection (in Portuguese)</h2> <p>[1] - <a href="https://expresso.pt/semanario/economia/o-ceo-e-o-limite/2025-06-05-a-geracao-z-nao-quer-liderar--e-isso-esta-a-trazer-desafios-a-gestao-das-empresas--952c67d7#Echobox=1749379432-1">Expresso - Z Generation does not want to lead</a> [2] - <a href="https://cnnportugal.iol.pt/videos/ha-um-novo-risco-associado-ao-trabalho-o-boreout/682c347b0cf216cd3ad3680c">CNN - There is a new risk associated to work: the boreout</a></p>]]></content><author><name></name></author><category term="anomie,"/><category term="quiet"/><category term="quitting,"/><category term="boreout,"/><category term="liquid"/><category term="identities,"/><category term="AI,"/><category term="recruitment,"/><category term="organizational"/><category term="design"/><summary type="html"><![CDATA[how 'anomie' explains modern workplace phenomena like quiet quitting and boreout, driven by fluid identities and outdated organizational structures]]></summary></entry><entry><title type="html">the future of development - ai</title><link href="jjginga.com/blog/2024/ai_changing_the_game/" rel="alternate" type="text/html" title="the future of development - ai"/><published>2024-12-26T00:00:00+00:00</published><updated>2024-12-26T00:00:00+00:00</updated><id>jjginga.com/blog/2024/ai_changing_the_game</id><content type="html" xml:base="jjginga.com/blog/2024/ai_changing_the_game/"><![CDATA[<div class="row mt-3"> <div class="col-sm mt-3 mt-md-0"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/robot_coding-480.webp 480w,/assets/img/robot_coding-800.webp 800w,/assets/img/robot_coding-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/robot_coding.jpg" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <h2 id="introduction">Introduction</h2> <p>The rise of advanced AI, particularly models like OpenAI?s O3, is reshaping the software development landscape. OpenAI’s O3 model, announced on December 20, 2024, is designed to enhance reasoning capabilities, enabling it to tackle complex tasks in coding, mathematics, and science. Reportedly, it can perform 72% of tasks a software engineer faces daily, including writing efficient code snippets, debugging, and optimizing algorithms. Early case studies have demonstrated that developers using O3 have seen productivity boosts of up to 40%, especially in repetitive or time-intensive tasks[1].</p> <p>However, such groundbreaking advancements come at a cost. Tools like OpenAI?s O3, while incredibly powerful, are currently resource-intensive and expensive to operate - <strong>costing as much as $1,000 per task</strong>[2]. This limits their accessibility to large organizations or well-funded teams for now.</p> <p>Yet, as with most technologies, history has shown that costs tend to decrease over time as adoption scales and efficiencies improve. A prime example is cloud storage: over the past decade, costs have dropped by more than 80%, making it widely accessible[3]. It?s reasonable to expect AI tools like O3 to follow a similar trajectory, eventually democratizing access and enabling businesses of all sizes to leverage their potential.</p> <h2 id="adapting-to-ai">Adapting to AI</h2> <p>AI is becoming a powerful ally for developers. When used as a tool, models like O3 have the potential to increase the productivity of a single developer to levels that could surpass that of an entire team. These systems can generate, test, and optimize code at a pace humans cannot match. However, they are far from self-sufficient.</p> <p><strong>Developers remain critical for understanding processes, optimization, and requirements?the strategic elements that ensure solutions meet business and user needs</strong>. AI might handle the heavy lifting of generating code, but developers act as directors, testers, and validators, ensuring the AI?s output aligns with the goals of the project.</p> <h2 id="transforming-roles">Transforming Roles</h2> <p><strong>This shift doesn?t eliminate the role of developers?it reconfigures it.</strong> The future developer will not just write code but will leverage AI to accelerate workflows, reduce repetitive tasks, and focus on higher-order problem-solving. The goal is not merely to perform tasks that AI can handle but to achieve outcomes that would be impossible without it.</p> <p>Much like the role of online tools in education, AI offers immense opportunities to enhance productivity but carries the risk of becoming a crutch. Developers must avoid letting AI do all the work and instead focus on becoming individuals capable of using AI to push the boundaries of innovation. The question is not what AI can do for you, but how you can leverage it to achieve what was once impossible.</p> <h2 id="the-path-forward">The Path Forward</h2> <p>To thrive, developers will need to:</p> <ul> <li><strong>Understand Requirements</strong>: Translating business needs into actionable tasks for AI.</li> <li><strong>Validate and Test</strong>: Ensuring AI-generated solutions are secure, efficient, and practical.</li> <li><strong>Optimize Solutions</strong>: Fine-tuning AI outputs for performance, scalability, and alignment with the bigger picture.</li> </ul> <p>The key is to use AI as an amplifier of human potential. Those who use it effectively will multiply their impact and unlock new possibilities, while those who rely on it passively may find themselves left behind.</p> <h2 id="a-symbiotic-relationship">A Symbiotic Relationship</h2> <p>AI is a tool, not a replacement. It is there to complement developers’ skills and enable them to reach new heights, not to render them obsolete. This symbiotic relationship requires both adaptability and critical thinking. As developers, we must learn to embrace AI as a partner in creativity and problem-solving, using it to complete our abilities and achieve what was previously impossible.</p> <p><strong>As the field evolves, one thing is clear: developers who embrace AI will thrive. Those who resist might find themselves outpaced?not because AI eliminates their role, but because it redefines it.</strong></p> <hr/> <h2 id="sources">Sources</h2> <p>[1] - <a href="https://arstechnica.com/information-technology/2024/12/openai-announces-o3-and-o3-mini-its-next-simulated-reasoning-models/">Ars Technica</a><br/> [2] - <a href="https://techcrunch.com/2024/12/23/openais-o3-suggests-ai-models-are-scaling-in-new-ways-but-so-are-the-costs/">TechCrunch</a><br/> [3] - <a href="https://wasabi.com/blog/industry/cloud-storage-fee-inflation">Wasabi Blog</a></p> <hr/> <h2 id="further-reading">Further Reading</h2> <ul> <li><a href="https://en.wikipedia.org/wiki/OpenAI_o3">Wikipedia on OpenAI O3</a></li> <li><a href="https://medium.com/@tsecretdeveloper/why-openais-o3-won-t-replace-you-yet-21699ac3d5c6">Why OpenAI?s O3 Won?t Replace You Yet</a></li> </ul> <hr/> <h2 id="image-source">Image Source</h2> <p>Image by <a href="https://www.freepik.com/">Freepik</a>.</p>]]></content><author><name></name></author><category term="AI,"/><category term="software"/><category term="development,"/><category term="OpenAI,"/><category term="productivity,"/><category term="innovation,"/><category term="o3,"/><category term="software"/><category term="engineering,"/><category term="developer,"/><category term="development"/><summary type="html"><![CDATA[a reflection on how ai tools like OpenAI's o3 are reshaping the role of developers, amplifying their capabilities rather than replacing them.]]></summary></entry><entry><title type="html">railway station puzzle</title><link href="jjginga.com/blog/2024/railway-station-problem/" rel="alternate" type="text/html" title="railway station puzzle"/><published>2024-05-25T00:00:00+00:00</published><updated>2024-05-25T00:00:00+00:00</updated><id>jjginga.com/blog/2024/railway-station-problem</id><content type="html" xml:base="jjginga.com/blog/2024/railway-station-problem/"><![CDATA[<h2 id="the-railway-station-puzzle">The Railway Station Puzzle</h2> <p>The Railway Station Puzzle aims to optimize the placement of railway stations to minimize the number of stations and the average travel cost for families. This problem is inspired by historical railway expansion, requiring efficient computational solutions. To understand the intricacies and rules of this puzzle, explore the problem statement on <a href="https://github.com/jjginga/railway-station-puzzle">GitHub</a>.</p> <p><em>The problem as described in the README of the repository and in an attached PDF is a practical component of a course in my university, heard some collegues talking about it and decided to try.</em></p> <h2 id="problem-definition">Problem Definition</h2> <p>The task involves an NxM grid representing a map where each cell contains a number of families. The goal is to minimize the number of railway stations (A) and the average travel cost (B) to the nearest station. The travel cost is defined by distance with specified unit costs. The overall cost function is:</p> \[\text{Cost} = 1000A + 100B\] <table> <thead> <tr> <th>Component</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>Objective</td> <td>Minimize the number of stations and the average travel cost</td> </tr> <tr> <td>Initial State</td> <td>An empty grid with no stations</td> </tr> <tr> <td>Possible Actions</td> <td>Place a station in any cell</td> </tr> <tr> <td>Transition Model</td> <td>Update grid state with the new station placement</td> </tr> <tr> <td>Cost</td> <td>Calculated based on the number of stations and the average travel distance</td> </tr> <tr> <td>Successors</td> <td>All possible grids with one additional station</td> </tr> <tr> <td>Solution</td> <td>Grid configuration that meets the objective with minimum cost</td> </tr> <tr> <td>Constraints</td> <td>Average travel cost must be less than 3</td> </tr> </tbody> </table> <h3 id="graph-representation">Graph Representation</h3> <p>The problem is represented as a grid where each cell is a zone containing a number of families. The nodes are the zones, and edges represent the possible placements of stations.</p> <p>This table succinctly encapsulates the problem’s components, providing a clear framework for the “Land Permutation Problem.” The constraints ensure that the number of borders in a successor state must not exceed that of the current state, guiding the search towards the objective.</p> <h2 id="approach-and-algorithms">Approach and Algorithms</h2> <h3 id="informed-search-algorithms">Informed Search Algorithms</h3> <p>Informed search algorithms use heuristics to guide the search process, making them more efficient than uninformed search methods. These algorithms prioritize exploring paths that are more likely to lead to the goal, thus reducing the search space and time.</p> <h4 id="what-is-a-heuristic">What is a Heuristic?</h4> <p>A heuristic is a technique used to estimate the cost of reaching the goal from a given state. It provides an educated guess based on available information, helping to prioritize certain paths over others.</p> <h4 id="why-use-heuristics-instead-of-cost">Why Use Heuristics Instead of Cost?</h4> <p>Heuristics allow algorithms to make decisions based on estimated future costs, leading to more efficient searches. By using heuristics, informed search algorithms can quickly discard less promising paths, focusing computational resources on more likely solutions.</p> <h3 id="a-algorithm">A* Algorithm</h3> <p>A* combines the actual cost to reach a node and the heuristic estimated cost to the goal, ensuring that the path found is both optimal and efficient.</p> <h4 id="how-a-works">How A* Works:</h4> <ol> <li><strong>Initialization</strong>: Starts from the initial node, using a priority queue to manage the frontier.</li> <li><strong>Cost Function</strong>: Uses the evaluation function \(( f(n) = g(n) + h(n) )\), where \(( g(n) )\) is the actual cost and \(( h(n) )\) is the heuristic estimate.</li> <li><strong>Node Expansion</strong>: Expands the node with the lowest \(( f(n) )\) value.</li> <li><strong>Path Finding</strong>: Continues until the goal node is reached, ensuring the path with the minimum cost is found.</li> </ol> <h3 id="best-first-search">Best-First Search</h3> <p>Best-First Search uses only the heuristic estimate to guide the search, always expanding the most promising node based on the heuristic value.</p> <h4 id="how-best-first-search-works">How Best-First Search Works:</h4> <ol> <li><strong>Initialization</strong>: Begins at the start node, with a priority queue to handle the frontier.</li> <li><strong>Heuristic Evaluation</strong>: Uses a heuristic function \(( h(n) )\) to estimate the cost to the goal.</li> <li><strong>Node Expansion</strong>: Prioritizes nodes with the lowest heuristic value.</li> <li><strong>Efficiency</strong>: Can quickly find a solution, but may not always find the optimal path compared to A*.</li> </ol> <h3 id="algorithm-selection">Algorithm Selection</h3> <p>Informed search algorithms are chosen for their efficiency because they use heuristics to guide the search process. Unlike blind search methods, which explore all possible paths without direction, informed search algorithms focus on the most promising paths based on heuristic estimates. This reduces the search space and time required to find a solution. For the Railway Station Puzzle, using an informed search like Best-First Search ensures that both the number of stations and the average travel cost are minimized effectively, providing optimal results more efficiently.</p> <h3 id="why-use-best-first-search-for-the-railway-station-puzzle">Why Use Best-First Search for the Railway Station Puzzle</h3> <p>Best-First Search (BFS) is used in your Railway Station Puzzle implementation for several reasons:</p> <h4 id="1-heuristic-driven-efficiency">1. Heuristic-Driven Efficiency</h4> <ul> <li><strong>Heuristic Focus</strong>: Best-First Search uses a heuristic to guide the search process, prioritizing nodes that are estimated to be closer to the goal. This can be particularly effective in problems where a good heuristic can significantly reduce the search space.</li> <li><strong>Problem-Specific Heuristic</strong>: In this implementation, the heuristic is based on the distance to the nearest station and the number of families affected, which directly aligns with the goal of minimizing the total cost.</li> </ul> <h4 id="2-memory-management">2. Memory Management</h4> <ul> <li><strong>Simplified Cost Handling</strong>: Best-First Search avoids the need to manage and update the actual cost (g(n)) for each node, focusing solely on the heuristic (h(n)). This reduces the complexity of state management and can lead to better performance in terms of memory usage.</li> <li><strong>Priority Queue Efficiency</strong>: Using a priority queue based on heuristic values allows the algorithm to efficiently manage and retrieve the most promising nodes without the overhead of combining costs.</li> </ul> <h4 id="3-implementation-suitability">3. Implementation Suitability</h4> <ul> <li><strong>Straightforward Heuristic Application</strong>: The heuristic calculation in this implementation is straightforward and effectively guides the search towards solutions that minimize travel costs for families.</li> <li><strong>Focused Search</strong>: By prioritizing nodes with the lowest heuristic values, Best-First Search can quickly hone in on promising areas of the search space, which is suitable for the structure of the Railway Station Puzzle.</li> </ul> <h3 id="why-best-first-search-is-better-for-this-problem">Why Best-First Search Is Better for This Problem</h3> <h4 id="1-effective-heuristic-utilization">1. Effective Heuristic Utilization</h4> <ul> <li><strong>Quality of Heuristic</strong>: The heuristic used in this implementation is effective in estimating the cost of reaching the goal. It accurately reflects the problem constraints by considering both the distance to the nearest station and the number of families affected, leading to efficient pathfinding.</li> </ul> <h4 id="2-reduced-computational-overhead">2. Reduced Computational Overhead</h4> <ul> <li><strong>Simpler Calculations</strong>: Best-First Search requires fewer calculations per node compared to A*, as it only uses heuristic values for prioritization. This can result in faster execution, particularly for large search spaces.</li> </ul> <h4 id="3-flexibility-in-state-expansion">3. Flexibility in State Expansion</h4> <ul> <li><strong>State Expansion Based on Heuristics</strong>: The algorithm expands nodes based on heuristic values, which can be particularly useful in problems where the heuristic provides a strong indication of the optimal path. This aligns well with the goal of minimizing station placements and travel costs.</li> </ul> <h3 id="conclusion">Conclusion</h3> <p>Best-First Search is a suitable choice for the Railway Station Puzzle because it leverages an effective heuristic to guide the search process, reducing the search space and computational overhead. By focusing on heuristic values, the algorithm can efficiently navigate towards solutions that minimize the total cost, making it a practical approach for this optimization problem.</p> <h3 id="heuristic-selection">Heuristic Selection</h3> <p>The heuristic used in this problem balances the number of stations and the travel cost:</p> \[\text{Heuristic} = \sum (\text{Families} \times \text{MinDistance}) \times \left(1 + \frac{\text{Number of Stations}}{\text{Total Families}}\right)\] <p>This ensures that the search process focuses on minimizing both the number of stations and the average travel cost, making it well-suited for the A* algorithm.</p> <h2 id="implementation">Implementation</h2> <p>To solve the Railway Station Puzzle, the problem was implemented in a structured manner using a Best-First Search algorithm. The solution includes:</p> <ul> <li>A <code class="language-plaintext highlighter-rouge">RailwayStation</code> class that defines the state space of the problem, including the map layout, station placements, sucessor state generation, heuristc calculation and cost calculations.</li> <li>A <code class="language-plaintext highlighter-rouge">BestFirst</code> class that implements the Best-First Search algorithm to find the optimal placement of stations.</li> <li>A <code class="language-plaintext highlighter-rouge">DistanceMapViewer</code> class to visualize the results using JavaFX.</li> </ul> <h2 id="problem-modeling">Problem Modeling</h2> <p>The <strong>RailwayStation</strong> class represents the state space, defining the layout of the map, the placement of stations, and the cost calculations. The cost calculation is done using the formula provided in the problem statement, the heuristic is calculated using the formula provided above and a set is used to prevent states that are already in the queue of being added again (and having the cost of calculating the heuristic again). These are all fundamental for the algorithm.</p> <h4 id="algorithms">Algorithms</h4> <p>In the implementation of the search algorithms we used the tools provided to us by java, we use a <strong>PriorityQueue</strong> to store the generated states after each interaction, and for this we also overriden the <strong>compareTo</strong> method in the state to use the heuristic. We also use a <strong>HashSet</strong> to keep track of the states already explored and since it has the states we overriden the <strong>equals</strong> and <strong>hashCode</strong>.</p> <p>So on every iteration, we get the state with the best heuristic from the Priority Queue, we calculate its cost, check if it is a valid solution, if it is, we compare it with the best solution we have so far. Generate its children and add them to the priority queue. The algorithm goes on until all the state space is explored or until a minute or 100000 evaluations (of the cost), have been met.</p> <h3 id="result-presentation">Result Presentation</h3> <p>The <strong>DistanceMapViewer</strong> class visualizes the results using JavaFX. This class is responsible for presenting the map, station placements, and various statistics such as average cost, total cost, evaluations, and generations. The visualization provides a clear and interactive way to analyze the performance of the algorithm and the effectiveness of the station placements.</p> <h4 id="color-coding-of-the-map">Color Coding of the Map:</h4> <ul> <li><strong>Green</strong>: Distance 0 (station location)</li> <li><strong>Light Blue</strong>: Distance 1</li> <li><strong>Yellow</strong>: Distance 2</li> <li><strong>Orange</strong>: Distance 3</li> <li><strong>Light Coral</strong>: Distance 4</li> <li><strong>Red</strong>: Distance 5</li> <li><strong>Light Gray</strong>: Distance 6</li> </ul> <div class="row mt-3"> <div class="col-sm mt-3 mt-md-0"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/map_railway-480.webp 480w,/assets/img/map_railway-800.webp 800w,/assets/img/map_railway-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/map_railway.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <h2 id="results">Results</h2> <p>The table below presents the outcomes of applying the Best-First Search algorithm to the Railway Station Puzzle across 20 different instances. Each entry includes the number of stations placed, the average travel cost, the total cost, the number of evaluations, the number of states generated, and the execution time.</p> <table> <thead> <tr> <th>Instance</th> <th>Stations</th> <th>Average Cost</th> <th>Total Cost</th> <th>Evaluations</th> <th>Generated States</th> <th>Execution Time</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>[[3, 1]]</td> <td>2.48</td> <td>1247</td> <td>100000</td> <td>101653</td> <td>0s</td> </tr> <tr> <td>2</td> <td>[[2, 2]]</td> <td>1.98</td> <td>1197</td> <td>100000</td> <td>101009</td> <td>0s</td> </tr> <tr> <td>3</td> <td>[[2, 3]]</td> <td>2.95</td> <td>1295</td> <td>100000</td> <td>114061</td> <td>1s</td> </tr> <tr> <td>4</td> <td>[[3, 4], [6, 2]]</td> <td>1.42</td> <td>2141</td> <td>100000</td> <td>115284</td> <td>1s</td> </tr> <tr> <td>5</td> <td>[[4, 3], [7, 6], [1, 2]]</td> <td>2.28</td> <td>3227</td> <td>100000</td> <td>184671</td> <td>4s</td> </tr> <tr> <td>6</td> <td>[[6, 4], [1, 2]]</td> <td>2.57</td> <td>2256</td> <td>100000</td> <td>159621</td> <td>2s</td> </tr> <tr> <td>7</td> <td>[[5, 4], [8, 7], [1, 6], [9, 2]]</td> <td>2.71</td> <td>4270</td> <td>100000</td> <td>280044</td> <td>10s</td> </tr> <tr> <td>8</td> <td>[[3, 4], [3, 9], [4, 0]]</td> <td>2.15</td> <td>3215</td> <td>100000</td> <td>188545</td> <td>4s</td> </tr> <tr> <td>9</td> <td>[[8, 4], [9, 11], [1, 7], [4, 2], [1, 11]]</td> <td>2.93</td> <td>5293</td> <td>100000</td> <td>425080</td> <td>29s</td> </tr> <tr> <td>10</td> <td>[[5, 8], [7, 2], [1, 6], [9, 9]]</td> <td>2.96</td> <td>4296</td> <td>100000</td> <td>524928</td> <td>17s</td> </tr> <tr> <td>11</td> <td>[[9, 5], [1, 9], [6, 1], [11, 10], [1, 2]]</td> <td>2.70</td> <td>5270</td> <td>100000</td> <td>459776</td> <td>22s</td> </tr> <tr> <td>12</td> <td>[[8, 12], [3, 5], [10, 3], [6, 9], [12, 13]]</td> <td>2.50</td> <td>5249</td> <td>100000</td> <td>591103</td> <td>26s</td> </tr> <tr> <td>13</td> <td>[[5, 10], [9, 15], [8, 3], [2, 8], [11, 7], [3, 1]]</td> <td>2.31</td> <td>6231</td> <td>100000</td> <td>651655</td> <td>50s</td> </tr> <tr> <td>14</td> <td>[[4, 5], [6, 12], [11, 1], [12, 15], [5, 2], [3, 7]]</td> <td>2.74</td> <td>6273</td> <td>100000</td> <td>802805</td> <td>32s</td> </tr> <tr> <td>15</td> <td>[[3, 15], [11, 5], [2, 7], [10, 15], [2, 2], [9, 8]]</td> <td>2.97</td> <td>6297</td> <td>50278</td> <td>722772</td> <td>60s</td> </tr> <tr> <td>16</td> <td>[[7, 10], [10, 3], [9, 14], [2, 11], [4, 2], [9, 6]]</td> <td>2.65</td> <td>6265</td> <td>100000</td> <td>639612</td> <td>35s</td> </tr> <tr> <td>17</td> <td>[[3, 14], [10, 4], [11, 15], [4, 3], [8, 11], [0, 8], [2, 17]]</td> <td>2.57</td> <td>7257</td> <td>10510</td> <td>810996</td> <td>60s</td> </tr> <tr> <td>18</td> <td>[[8, 9], [2, 3], [4, 14], [9, 15], [8, 1], [10, 7], [4, 7]]</td> <td>2.42</td> <td>7241</td> <td>100000</td> <td>599432</td> <td>43s</td> </tr> <tr> <td>19</td> <td>[[8, 12], [3, 4], [3, 13], [12, 9], [11, 15], [10, 2], [7, 17]]</td> <td>2.68</td> <td>7268</td> <td>9909</td> <td>592817</td> <td>60s</td> </tr> <tr> <td>20</td> <td>[[8, 4], [7, 14], [13, 15], [1, 8], [1, 2], [9, 1], [4, 16]]</td> <td>2.92</td> <td>7291</td> <td>6327</td> <td>1252650</td> <td>60s</td> </tr> </tbody> </table> <h3 id="discussion">Discussion</h3> <p>The results of the Best-First Search algorithm on the Railway Station Puzzle reveal several key points about its performance:</p> <ol> <li><strong>Evaluation Consistency</strong>: Across all instances, the algorithm consistently performed 100,000 evaluations, indicating that it fully utilized the allowed computation budget in most cases.</li> <li><strong>Execution Time</strong>: The execution times varied significantly, from 0 seconds to 60 seconds. The instances that reached the 60-second mark did not find an optimal solution within the time limit, suggesting increased complexity and larger state spaces in these cases.</li> <li><strong>Generation of States</strong>: The number of generated states varied widely, from around 100,000 to over 1,250,000. This highlights the varying complexity of different instances and the corresponding impact on search space exploration.</li> <li><strong>Solution Quality</strong>: The average travel costs ranged from 1.42 to 2.97, and the total costs ranged from 1197 to 7291. Instances with more stations generally had higher total costs but sometimes lower average costs, indicating a trade-off between the number of stations and travel efficiency.</li> </ol> <p>The table provides a comprehensive overview of the algorithm’s performance, reflecting its strengths in efficiently exploring large search spaces and generating numerous potential solutions. However, the instances that hit the 60-second limit reveal areas where the algorithm struggles with time complexity, suggesting that further optimizations or alternative approaches may be needed for more complex scenarios.</p> <h2 id="further-information">Further Information</h2> <p>For readers interested in learning more about heuristic search algorithms and their applications, the following resources provide comprehensive explanations and examples:</p> <ul> <li><a href="https://www.geeksforgeeks.org/a-search-algorithm/">Geeks for Geeks - A* Algorithm</a>: An in-depth article on the A* search algorithm, explaining its methodology, implementation techniques, and practical applications.</li> <li><a href="https://www.geeksforgeeks.org/best-first-search-informed-search/">Geeks for Geeks - Best-First Search</a>: A detailed guide on Best-First Search, including its algorithmic approach, implementation, and use cases.</li> <li><a href="https://www.javatpoint.com/heuristic-techniques">Javatpoint - Heuristic Techniques</a>: An informative piece on heuristic techniques, covering various heuristic search algorithms and their applications.</li> <li><a href="https://link.springer.com/chapter/10.1007/978-3-319-07153-4_4">SpringerLink - An Overview of Heuristics and Metaheuristics</a>: A comprehensive overview of heuristics and metaheuristics, including traditional local search methods and advanced metaheuristic algorithms.</li> <li>Russell, S., &amp; Norvig, P. (2001). <em>Artificial Intelligence: A Modern Approach (3rd ed)</em>. <a href="https://aima.cs.berkeley.edu/">AIMA</a>: A seminal textbook in the field of artificial intelligence, providing a thorough grounding in search algorithms, heuristic methods, and a broad range of AI topics.</li> </ul> <p>These resources offer valuable insights and deeper understanding for anyone looking to expand their knowledge of heuristic search algorithms and their implementation in solving complex problems like the Railway Station Puzzle.</p>]]></content><author><name></name></author><category term="algorithms"/><category term="java"/><category term="programming"/><category term="artificialintelligence"/><category term="problemsolving"/><category term="optimization"/><summary type="html"><![CDATA[tackling the railway station puzzle - a quest for minimized cost]]></summary></entry><entry><title type="html">land permutation puzzle</title><link href="jjginga.com/blog/2024/land_permutation_puzzle/" rel="alternate" type="text/html" title="land permutation puzzle"/><published>2024-03-23T00:00:00+00:00</published><updated>2024-03-23T00:00:00+00:00</updated><id>jjginga.com/blog/2024/land_permutation_puzzle</id><content type="html" xml:base="jjginga.com/blog/2024/land_permutation_puzzle/"><![CDATA[<h2 id="the-land-permutation-puzzle">The Land Permutation Puzzle</h2> <p>In the intricate tapestry of computational challenges, the “Land Permutation Problem” stands out. It’s a deceivingly straightforward puzzle derived from the artificial intelligence course in computer science degree, involving a matrix of lands owned by various proprietors. The goal is to minimize the number of different owners’ borders. This territorial jigsaw requires both insightful analysis and strategic algorithmic application. To understand the intricacies and rules of this puzzle, explore the problem statement on <a href="https://github.com/jjginga/LandPermutationProblem">GitHub</a>.</p> <p><em>The problem as described in the README of the repository is a practical component of my studies.</em></p> <h4 id="problem-definition">Problem Definition</h4> <p>The task involves a given matrix of NxM houses representing a map of lands of equal dimension with K owners. The goal is to reduce the number of borders between lands of different owners to a number equal to or less than W. A border exists between two houses with different owners. Owners can be represented by colors, letters, or numbers, with numbers being used in our program.</p> <table> <thead> <tr> <th>Component</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><strong>Objective</strong></td> <td>test(s) ≤ W</td> </tr> <tr> <td><strong>Initial State</strong></td> <td>s0 ∈ S</td> </tr> <tr> <td><strong>Possible Actions</strong></td> <td>A = {(n, m, n’, m’) | lands (n,m) and (n’,m’) are adjacent}</td> </tr> <tr> <td><strong>Transition Model</strong></td> <td>exe((s, (n, m, n’, m’))) = s’ where s’ is the matrix resulting from exchanging the owner of (n,m) and (n’, m’).</td> </tr> <tr> <td><strong>Cost</strong></td> <td>-</td> </tr> <tr> <td><strong>Successors</strong></td> <td>succ(s) = {s’ | ∃a ∈ A, s’ = exe(s, a)}</td> </tr> <tr> <td><strong>Solution</strong></td> <td>sf ∈ S | test(sf)</td> </tr> <tr> <td><strong>Constraints</strong></td> <td>test(s’) ≤ test(s)</td> </tr> </tbody> </table> <p>This table succinctly encapsulates the problem’s components, providing a clear framework for the “Land Permutation Problem.” The constraints ensure that the number of borders in a successor state must not exceed that of the current state, guiding the search towards the objective.</p> <h2 id="navigating-solutions-bfs-dfs-and-iddfs-explained">Navigating Solutions: BFS, DFS, and IDDFS Explained</h2> <p>This problem — minimizing the number of borders between lands with different owners — can be modeled as a graph traversal challenge. Each configuration of the land map, representing territories and their borders, can be considered a node (or vertex) in a graph. The initial state is the root node, and each possible action (e.g., swapping two adjacent territories to potentially reduce borders) leads to a successor state, which is a child node in the graph.</p> <p>So, to solve this enigmatic puzzle we can use three classic algorithms, each with its own strengths and peculiarities. <strong>Breadth-First Search (BFS)</strong> is like casting a wide net, exploring all neighboring nodes at the current depth before diving deeper. It’s methodical, ensuring no stone is left unturned, but its memory consumption can quickly become a concern as the breadth of exploration expands.</p> <p>In contrast, <strong>Depth-First Search (DFS)</strong> opts for a more tunnel-vision approach, diving deep into the problem space one path at a time. Its memory footprint is lighter, making it nimble and efficient in certain mazes of complexity. However, its laser focus can sometimes be a drawback, as it might miss broader solutions found at shallower depths.</p> <p><strong>Iterative Deepening Depth-First Search (IDDFS)</strong>, then, marries the thoroughness of BFS with the memory efficiency of DFS. By incrementally deepening the search depth, IDDFS systematically covers the search space, ensuring completeness without the heavy memory burden associated with BFS.</p> <div class="row mt-3"> <div class="col-sm mt-3 mt-md-0"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/search_algorithms-480.webp 480w,/assets/img/search_algorithms-800.webp 800w,/assets/img/search_algorithms-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/search_algorithms.jpg" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <h2 id="breadth-first-search-bfs">Breadth-First Search (BFS)</h2> <p>Breadth-First Search (BFS) systematically covers a graph by visiting adjacent vertices in successive levels.</p> <h4 id="how-bfs-works">How BFS Works:</h4> <ol> <li><strong>Initialization</strong>: BFS begins with the root node (initial state) and explores all its neighbors (successor states).</li> <li><strong>Queue Management</strong>: It employs a queue to keep track of the frontier—the set of all nodes visible but not yet explored.</li> <li><strong>Uniform Exploration</strong>: At each step, BFS dequeues the next node from the frontier, examines it for a solution, and enqueues all its unvisited neighbors.</li> <li><strong>Memory Consideration</strong>: The major downside of BFS in complex puzzles like land permutation is its memory usage, which grows exponentially with the number of levels explored.</li> </ol> <h2 id="depth-first-search-dfs">Depth-First Search (DFS)</h2> <p>Depth-First Search delves into a graph, extending as far along each path as possible before backtracking to explore other branches.</p> <h4 id="how-dfs-operates">How DFS Operates:</h4> <ol> <li><strong>Initialization</strong>: Starting at the root, DFS pushes the initial state onto a stack.</li> <li><strong>Stack Utilization</strong>: It utilizes a stack to manage the nodes currently being explored.</li> <li><strong>Depth Exploration</strong>: DFS continuously explores down one branch until it hits a dead end, then backtracks to explore other branches.</li> <li><strong>Memory Efficiency</strong>: Its stack-based approach limits memory usage to the maximum depth of the search space, unlike BFS’s breadth-based memory expansion.</li> <li><strong>Solution Depth</strong>: While efficient, DFS is not guaranteed to find the shortest solution in terms of moves or transformations.</li> </ol> <h2 id="iterative-deepening-depth-first-search-iddfs">Iterative Deepening Depth-First Search (IDDFS)</h2> <p>Iterative Deepening Depth First Search (IDDFS) progressively deepens the reach of Depth-First Search, applying a breadth-like approach to depth-limited searches.</p> <h4 id="how-iddfs-functions">How IDDFS Functions:</h4> <ol> <li><strong>Depth Iteration</strong>: Starting with a shallow depth limit, IDDFS performs a DFS within this limit. If no solution is found, the limit is increased, and the search is repeated.</li> <li><strong>Balance of Efficiency</strong>: This approach ensures that the memory advantages of DFS are maintained while achieving the breadth of coverage that BFS offers.</li> <li><strong>Optimality</strong>: Like BFS, IDDFS will find the optimal solution in terms of the shortest path to the goal.</li> <li><strong>Revisitation</strong>: Each iteration revisits nodes from previous depths, which is the trade-off for its balance between BFS and DFS advantages.</li> </ol> <h2 id="comparassion">Comparassion</h2> <table> <thead> <tr> <th>Algorithm</th> <th>Time Complexity</th> <th>Space Complexity</th> <th>Characteristics</th> </tr> </thead> <tbody> <tr> <td><strong>BFS</strong></td> <td>O( \(b^d\) )</td> <td>O( \(b^d\) )</td> <td>Complete and optimal; explores all neighbors at a given depth before moving deeper. High memory use due to storage of all nodes at the current level.</td> </tr> <tr> <td><strong>DFS</strong></td> <td>O( \(b^m\) )</td> <td>O(bm)</td> <td>Not necessarily complete or optimal; explores as far as possible along a branch before backtracking. Lower memory use as it stores only a single path from the root to a leaf node, along with remaining unexplored siblings for each node on the path.</td> </tr> <tr> <td><strong>IDDFS</strong></td> <td>O( \(b^d\) )</td> <td>O(bd)</td> <td>Combines the advantages of BFS and DFS. Complete and optimal like BFS, but with the memory efficiency of DFS. Iteratively deepens, effectively performing a DFS to a specific depth, then increasing this limit iteratively.</td> </tr> </tbody> </table> <p><strong>Key:</strong></p> <ul> <li><strong>b</strong>: branching factor (the average number of child nodes per node)</li> <li><strong>d</strong>: depth of the shallowest solution</li> <li><strong>m</strong>: maximum depth of the state space (potentially infinite)</li> </ul> <p>This table elucidates the stark contrasts between these search algorithms, particularly in terms of their space and time efficiency. BFS and IDDFS share the same time complexity, reflecting their completeness and ability to find the optimal solution. However, the space complexity of IDDFS is dramatically lower, akin to DFS, making it an appealing choice for problems where space is a limiting factor and completeness is required.</p> <p>This table elucidates the stark contrasts between these search algorithms, particularly in terms of their space and time efficiency. BFS and IDDFS share the same time complexity, reflecting their completeness and ability to find the optimal solution. However, the space complexity of IDDFS is dramatically lower, akin to DFS, making it an appealing choice for problems where space is a limiting factor and completeness is required.</p> <h2 id="implementation">Implementation</h2> <p>To solve this problem I splitted it into two parts:</p> <ul> <li>a “framework” designed to solve state space search problems where the objective is to minimize some goal that can be expressed numerically and can be reused for other problems. For that purpose we implemented a variety of search techniques including Breadth-First Search (BFS), Depth-First Search (DFS), and Iterative Deepening Depth-First Search (IDDFS), each encapsulated within its own class and implementing a common abstract search technique interface.</li> <li>a class that defines the state space of the Land Permutation problem and can be coupled with the “framework”.</li> </ul> <h4 id="problem-modeling">Problem Modeling</h4> <p>The <strong>LandMap</strong> class represents the state space, defining the layout of lands and borders that need to be manipulated. It includes methods for state evaluation (border count), generating successors, and performing swap operations which are fundamental actions of the search algorithms. These actions ensure that only valid moves towards the objective are considered, preventing an increase in the number of borders.</p> <p>The <strong>countBorders</strong> function is designed to iterate through each cell of the land map matrix and check for borders, where a border is defined as a side where adjacent cells have different values (representing different owners). This operation is O(N) where N is the number of cells in the matrix because it performs a constant amount of work for each cell: one comparison with its right neighbor and one with its bottom neighbor (except for cells on the rightmost and bottom edges, which have no neighbors in that direction). Without two loops or nested loops per cell that would increase the time complexity.</p> <p>To <strong>generate the successor states</strong> the border count is tested before creating a new object to ensure the move is beneficial towards achieving the goal of minimizing borders, and this pre-validation step is crucial for maintaining the efficiency of the search algorithms. Each potential swap of adjacent territories in the land map is a candidate move that might lead to a new state; however, not all moves lead to a desirable outcome. By assessing the impact of a swap on the number of borders before actually instantiating a new LandMap object, the algorithm effectively filters out successor states that would not contribute to the solution. This pre-emptive check prevents the unnecessary creation and storage of state objects that do not bring the search any closer to the goal, which would otherwise waste computational resources and potentially slow down the search process due to increased memory usage and garbage collection overhead.</p> <h4 id="algorithms">Algorithms</h4> <p>In the implementation of the search algorithms we went for a clean and modular approach, going for an algorithm design that are applicable to a broad range of problems beyond just the land permutation challenge. The Breadth-First Search (BFS) class utilizes a <strong>Queue</strong> data structure to ensure a level-order traversal. The Depth-First Search (DFS) class employs a <strong>Stack</strong> to dive deep into the search space, backtracking when necessary. This approach is typical for DFS’s deep exploration strategy, which is both memory-efficient and adept at handling problems with vast search spaces.</p> <p>Iterative Deepening Depth-First Search (IDDFS) marries the strengths of both BFS and DFS by combining the memory efficiency of DFS with the completeness of BFS. IDDFS systematically increases the depth limit, essentially performing a DFS to the current limit, then restarting with a deeper limit, allowing for a progressive exploration that is both thorough and space-conscious. Each of these classes is architected to encapsulate the search logic within its own module, using polymorphism through an interface that defines the structure of a search technique, making the algorithms interchangeable and reusable.</p> <h2 id="results">Results</h2> <p>The following table presents the outcomes of applying different search algorithms to the several instances of the land permutation problem with the w1 goals. Each entry outlines the depth reached, the number of states generated, and the execution time recorded for the corresponding search technique and instance. It’s important to note that an execution time marked as “&gt;60s” indicates that the algorithm did not find a solution within the 60-second time limit. For these instances, the depth and generated states reflect the search progress made up to the point of timeout. These metrics are crucial for understanding the performance and efficiency of each algorithm under the constraints of time-limited execution.</p> <table> <thead> <tr> <th>Instance</th> <th>Algorithm</th> <th>Depth</th> <th>Generated States</th> <th>Execution Time</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Breath First Search</td> <td>3</td> <td>16</td> <td>0.0045 s</td> </tr> <tr> <td>1</td> <td>Depth-First Search</td> <td>3</td> <td>10</td> <td>0.0008 s</td> </tr> <tr> <td>1</td> <td>Iterative Deep-First Search</td> <td>3</td> <td>40</td> <td>0.0024 s</td> </tr> <tr> <td>2</td> <td>Breath First Search</td> <td>2</td> <td>9</td> <td>0.0002 s</td> </tr> <tr> <td>2</td> <td>Depth-First Search</td> <td>2</td> <td>5</td> <td>0.0001 s</td> </tr> <tr> <td>2</td> <td>Iterative Deep-First Search</td> <td>2</td> <td>15</td> <td>0.0004 s</td> </tr> <tr> <td>3</td> <td>Breath First Search</td> <td>3</td> <td>5298084</td> <td>&gt;60s</td> </tr> <tr> <td>3</td> <td>Depth-First Search</td> <td>159</td> <td>7788</td> <td>0.0065 s</td> </tr> <tr> <td>3</td> <td>Iterative Deep-First Search</td> <td>3</td> <td>5364301</td> <td>&gt;60s</td> </tr> <tr> <td>4</td> <td>Breath First Search</td> <td>4</td> <td>968</td> <td>0.0007 s</td> </tr> <tr> <td>4</td> <td>Depth-First Search</td> <td>10</td> <td>34</td> <td>0.0001 s</td> </tr> <tr> <td>4</td> <td>Iterative Deep-First Search</td> <td>4</td> <td>1819</td> <td>0.0013 s</td> </tr> <tr> <td>5</td> <td>Breath First Search</td> <td>6</td> <td>146233</td> <td>0.2841 s</td> </tr> <tr> <td>5</td> <td>Depth-First Search</td> <td>18</td> <td>174</td> <td>0.0002 s</td> </tr> <tr> <td>5</td> <td>Iterative Deep-First Search</td> <td>6</td> <td>352495</td> <td>0.7041 s</td> </tr> <tr> <td>6</td> <td>Breath First Search</td> <td>9</td> <td>14146290</td> <td>&gt;60s</td> </tr> <tr> <td>6</td> <td>Depth-First Search</td> <td>77</td> <td>679</td> <td>0.0012 s</td> </tr> <tr> <td>6</td> <td>Iterative Deep-First Search</td> <td>7</td> <td>2336663</td> <td>&gt;60s</td> </tr> <tr> <td>7</td> <td>Breath First Search</td> <td>3</td> <td>609063</td> <td>&gt;60s</td> </tr> <tr> <td>7</td> <td>Depth-First Search</td> <td>216</td> <td>5183</td> <td>0.0059 s</td> </tr> <tr> <td>7</td> <td>Iterative Deep-First Search</td> <td>4</td> <td>8387805</td> <td>&gt;60s</td> </tr> </tbody> </table> <h2 id="discussion">Discussion</h2> <p>The execution of search algorithms on the land permutation problem has demonstrated varied outcomes, which reveal the strengths and weaknesses inherent in each method. The Breath First Search (BFS), noted for its exhaustive level-by-level exploration, has proven effective in shallower instances where the breadth of potential states remains manageable. However, its performance notably diminishes as the complexity of the state space increases, often resulting in timeouts. This indicates that while BFS is thorough, its resource consumption makes it less viable for problems with expansive search trees.</p> <p>Depth-First Search (DFS), on the other hand, showcased its ability to reach deeper into the search space with a significantly lower number of generated states, emphasizing its depth-focused approach. Its memory efficiency shines in instances where the solution lies far from the root, but this comes with the trade-off of potentially overlooking nearer solutions due to its path-focused traversal. The variation in depth and generated states between DFS and BFS highlight the impact of the algorithms’ differing exploration strategies.</p> <p>Iterative Deepening Depth-First Search (IDDFS) strikes a balance between the two, ensuring completeness while retaining memory efficiency. Although IDDFS also faced timeouts in more complex instances, it consistently covered more ground, as indicated by the greater depth reached before the time cap. The iterative nature of IDDFS allows it to comb through the state space methodically, which is particularly beneficial when the solution’s depth is unknown, making it a robust choice for a wide range of scenarios.</p> <p>The results table succinctly captures the essence of these algorithms’ performances and provides a clear comparison that aids in selecting the appropriate approach for different instances of the problem. The trade-offs between time complexity, space complexity, and solution optimality become apparent, informing the decision-making process for algorithm selection in problem-solving.</p> <h2 id="further-information">Further information</h2> <p><a href="https://www.geeksforgeeks.org/breadth-first-search-or-bfs-for-a-graph/">Geeks for Geeks - BFS</a></p> <p><a href="https://www.geeksforgeeks.org/depth-first-search-or-dfs-for-a-graph/">Geeks for Geeks - DFS</a></p> <p><a href="https://www.geeksforgeeks.org/iterative-deepening-searchids-iterative-deepening-depth-first-searchiddfs/">Geeks for Geeks - IDDFS</a></p> <p>Russell, S., &amp; Norvig, P. (2001). <em>Artificial Intelligence: A Modern Approach (3rd ed)</em>. <a href="https://aima.cs.berkeley.edu/">AIMA</a></p>]]></content><author><name></name></author><category term="algorithms"/><category term="java"/><category term="programming"/><category term="artificialintelligence"/><category term="problemsolving"/><category term="optimization"/><summary type="html"><![CDATA[tackling the land permutation puzzle - a quest for minimized borders]]></summary></entry><entry><title type="html">what is a transaction</title><link href="jjginga.com/blog/2024/what_is_a_transaction/" rel="alternate" type="text/html" title="what is a transaction"/><published>2024-03-01T00:00:00+00:00</published><updated>2024-03-01T00:00:00+00:00</updated><id>jjginga.com/blog/2024/what_is_a_transaction</id><content type="html" xml:base="jjginga.com/blog/2024/what_is_a_transaction/"><![CDATA[<h2 id="introduction">Introduction</h2> <p>Imagine you want to transfer money from one bank account to another. To do this there are several steps involve: withdrawing the amount from the first account, depositing the money in the second account. Since money unfortunately doesn’t grow in transactions, both of these operations must be completed successfully, if the withdrawn isn’t made the deposit can’t be done other, the amount deposited must be the same as the amount withdrawn. If it is not possible to complete the deposit the money must return to the account where it was withdrawn from. So these operations must be treated as a single operation or as we can say a single <strong>unit of work</strong>.</p> <p>From banking to baking, now let’s imagine we want to bake chocolate chip cookies, there is a set of actions you have to perform, like, getting all the ingredients, mixing them together, molding the cookies, putting them in the oven and baking them. While you are doing this your mother is also in the kitchen baking a cake. If she were to come and mix some some ingredients of the cake in the mixing bowl while you are preparing your dough, then it would become too runny or too dry. Or imagine the disappointment on your face tasting some bland hard bread because your mother accidentally used the sugar out of your ingredients. So, these operations must be performed with some level of <strong>isolation</strong> to ensure each task’s integrity and success.</p> <p>In both scenarios, the concept of <strong>“transaction”</strong> comes into play. A transaction, in its essence, is a sequence of operations or actions that are treated as a single unit of work. These operations must either all succeed together or fail together, ensuring <strong>data integrity and consistency</strong>. This concept is pivotal in various domains, including database management and software development.</p> <hr/> <h2 id="understanding-transactions">Understanding transactions</h2> <p>We use databases to store information, but, for this information to keep it’s usefulness it must be accessed and modified from time to time. To maintain the <strong>consistency and integrity</strong> of the data present this tasks should be performed systematically with a specific set of rules. In Database Management Systems this is called a <strong>transaction</strong>.</p> <p>Before diving deeper into transactions, let’s first clarify what we mean by <strong>consistency and integrity</strong>, as these are foundational to understanding the role and importance of transactions in database management.</p> <h1 id="consistency-and-integrity">Consistency and Integrity</h1> <p><strong>Consistency</strong> refers to the requirement that any transaction should bring the database from one valid state to another, ensuring that all data follows all rules and constraints (e.g., data types, triggers, constraints) of the database. For instance, in our cookie-baking analogy, consistency would be akin to following a recipe precisely. Just as using the right proportions of ingredients ensures the cookies turn out as expected (not too runny or too dry), in a database, consistency ensures that all data remains accurate and in the correct format throughout any transaction.</p> <p><strong>Integrity</strong>, on the other hand, involves maintaining the accuracy and reliability of the data over its entire lifecycle. This means that the data in the database is always accurate, and any changes made to it are done correctly. Going back to our baking analogy, integrity is similar to ensuring that the sugar intended for your cookies isn’t mistakenly used in your mother’s cake.</p> <p>In summary, consistency and integrity in databases are like following a recipe and using the correct ingredients in baking. Just as each step and ingredient must be correctly followed and used to produce the desired cookie outcome, transactions in a database must adhere to rules that preserve <strong>data consistency and integrity</strong>, ensuring the database remains useful, accurate, and reliable.</p> <h1 id="definition">Definition</h1> <p><a href="https://www.geeksforgeeks.org/transaction-in-dbms/">GeeksForGeeks</a> defines a transaction as <code class="language-plaintext highlighter-rouge">a set of logically related operations. It is the result of a request made by the user to access the contents of the database and perform operations on it. It consists of various operations and has various states in its completion journey. It also has some specific properties that must be followed to keep the database consistent. </code></p> <h1 id="acid">ACID</h1> <p>These proprieties mentioned in the definition followed by transactions are usually referred to has <strong>ACID properties</strong>, they are <strong>Atomicity</strong>, <strong>Consistency</strong>, <strong>Isolation</strong> and <strong>Durability</strong>. We already mentioned Consistency.</p> <p><strong>Atomicity</strong> ensures that a transaction is treated as a single unit of work, meaning, that it either completes in its entirety or it doesn’t happen at all. There is no in-between state. If any part of the transaction fails, the entire transaction is rolled back, and the database state is left unchanged as if the transaction never occurred. Baking cookies is not a good example for this, because, if you realize midway that your oven is broken, you can not return to a state where all the ingredients are separated and in their original container, but, I’m sure you get the idea.</p> <p><strong>Isolation</strong> ensures that transactions are securely isolated from each other. This means the operations of one transaction are hidden from other transactions until it’s completed. This property prevents transactions from interfering with each other, ensuring data integrity. Meaning, that the other transaction in your kitchen - your mother - can not interfere with your cookie baking. Like if both of you have separate workstations separate from each other.</p> <p><strong>Durability</strong> guarantees that once a transaction has been committed, it will remain so, even in the event of a power loss, crash, or error. This means the changes made by the transaction are permanently recorded in the database. Once the cookies are baked and taken out of the oven, they don’t magically disappear if the power goes out - only if you eat them, but that is another story.</p> <p>By adhering to these ACID properties, databases ensure that transactions are processed reliably, maintaining the integrity and consistency of the data.</p> <h1 id="operations">Operations</h1> <p>We mentioned before that a user can make different types of operations to access the contents of the database.</p> <p>The information in the database can be read - <strong>Read(X)</strong> - that is, the value is read from the database and stored in a buffer for displaying or other action. Like checking your account ballance online or your pantry to see if you have all the ingredients needed for the cookies. You are not taking anything out or using it.</p> <p>During a write operation we write - <strong>Write(X)</strong> the value from the memory buffer to the database. It must be preceded by a read operation during which the are brought to the buffer and some operations are performed on it - according to what the user requested. Then the modified value is written in the database. Think of checking you balance (Read) before you withdraw (Write) money from you bank account or you check what you have (Read) before putting all of your ingredients together (Write) to bake your cookies.</p> <p><strong>Commit</strong> is an operation that ensures that the integrity of the database is maintained. Operations are only made permanent after all of the work performed by the current transaction is completed, that is, the changes done by the transaction are made permanent in the database. There may be interruptions on transactions (like an error or a power failure), and this way we ensure that the consistency of the data is maintained. Imagine a Commit like you with or apron and the oven gloves with a batch of cookies ready for baking.</p> <p><strong>Rollback</strong> is intimately connected to the transaction, it’s what happens when a transaction is interrupted, all the operations are undone and the database returns to the original state. The cookie example is not a good one, but, it’s like if you decide that the batch isn’t good, and there is a operation that can return all the ingredients to the pantry.</p> <h1 id="deadlock">Deadlock</h1> <p>When a transaction needs to read or modify data, to ensure that no other transaction makes conflicting changes, it acquires <strong>locks</strong> to certain resources. When two or more transactions hold locks on resources the others need to complete their operations, and none can proceed until the other releases its locks. Each transaction is waiting for the other to finish, but neither can without the resources held by the other. This situation is called a <strong>deadlock</strong>.</p> <p>Deadlocks can significantly hinder database performance, leading to stalled transactions and system inefficiencies. To manage deadlocks, Database Management Systems (DBMS) employ various strategies, including <strong>deadlock detection algorithms</strong> that identify and break deadlocks by aborting one or more transactions to free up resources. Another approach is <strong>deadlock prevention</strong>, which involves designing the database and transactions in a way that avoids the conditions leading to deadlocks.</p> <p>Imagine you and your mother are both trying to bake in a kitchen with only one oven and one oven mint. If you both are ready to bake and you hold the mint and your mother the oven, neither of you can proceed creating “kitchen deadlock.” In databases, similar scenarios require careful management to ensure smooth operation.</p> <h1 id="conclusion">Conclusion</h1> <p>Transactions are fundamental to maintaining the <strong>consistency, integrity, and reliability</strong> of data in database systems. By understanding and implementing the ACID properties, databases can ensure that transactions are processed in a manner that preserves data integrity, even in the face of errors or system failures. Operations like <strong>Read, Write, Commit, and Rollback</strong> play crucial roles in transaction management, while mechanisms to handle deadlocks ensure that databases remain efficient and responsive.</p> <p>Just as following a recipe step by step leads to delicious cookies, adhering to transaction principles ensures that databases function effectively, supporting a wide range of applications from financial services to online shopping. Understanding transactions and their management is essential for anyone working with database systems, providing the foundation for building robust and reliable software solutions.</p> <h2 id="further-information">Further information</h2> <p><a href="https://www.geeksforgeeks.org/transaction-in-dbms/">Geeks for Geeks</a></p> <p><a href="https://www.tutorialspoint.com/dbms/dbms_transaction.htm">Tutorialspoint</a></p> <p><a href="https://www.javatpoint.com/dbms-transaction-processing-concept">Javapoint</a></p>]]></content><author><name></name></author><category term="dbms"/><category term="sql"/><category term="transaction"/><summary type="html"><![CDATA[a short explication of what is a transaction in the context of database management systems]]></summary></entry><entry><title type="html">challenge - two sum</title><link href="jjginga.com/blog/2024/two_sum/" rel="alternate" type="text/html" title="challenge - two sum"/><published>2024-02-29T00:00:00+00:00</published><updated>2024-02-29T00:00:00+00:00</updated><id>jjginga.com/blog/2024/two_sum</id><content type="html" xml:base="jjginga.com/blog/2024/two_sum/"><![CDATA[<h2 id="challenge">Challenge</h2> <p>I had never used leetcode before. In the last few years I spend some time in other similar platforms such has hackerank and codewars, but never leetcode. It crossed my path the other day in some random post on reddit where i read something like “everything starts with two sum”. So I decided to take a look. At first glance the problem <a href="https://leetcode.com/problems/two-sum/">Two Sum</a> seemed quite simple and reminded me of some other basic challenges I had encountered on other platforms. However, as I delved deeper, I realized there was more to this problem than met the eye</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Given an array of integers nums and an integer target, return indices of the two 
numbers such that they add up to target.

You may assume that each input would have exactly one solution, and you may not 
use the same element twice.

You can return the answer in any order.
</code></pre></div></div> <p>For visualization purpose let’s take one of examples, if the input is nums = \([ 15 , 11 , 2 , 7 ]\) and target = 9, the output should be \([ 2 , 3 ]\) , the numbers at indices 2 and 3 (values 2 and 7, respectively) add up to the target value of 9.</p> <p>This problem is significant not only because it’s a common interview question but also because it serves as an accessible introduction to key concepts in computer science, including hashing, array manipulation, and the trade-offs between time and space complexity. Its simplicity allows for a focus on algorithmic thinking and optimization strategies without the overhead of complex data structures or algorithms. Moreover, the problem’s applicability to real-world scenarios, such as database queries or financial transactions, highlights the practical importance of efficient data processing and retrieval techniques, making it a relevant and engaging challenge for programmers at all levels.</p> <h2 id="initial-approach">Initial approach</h2> <p>In a problem like this the the straightforward approach, and I think the one that comes to most people minds is the use of brute force. That is, using a double loop to iterate through the array, comparing each pair of numbers to see if they added up to the target. This method it simple enough, but, it has a time complexity of $$ O ( n^2 ) $, it is far from optimal for larger arrays. Here is this approach in C++, Java and python. This was confirmed when I submitted the code and found that it was only faster than 30% of code submitted by other users.</p> <h2 id="c">C++</h2> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span> <span class="p">{</span>
<span class="nl">public:</span>
    <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">twoSum</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">nums</span><span class="p">,</span> <span class="kt">int</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">();</span>
        
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="o">-</span><span class="mi">1</span> <span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> 
            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">n</span> <span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> 
                <span class="k">if</span> <span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">==</span> <span class="n">target</span><span class="p">)</span> 
                    <span class="k">return</span> <span class="p">{</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">};</span>
                        
        <span class="k">return</span> <span class="p">{};</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div> <h2 id="java">Java</h2> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Solution</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kt">int</span><span class="o">[]</span> <span class="nf">twoSum</span><span class="o">(</span><span class="kt">int</span><span class="o">[]</span> <span class="n">nums</span><span class="o">,</span> <span class="kt">int</span> <span class="n">target</span><span class="o">)</span> <span class="o">{</span>
        <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span>

        <span class="nf">for</span><span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span> <span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="o">-</span><span class="mi">1</span> <span class="o">;</span> <span class="n">i</span> <span class="o">++)</span>
            <span class="k">for</span><span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">i</span><span class="o">+</span><span class="mi">1</span> <span class="o">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">n</span> <span class="o">;</span> <span class="n">j</span><span class="o">++)</span>
                <span class="k">if</span><span class="o">(</span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">]+</span><span class="n">nums</span><span class="o">[</span><span class="n">j</span><span class="o">]==</span><span class="n">target</span><span class="o">)</span>
                    <span class="k">return</span> <span class="k">new</span> <span class="kt">int</span><span class="o">[]{</span><span class="n">i</span><span class="o">,</span><span class="n">j</span><span class="o">};</span>
      
        <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div> <h2 id="python">python</h2> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">twoSum</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">nums</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">target</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
        
        <span class="n">n</span> <span class="o">=</span> <span class="nf">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">):</span>
            <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nf">range </span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
                <span class="nf">if</span><span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">+</span><span class="n">nums</span><span class="p">[</span><span class="n">j</span><span class="p">]</span><span class="o">==</span><span class="n">target</span><span class="p">):</span>
                    <span class="k">return</span> <span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">]</span>
        
        <span class="k">return</span> <span class="p">[]</span>
</code></pre></div></div> <h2 id="time-efficient-with-hash-map">Time efficient with Hash Map</h2> <p>A resounding difference between leetcode and the other platforms I used before is the plethora of discussions, solutions and even videos. I always like to see how a problem can be tackled in multiple ways, each with its own trade-offs in terms of time and space complexity. So i revisited the problem, but this time armed with new insights and a hash table. The idea was to store each element’s value and its index in the array. As I iterated through the array, I checked if the complement of the current element (target - current element) was already in the hash table. If it was, I had found the two numbers.</p> <p>In a hash table the values are stored associating a specific key to corresponding values. To store and retrieve these values, an hashing function is used. This causes that the average complexity of search, insert and delete data in a hash table is of a \(O ( 1 )\) time complexity. This approach significantly reduced the time complexity to \(O ( n )\), a vast improvement over my initial brute-force method. But, unlike the brute force method that has a space complexity of \(O ( 1 )\), the space complexity is now \(O ( n )\), since in the worst case scenario we will need to store each element of the array in the hash map.</p> <h2 id="c-1">C++</h2> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span> <span class="p">{</span>
<span class="nl">public:</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">twoSum</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">nums</span><span class="p">,</span> <span class="kt">int</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">//initialize an unordered_map to store the array values and their indices</span>
        <span class="n">unordered_map</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;</span> <span class="n">numMap</span><span class="p">;</span>

        <span class="c1">//iterate through the array</span>
        <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span>  <span class="o">=</span> <span class="mi">0</span> <span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
            <span class="c1">//calculate the complement by subtracting the current value to the target</span>
            <span class="kt">int</span> <span class="n">complement</span> <span class="o">=</span> <span class="n">target</span> <span class="o">-</span> <span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
            <span class="c1">//check if the element exists in the map</span>
            <span class="k">if</span><span class="p">(</span><span class="n">numMap</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">complement</span><span class="p">)</span> <span class="o">!=</span> <span class="n">numMap</span><span class="p">.</span><span class="n">end</span><span class="p">())</span>
                <span class="c1">//if found, return the indices of the current value and its complement</span>
                <span class="k">return</span><span class="p">{</span><span class="n">numMap</span><span class="p">[</span><span class="n">complement</span><span class="p">],</span> <span class="n">i</span><span class="p">};</span>
            <span class="c1">//store the current value and it's index in the map</span>
            <span class="n">numMap</span><span class="p">[</span><span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">]]</span><span class="o">=</span><span class="n">i</span><span class="p">;</span>
        <span class="p">}</span>   

        <span class="k">return</span> <span class="p">{};</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div> <h2 id="java-1">Java</h2> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Solution</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kt">int</span><span class="o">[]</span> <span class="nf">twoSum</span><span class="o">(</span><span class="kt">int</span><span class="o">[]</span> <span class="n">nums</span><span class="o">,</span> <span class="kt">int</span> <span class="n">target</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">//create an HashMap to store the array values and their indices</span>
        <span class="nc">Map</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">&gt;</span> <span class="n">map</span> <span class="o">=</span>  <span class="k">new</span> <span class="nc">HashMap</span><span class="o">&lt;&gt;();</span>
        
        <span class="c1">//iterate through the array</span>
        <span class="k">for</span><span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span> <span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">;</span> <span class="n">i</span> <span class="o">++){</span>
            <span class="c1">//calculate the complement by subtracting the current value to the target</span>
            <span class="kt">int</span> <span class="n">complement</span> <span class="o">=</span> <span class="n">target</span> <span class="o">-</span> <span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">];</span>
            <span class="c1">//check if the element exists in the map</span>
            <span class="k">if</span><span class="o">(</span><span class="n">map</span><span class="o">.</span><span class="na">containsKey</span><span class="o">(</span><span class="n">complement</span><span class="o">))</span>
                <span class="c1">//if found, return the indices of the current value and its complement</span>
                <span class="k">return</span> <span class="k">new</span> <span class="kt">int</span><span class="o">[]{</span><span class="n">map</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">complement</span><span class="o">),</span><span class="n">i</span><span class="o">};</span>
            <span class="c1">//store the current value and it's index in the map</span>
            <span class="n">map</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">],</span> <span class="n">i</span><span class="o">);</span>
        <span class="o">}</span>
        <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div> <h2 id="python-1">python</h2> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">twoSum</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">nums</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">target</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
        <span class="c1"># initialize the set to store the array values and their indices
</span>        <span class="n">nums_set</span> <span class="o">=</span> <span class="nf">set</span><span class="p">()</span>
        
        <span class="c1"># iterate through the array
</span>        <span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="n">nums</span><span class="p">:</span>
            <span class="c1"># calculate the complement by subtracting the current value to the target
</span>            <span class="n">complement</span> <span class="o">=</span> <span class="n">target</span> <span class="o">-</span> <span class="n">num</span>
            <span class="c1">#check if the complement exists in the set
</span>            <span class="k">if</span> <span class="n">complement</span> <span class="ow">in</span> <span class="n">nums_set</span><span class="p">:</span>
                <span class="c1"># if found, return the indices of the current value and its complement
</span>                <span class="k">return</span> <span class="p">[</span><span class="n">nums</span><span class="p">.</span><span class="nf">index</span><span class="p">(</span><span class="n">complement</span><span class="p">),</span> <span class="n">nums</span><span class="p">.</span><span class="nf">index</span><span class="p">(</span><span class="n">num</span><span class="p">,</span> <span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)]</span>
            <span class="c1">#store the current number in the set
</span>            <span class="n">nums_set</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="n">num</span><span class="p">)</span>
        
        <span class="k">return</span> <span class="p">[]</span>
</code></pre></div></div> <p>This approach parachuted me to be faster than 80% of other submissions in C++ and to the 98% faster in Java and python.</p> <h2 id="alternative-approach">Alternative approach</h2> <p>While the hash table approach significantly improves the time efficiency, it does introduce a higher space complexity of \(O ( n )\). This got me thinking if whether there’s a middle ground that could offer a better balance between time and space efficiency.</p> <p>A potential approach is to consider the two pointer technique, applicable if the array is sorted or if sorting doesn’t affect the solution’s correctness. This method involves placing one pointer at the beginning of the array and another at the end and then moving them towards each other until the target sum is found. The time complexity is then \(O ( n \log n)\) from the sorting and the space complexity is \(O ( n )\).</p> <h2 id="c-2">C++</h2> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span> <span class="p">{</span>
<span class="nl">public:</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">twoSum</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">nums</span><span class="p">,</span> <span class="kt">int</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
        
        <span class="c1">//create vector to store pairs of numbers and their original indices</span>
        <span class="n">vector</span><span class="o">&lt;</span><span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">vector</span><span class="p">;</span>
        
        <span class="c1">//populate the vector with the pairs</span>
        <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
            <span class="n">vector</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">make_pair</span><span class="p">(</span><span class="n">nums</span><span class="p">[</span><span class="n">i</span><span class="p">],</span><span class="n">i</span><span class="p">));</span>

        <span class="c1">//sort the vector based on the numeric value</span>
        <span class="n">sort</span><span class="p">(</span><span class="n">vector</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">vector</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span>
            <span class="p">[](</span><span class="k">const</span> <span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">b</span><span class="p">){</span>
                <span class="k">return</span> <span class="n">a</span><span class="p">.</span><span class="n">first</span> <span class="o">&lt;</span> <span class="n">b</span><span class="p">.</span><span class="n">first</span><span class="p">;</span>
            <span class="p">});</span>
        
        <span class="c1">//initialize the pointers</span>
        <span class="kt">int</span> <span class="n">left</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">,</span> <span class="n">right</span> <span class="o">=</span> <span class="n">nums</span><span class="p">.</span><span class="n">size</span><span class="p">()</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span>
        
        
        <span class="k">while</span><span class="p">(</span><span class="n">left</span> <span class="o">&lt;</span> <span class="n">right</span><span class="p">){</span>
            <span class="c1">//calculate the sum of the two pointed values</span>
            <span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="n">vector</span><span class="p">[</span><span class="n">left</span><span class="p">].</span><span class="n">first</span><span class="o">+</span><span class="n">vector</span><span class="p">[</span><span class="n">right</span><span class="p">].</span><span class="n">first</span><span class="p">;</span>
            
            <span class="c1">//if the sum matches the target, we return the original indices of the two</span>
            <span class="k">if</span><span class="p">(</span><span class="n">sum</span> <span class="o">==</span> <span class="n">target</span><span class="p">)</span>
                <span class="k">return</span> <span class="p">{</span> <span class="n">vector</span><span class="p">[</span><span class="n">left</span><span class="p">].</span><span class="n">second</span><span class="p">,</span> <span class="n">vector</span><span class="p">[</span><span class="n">right</span><span class="p">].</span><span class="n">second</span><span class="p">};</span>
            
            <span class="c1">//if the sum is less than the target, move the left pointer to the right</span>
            <span class="c1">//that is, we increase the left value</span>
            <span class="k">if</span><span class="p">(</span><span class="n">sum</span> <span class="o">&lt;</span> <span class="n">target</span><span class="p">){</span>
                <span class="n">left</span><span class="o">++</span><span class="p">;</span>
                <span class="k">continue</span><span class="p">;</span>
            <span class="p">}</span>
            
            <span class="c1">//if the sum is greater than the target, we move the right pointer to the left</span>
            <span class="n">right</span><span class="o">--</span><span class="p">;</span>
            
        <span class="p">}</span>
 
        <span class="k">return</span> <span class="p">{};</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div> <h2 id="java-2">Java</h2> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Solution</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kt">int</span><span class="o">[]</span> <span class="nf">twoSum</span><span class="o">(</span><span class="kt">int</span><span class="o">[]</span> <span class="n">nums</span><span class="o">,</span> <span class="kt">int</span> <span class="n">target</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">//create an array with the numbers and their indices</span>
        <span class="kt">int</span><span class="o">[][]</span> <span class="n">numsWithIndices</span> <span class="o">=</span> <span class="nc">IntStream</span><span class="o">.</span><span class="na">range</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span><span class="o">)</span>
                                           <span class="o">.</span><span class="na">mapToObj</span><span class="o">(</span><span class="n">i</span> <span class="o">-&gt;</span> <span class="k">new</span> <span class="kt">int</span><span class="o">[]{</span><span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">],</span> <span class="n">i</span><span class="o">})</span>
                                           <span class="o">.</span><span class="na">toArray</span><span class="o">(</span><span class="kt">int</span><span class="o">[][]::</span><span class="k">new</span><span class="o">);</span>
        
        <span class="c1">//sort the array based on the numeric vlaues</span>
        <span class="nc">Arrays</span><span class="o">.</span><span class="na">sort</span><span class="o">(</span><span class="n">numsWithIndices</span><span class="o">,</span> <span class="nc">Comparator</span><span class="o">.</span><span class="na">comparingInt</span><span class="o">(</span><span class="n">a</span> <span class="o">-&gt;</span> <span class="n">a</span><span class="o">[</span><span class="mi">0</span><span class="o">]));</span>

        <span class="c1">//initialize the two pointers</span>
        <span class="kt">int</span> <span class="n">left</span> <span class="o">=</span> <span class="mi">0</span><span class="o">,</span> <span class="n">right</span> <span class="o">=</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
        

        <span class="c1">//iterate through the array using the two pointers</span>
        <span class="k">while</span><span class="o">(</span><span class="n">left</span> <span class="o">&lt;</span> <span class="n">right</span><span class="o">){</span>
            <span class="c1">//calculate the sum of the two pointed values</span>
            <span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="n">numsWithIndices</span><span class="o">[</span><span class="n">left</span><span class="o">][</span><span class="mi">0</span><span class="o">]+</span><span class="n">numsWithIndices</span><span class="o">[</span><span class="n">right</span><span class="o">][</span><span class="mi">0</span><span class="o">];</span>
            
            <span class="c1">//if the sum matches the target, we return the original indices of the two</span>
            <span class="k">if</span><span class="o">(</span><span class="n">sum</span><span class="o">==</span><span class="n">target</span><span class="o">)</span>
                <span class="k">return</span> <span class="k">new</span> <span class="kt">int</span><span class="o">[]{</span><span class="n">numsWithIndices</span><span class="o">[</span><span class="n">left</span><span class="o">][</span><span class="mi">1</span><span class="o">],</span> <span class="n">numsWithIndices</span><span class="o">[</span><span class="n">right</span><span class="o">][</span><span class="mi">1</span><span class="o">]};</span>
            
            <span class="c1">//if the sum is less than the target, move the left pointer to the right</span>
            <span class="c1">//that is, we increase the left value</span>
            <span class="k">if</span><span class="o">(</span><span class="n">sum</span> <span class="o">&lt;</span> <span class="n">target</span><span class="o">){</span>
                <span class="n">left</span><span class="o">++;</span>
                <span class="k">continue</span><span class="o">;</span>
            <span class="o">}</span>
            
            <span class="c1">//if the sum is greater than the target, we move the right pointer to the left</span>
            <span class="n">right</span><span class="o">--;</span>
        <span class="o">}</span>
        
        <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div> <h2 id="python-2">python</h2> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Solution</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">twoSum</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">nums</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">target</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
        <span class="c1"># create an array of tuples of the numbers and the original indices, sorted by the number
</span>        <span class="n">numsWithIndices</span> <span class="o">=</span> <span class="nf">sorted</span><span class="p">((</span><span class="n">num</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">num</span> <span class="ow">in</span> <span class="nf">enumerate</span><span class="p">(</span><span class="n">nums</span><span class="p">))</span>
        
        <span class="c1"># initialize the pointers
</span>        <span class="n">left</span><span class="p">,</span> <span class="n">right</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">nums</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span>
        
        <span class="c1"># iterate through the array using the two pointers
</span>        <span class="k">while</span> <span class="n">left</span> <span class="o">&lt;</span> <span class="n">right</span><span class="p">:</span>
            <span class="c1"># calculate the sum of the two pointed values
</span>            <span class="nb">sum</span> <span class="o">=</span> <span class="n">numsWithIndices</span><span class="p">[</span><span class="n">left</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">numsWithIndices</span><span class="p">[</span><span class="n">right</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>

            <span class="c1"># if the sum matches the target, we return the original indices of the two
</span>            <span class="k">if</span> <span class="nb">sum</span> <span class="o">==</span> <span class="n">target</span><span class="p">:</span>
                <span class="k">return</span> <span class="p">[</span><span class="n">numsWithIndices</span><span class="p">[</span><span class="n">left</span><span class="p">][</span><span class="mi">1</span><span class="p">],</span> <span class="n">numsWithIndices</span><span class="p">[</span><span class="n">right</span><span class="p">][</span><span class="mi">1</span><span class="p">]]</span>
            
            <span class="c1">#if the sum is less than the target, move the left pointer to the right
</span>            <span class="k">if</span> <span class="nb">sum</span> <span class="o">&lt;</span> <span class="n">target</span><span class="p">:</span>
                <span class="n">left</span> <span class="o">+=</span> <span class="mi">1</span>
                <span class="k">continue</span>
            
            <span class="c1">#if the sum is greater than the target, we move the right pointer to the left
</span>            <span class="n">right</span> <span class="o">-=</span> <span class="mi">1</span>
        
        <span class="k">return</span> <span class="p">[]</span>
</code></pre></div></div> <h2 id="reflection">Reflection</h2> <p>The journey through solving the “Two Sum” problem on LeetCode has been enlightening. It showcased the importance of understanding both time and space complexity and how different approaches can significantly impact performance. From a brute-force method to a time-efficient hash table solution, and exploring a space-efficient two-pointer technique, each method provided valuable insights.</p> <p>The brute-force method, while straightforward, highlighted the importance of considering time complexity from the outset. The hash map solution, on the other hand, was a practical lesson in how space-time trade-offs work in algorithm design. Finally, the sorting and two-pointer technique provided a clear example of how sometimes a preliminary step (like sorting) can lead to more efficient solutions, even if it seems counterintuitive at first.</p> <h2 id="comparison-of-approaches">Comparison of Approaches</h2> <p>A comparison of these methods can offer valuable insights. Understanding the nuances of each approach allows developers to make more informed decisions when faced with similar problems.</p> <table> <thead> <tr> <th> </th> <th style="text-align: center">Time Complexity</th> <th style="text-align: center">Space Complexity</th> <th style="text-align: center">Readability</th> </tr> </thead> <tbody> <tr> <td>Brute-Force</td> <td style="text-align: center">\(O ( n^2 )\)</td> <td style="text-align: center">\(O ( 1 )\)</td> <td style="text-align: center">High</td> </tr> <tr> <td>Hash Map</td> <td style="text-align: center">\(O ( n )\)</td> <td style="text-align: center">\(O ( n )\)</td> <td style="text-align: center">Moderate</td> </tr> <tr> <td>Two-Pointer</td> <td style="text-align: center">\(O ( n \log n )\)</td> <td style="text-align: center">\(O ( n )\)</td> <td style="text-align: center">Moderate</td> </tr> </tbody> </table> <p>Choosing the right approach depends on several factors, including the size of the input data, the computational resources available, and the specific requirements of the application. By carefully considering these aspects, developers can select the most appropriate solution, optimizing for performance, readability, and resource usage as needed.</p> <p>So, Brute-force also has its advantages, it is the more readable solution and simple implementation. If we have small datasets, if the simplicity and readability are more important than efficiency concerns, or if our system has limited memory this solution may be ideal. If we have large data-sets and are in a performance-sensitive application, the reduction of the execution time from the constant-time lookups make this solution ideal. On the other hand the Two-Pointer technique is ideal for medium to large datasets where a balance between time and space efficiency is desired. This method is particularly useful when the input array can be sorted without affecting the solution’s correctness, offering a good compromise between the brute-force and hash map approaches.</p> <p>We can also analyze the performance in terms of execution time and memory usage in the leetcode platform for each programming language</p> <table> <thead> <tr> <th>Approach</th> <th>Language</th> <th>Best Time (ms)</th> <th>Best Memory (MB)</th> </tr> </thead> <tbody> <tr> <td>Brute-Force</td> <td>Java</td> <td>44</td> <td>44.6</td> </tr> <tr> <td> </td> <td>C++</td> <td>9</td> <td>14.2</td> </tr> <tr> <td> </td> <td>Python3</td> <td>1681</td> <td>17.3</td> </tr> <tr> <td>Hash Map</td> <td>Java</td> <td>2</td> <td>45.3</td> </tr> <tr> <td> </td> <td>C++</td> <td>8</td> <td>14.1</td> </tr> <tr> <td> </td> <td>Python3</td> <td>49</td> <td>17.8</td> </tr> <tr> <td>Two-Pointers</td> <td>Java</td> <td>9</td> <td>44.1</td> </tr> <tr> <td> </td> <td>C++</td> <td>3</td> <td>13.5</td> </tr> <tr> <td> </td> <td>Python3</td> <td>60</td> <td>18</td> </tr> </tbody> </table> <p>The Hash Map approach shows the best performance in Java, significantly reducing the execution time to 2 ms, which is a substantial improvement over the Brute-Force and Two-Pointers methods. This demonstrates the efficiency of hash maps in optimizing lookup operations, making it the preferred choice for large datasets where time complexity is a critical factor.</p> <p>C++ implementations consistently show low execution times across all approaches, with the Hash Map and Two-Pointers methods being particularly efficient. This underscores C++’s performance advantages, especially in memory usage, where it outperforms the other languages.</p> <p>Python, while not matching the execution speeds of Java and C++, benefits significantly from the Hash Map approach, reducing its execution time to 49 ms from the much slower brute-force implementation. This illustrates Python’s effective use of dynamic data structures like dictionaries for optimizing time complexity, albeit with a higher space complexity.</p> <p>The performance results for each approach in different programming languages also highlights the impact of language-specific optimizations and data structure implementations on algorithm efficiency. For instance, Java’s significant time reduction using hash maps can be attributed to its highly optimized HashMap class, while C++’s lower memory usage reflects the language’s close-to-hardware design, allowing for more controlled memory management. Python’s slower execution times, compared to C++ and Java, underscore the trade-offs of using a dynamically typed, interpreted language, which prioritizes development speed and ease of use over raw performance. These variations underscore the importance of choosing the right tool for the job, considering both the problem at hand and the execution environment.</p> <h2 id="conclusion">Conclusion</h2> <p>The “Two Sum” problem, in its simplicity and complexity, serves as a microcosm of the broader journey in computer science—a journey of continuous learning, problem-solving, and growth. The exploration of different solutions not only deepens our understanding of algorithmic efficiency but also equips us with a versatile toolkit for approaching future coding challenges. Recognizing when to use a brute-force method for its simplicity, a hash map for its time efficiency, or a two-pointer technique for a balanced approach can drastically improve our problem-solving strategies. These insights encourage a more nuanced consideration of trade-offs in algorithm design, preparing us to tackle a wide range of problems with informed decision-making about when and how to optimize for time, space, and code readability.</p> <p>This exploration has not only enhanced my problem-solving toolkit but also sharpened my decision-making skills, teaching me to weigh the trade-offs between different approaches based on the problem’s requirements. As I move forward, these insights will be invaluable, guiding me through future challenges with a deeper understanding of when and how to apply each method effectively.</p>]]></content><author><name></name></author><category term="leetcode"/><category term="array"/><category term="hashmap"/><category term="timecomplexity"/><category term="spacecomplexity"/><category term="leetcode"/><summary type="html"><![CDATA[to find two numbers in an array that sum up to a given target value.]]></summary></entry><entry><title type="html">challenge - binary multiple of 3</title><link href="jjginga.com/blog/2024/binary_multiple_of_3/" rel="alternate" type="text/html" title="challenge - binary multiple of 3"/><published>2024-02-28T00:00:00+00:00</published><updated>2024-02-28T00:00:00+00:00</updated><id>jjginga.com/blog/2024/binary_multiple_of_3</id><content type="html" xml:base="jjginga.com/blog/2024/binary_multiple_of_3/"><![CDATA[<h2 id="challenge">Challenge</h2> <p>After I run my marathon I took some days to rest by the sea. Little did I know that instead of a conventional boat I would embark on a computational odyssey where on a sea of zeros and ones. It happened when I crossed paths with <a href="https://www.codewars.com/kata/54de279df565808f8b00126a">this CodeWars kata</a>. It seemed simple, since I only had to create a regex expression. And it had a hight reward - 4 kata. So, I decided to take a try. After all, I just had to find a pattern amidst the cascade of 0s and 1s of the binary numbers multiple of 3.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>In this kata, your task is to create a regular expression capable of evaluating binary 
strings (strings with only 1s and 0s) and determining whether the given string 
represents a number divisible by 3.

Take into account that:

An empty string might be evaluated to true (it's not going to be tested, so you don't 
need to worry about it - unless you want)
The input should consist only of binary digits - no spaces, other digits, alphanumeric
 characters, etc.
There might be leading 0s.
</code></pre></div></div> <h2 id="journey">Journey</h2> <p>I started by writting a bunch of binary representations of multiples of 3, in the hope of finding a pattern visually.</p> <table> <thead> <tr> <th>Decimal</th> <th style="text-align: center">Binary</th> </tr> </thead> <tbody> <tr> <td>3</td> <td style="text-align: center">11</td> </tr> <tr> <td>6</td> <td style="text-align: center">110</td> </tr> <tr> <td>9</td> <td style="text-align: center">1001</td> </tr> <tr> <td>12</td> <td style="text-align: center">1100</td> </tr> <tr> <td>15</td> <td style="text-align: center">1111</td> </tr> <tr> <td>18</td> <td style="text-align: center">10010</td> </tr> <tr> <td>21</td> <td style="text-align: center">10101</td> </tr> <tr> <td>24</td> <td style="text-align: center">11000</td> </tr> <tr> <td>27</td> <td style="text-align: center">11011</td> </tr> <tr> <td>30</td> <td style="text-align: center">11110</td> </tr> </tbody> </table> <p>There is one, but I wasn’t able to recognize it, but a quick search on the internet showed me that if I sum all the odd-positioned bits and subtract to it the sum of all of the even-position bits and get a result that is divisible by 3, then the original binary number is also divisible by 3.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>27 in binary is 11011
odd bits: 1+0+1 = 2
even bitts: 1+1 = 2
different: 2-2 = 0, hence 27 is divisible by 3.
</code></pre></div></div> <p>Converting this logic to a regular expression is not straightforward, they work well for pattern matching and not for mathematical operations. So, it hit me. I could use an Automata. The Automata would have states representing the remainder when divided by 3 (the possible remainders are 0, 1 and 2), and transactions based on the next binary digit. And we could then determine the divisibility by 3 by observing the state after processing all the bits.</p> <p>With the available info it is easy to design the following DFA</p> <div class="col-sm mt-3 mt-md-0"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/DFA-480.webp 480w,/assets/img/DFA-800.webp 800w,/assets/img/DFA-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/DFA.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> <p>We could directly write a regular expression from this DFA, bur we can do better than that. We start by defining it mathematically</p> <p>Given the DFA \(( M = (Q, \Sigma, \delta, q_0, F) )\), where:</p> \[( Q = { q_0, q_1, q_2 } ) ( \Sigma = { 0, 1 } ) ( \delta )\] <p>is defined by the transitions:</p> \[\begin{aligned} \delta(q_0, 0) &amp;= q_0 \\ \delta(q_0, 1) &amp;= q_1 \\ \delta(q_1, 0) &amp;= q_2 \\ \delta(q_1, 1) &amp;= q_0 \\ \delta(q_2, 0) &amp;= q_1 \\ \delta(q_2, 1) &amp;= q_2 \end{aligned}\] <p>\(( q_0 )\) is the start state</p> <p>\(( F = { q_0 } )\) is the set of accept states</p> <p>Then we can start by writting the equations for all the states:</p> \[q_0 = \epsilon + q_0 0 + q_1 1 q_1 = q_0 1 + q_2 0 q_2 = q_1 0 + q_2 1\] <p>To simplify \(q_2\) and \(q_1\) we can use <a href="https://www.geeksforgeeks.org/ardens-theorem-in-theory-of-computation/">Arden’s theorem</a>. We first note that the theorem states that if a regular expression \(R\) satisfies the equation \(R = Q + RP\), where \(Q\) and \(P\) are regular expressions and \(P\) does not contain the empty string \(\epsilon\) then \(R = QP*\) is a solution for the equation.</p> <p>Applying it to \(q_2\) we get \(q_2 = ( q_1 0 ) ( 1 * )\) - this represents the language accepted by \(q_1\) followed by a 0 and then any number of 1s.</p> <p>Now substituting this on the equation for \(q_1\) we get \(q_1 = q_0 1 + ( q_1 0 ) (\ 1 * ) 0\).</p> <p>We can now apply Arden’s Theorem again: \(q_1 = ( q_0 1 ) ( 0 ( 1 * ) 0 ) *\) - and you get the language accepted by \(q_0\) followed by a 1 and then any number of repetitions starting with 0, followed by zero or more 1s and ending with 0.</p> <p>Now we can write \(q_0 = \epsilon + q_0 0 + q_0 1 ( 0 ( 1 * ) 0 ) * 1 = \epsilon + q_0 ( 0 + 1 ( 0 ( 1 * ) 0 ) * 1 )\).</p> <p>Applying Arden’s Theorem again we get:</p> \[q_0 = ( \epsilon ) ( 0 + 1 ( 0 ( 1 * ) 0 ) * 1 ) *\] <p>That can be further simplified to</p> \[q_0 = ( \epsilon ) ( 0 + 1 ( 0 ( 1 * ) 0 ) * 1 ) *\] <p>This is the final expression for \(q_0\) that describe all the strings accepted by the automata that start and end in this state.</p> <h2 id="solution-and-reflection">Solution and reflection</h2> <figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">multiple_of_3_regex</span> <span class="o">=</span> <span class="s">"^(0|1(01*0)*1)*$"</span><span class="p">;</span></code></pre></figure> <p>It’s fascinating how theoretical computer science principles blend into practical programming challenges. It’s a vivid reminder that beneath every line of code lies a rich tapestry of logic and mathematics, waiting to be explored and appreciated. Every challenge is not just a test of skill, but an invitation to an intellectual adventure, revealing the interconnectedness of concepts we sometimes take for granted.</p> <h2 id="further-information">Further information</h2> <p><a href="https://www.geeksforgeeks.org/ardens-theorem-in-theory-of-computation/">Arden’s theorem</a> <a href="https://www.youtube.com/watch?v=SmT1DXLl3f4">Khan Academy - DFA to Regular Expression</a></p>]]></content><author><name></name></author><category term="regex"/><category term="codewars"/><category term="regex"/><category term="dfa"/><category term="binary"/><category term="codewars"/><summary type="html"><![CDATA[the challenge was to create a regular expression capable of recognizing multiples of 3.]]></summary></entry><entry><title type="html">Not so Agile Marathon</title><link href="jjginga.com/blog/2024/not-so-agile-marathon/" rel="alternate" type="text/html" title="Not so Agile Marathon"/><published>2024-02-24T20:00:00+00:00</published><updated>2024-02-24T20:00:00+00:00</updated><id>jjginga.com/blog/2024/not-so-agile-marathon</id><content type="html" xml:base="jjginga.com/blog/2024/not-so-agile-marathon/"><![CDATA[<div class="row mt-3"> <div class="col-sm mt-3 mt-md-0"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/medal_seville-480.webp 480w,/assets/img/medal_seville-800.webp 800w,/assets/img/medal_seville-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/medal_seville.jpg" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> </div> </div> <p>In preparing for a marathon, I embarked on a long-term project that spanned several months. It’s accurate to say I engaged in a series of sprints to prepare for my marathon, both figuratively, analogous to multiple sprints in an Agile framework, and literally. My training included ‘iterations’ of high effort followed by rest, mirroring the sprint-and-pause rhythm of interval training. But in a whimsical twist of language, these bursts of speed are known as “series” in Portuguese, giving a whole new meaning to running a series of sprints before the actual Sprint.</p> <p>The objective was clear: complete the marathon within a target time, serving as my project’s final goal. The training was divided into four phases, each with its own objectives, akin to sprint goals in Agile: enhancing overall fitness, boosting speed, increasing endurance, and ultimately tapering for the marathon. These phases were further broken down into smaller increments, approximately a week long—my sprint durations—culminating in a long run each Sunday, my deliverable.</p> <p>Given that running isn’t my primary activity—I balance both work and study—the normal adjustments from learning through experience were complemented by others, tailored to fit my circumstances. Typically, my sprint planning occurred on Mondays, where I would assess the previous week’s training and establish my commitments based on my velocity and past performances. Despite my initial fitness level, the goals were met.</p> <p>A marathon is so extensive that it could be seen both as a sprint – although my pace is far from swift – and as a goal. This happens because in a 42km race, time feels dilated, when you are running 4h41 minutes feel like more than a week. So as race day neared, I conducted my final planning, electing to gi with the 4:30-hour pace group. This was a realistic commitment, in line with in the achievements of earlier project phases.</p> <p>However, once the race day arrived—our sprint began—I faced a challenge. The pace set by the 4:30 group misaligned with my natural running rhythm; it felt too slow, especially in the flat terrain of Seville, making it increasingly hard to maintain, and I started to accelerate. Despite knowing from past marathons that this strategy would lead me to failure, overconfidence and a desire to outperform my expectations—a common pitfall in both running and Agile projects—led me to put aside what I learned from previous experience. Like a team realizing early in a sprint that their approach might not deliver the sprint goal due to external factors or misestimations, yet failing to adjust accordingly.</p> <p>I decided to adapt my strategy on the fly, aiming for a 4-hour finish, exceeding my initial expectations. By the 20km mark, it was evident things weren’t going as planned, but I continued, one kilometer at a time, maintaining my pace against the odds.</p> <p>Unfortunately, this led to a significant challenge at the 30km mark, where I “hit the wall,” or as we say in Portuguese, “o homem da marreta apanhou-me”—no direct translation can encapsulate this expression, the idea is that while you are running a marathon you are being chased by someone with a giant hammer, ready to strike if they catch you. He caught with me and hit me with the hammers, like a team realizing too late that they’ve overcommitted or misjudged their capacity, resulting in burnout or unfinished work.</p> <p>I ended up finishing in 4h41 minutes. I did 30km in 3h and I basically crawled the rest of the way. If I had gone with the 4h30 group the experience wouldn’t be as painful as it was.</p> <p>Reflecting on this experience, the importance of the Agile principle of inspecting and adapting becomes clear. Just as Agile uses retrospectives to reflect on what worked, what didn’t, and how to improve, I must take this opportunity to learn from both the race and the training leading up to it. Recognizing the significance of pacing, the limits of my current capabilities, and the need for strategy adjustments based on real-time feedback are all invaluable lessons.</p> <p>For my next race—our next project or sprint cycle—I’ll approach with a focus on realistic goal setting, continuous performance adjustment, and maintaining a sustainable pace. The aim is not only to reach the finish line but to do so smiling instead of crawling.</p>]]></content><author><name></name></author><category term="agile"/><category term="agile"/><category term="marathon"/><summary type="html"><![CDATA[reflecting on this experience, the importance of the agile principle of inspecting and adapting becomes clear.]]></summary></entry></feed>