<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: saksham780</title>
    <description>The latest articles on DEV Community by saksham780 (@saksham780).</description>
    <link>https://dev.to/saksham780</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4166363%2Fa8a50f27-14c7-46dc-b7c3-20086da7b508.jpg</url>
      <title>DEV Community: saksham780</title>
      <link>https://dev.to/saksham780</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/saksham780"/>
    <language>en</language>
    <item>
      <title>Shapley Addictive Explanation</title>
      <dc:creator>saksham780</dc:creator>
      <pubDate>Thu, 08 Oct 2026 11:00:27 +0000</pubDate>
      <link>https://dev.to/saksham780/shapley-addictive-explanation-1i3e</link>
      <guid>https://dev.to/saksham780/shapley-addictive-explanation-1i3e</guid>
      <description>&lt;p&gt;A practical, beginner-friendly guide to SHapley Additive exPlanations with a simple example and Python implementation&lt;/p&gt;

&lt;p&gt;Imagine applying for a loan and getting rejected by a machine learning model. You ask, “Why was my application rejected?” and someone simply tells you, “The algorithm decided.”&lt;/p&gt;

&lt;p&gt;That answer is not very useful.&lt;/p&gt;

&lt;p&gt;Modern machine learning models such as gradient boosting, random forests, and neural networks can make highly accurate predictions, but understanding why they made those predictions can be difficult.&lt;/p&gt;

&lt;p&gt;This is where SHAP, short for SHapley Additive exPlanations, becomes useful. SHAP explains a prediction by showing how much each feature contributed to moving the prediction up or down.&lt;/p&gt;

&lt;p&gt;In this article, we will understand the basic idea behind SHAP, work through a small example by hand, and then use the Python shap library to explain a real machine learning model.&lt;/p&gt;

&lt;p&gt;What Is SHAP?&lt;/p&gt;

&lt;p&gt;SHAP is based on Shapley values, a concept from cooperative game theory introduced by mathematician Lloyd Shapley in 1953.&lt;/p&gt;

&lt;p&gt;The basic idea is simple: when several people work together to earn a reward, how should the reward be divided fairly among them?&lt;/p&gt;

&lt;p&gt;Imagine three friends working together on a project. Each person may contribute differently, and some contributions may become more valuable when combined with another person's contribution.&lt;/p&gt;

&lt;p&gt;Shapley's approach is to consider the different possible orders in which the participants could join the project. For each order, we calculate how much additional value a participant brings when they join. The average of those contributions becomes that participant's fair share.&lt;/p&gt;

&lt;p&gt;Now replace:&lt;/p&gt;

&lt;p&gt;Friends → Features&lt;br&gt;
Project reward → Model prediction&lt;br&gt;
Contribution → Feature's effect on the prediction&lt;/p&gt;

&lt;p&gt;That is the basic idea behind SHAP.&lt;/p&gt;

&lt;p&gt;SHAP asks:&lt;/p&gt;

&lt;p&gt;How much did each feature contribute to this particular prediction?&lt;/p&gt;

&lt;p&gt;A Simple SHAP Example&lt;/p&gt;

&lt;p&gt;Let's consider a fictional loan prediction model with three features:&lt;/p&gt;

&lt;p&gt;Income&lt;br&gt;
Debt&lt;br&gt;
Credit history&lt;/p&gt;

&lt;p&gt;Suppose our simplified model starts with a score of 0 when it knows nothing about the applicant.&lt;/p&gt;

&lt;p&gt;The model behaves like this:&lt;/p&gt;

&lt;p&gt;High income adds 30 points&lt;br&gt;
Good credit history adds 20 points&lt;br&gt;
Debt subtracts 10 points&lt;br&gt;
Income and credit history together add an additional 10-point bonus&lt;/p&gt;

&lt;p&gt;Therefore, when all three features are known:&lt;/p&gt;

&lt;p&gt;30 + 20 − 10 + 10 = 50&lt;/p&gt;

&lt;p&gt;The final score is 50.&lt;/p&gt;

&lt;p&gt;But how much of that score should be attributed to each feature?&lt;/p&gt;

&lt;p&gt;The interaction between income and credit history makes this less obvious&lt;/p&gt;

&lt;p&gt;Looking at Every Possible Order&lt;/p&gt;

&lt;p&gt;There are six possible orders in which the three features can be added.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Order&lt;/th&gt;
&lt;th&gt;Income&lt;/th&gt;
&lt;th&gt;Debt&lt;/th&gt;
&lt;th&gt;History&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Income → Debt → History&lt;/td&gt;
&lt;td&gt;+30&lt;/td&gt;
&lt;td&gt;−10&lt;/td&gt;
&lt;td&gt;+30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Income → History → Debt&lt;/td&gt;
&lt;td&gt;+30&lt;/td&gt;
&lt;td&gt;−10&lt;/td&gt;
&lt;td&gt;+30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debt → Income → History&lt;/td&gt;
&lt;td&gt;+30&lt;/td&gt;
&lt;td&gt;−10&lt;/td&gt;
&lt;td&gt;+30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debt → History → Income&lt;/td&gt;
&lt;td&gt;+40&lt;/td&gt;
&lt;td&gt;−10&lt;/td&gt;
&lt;td&gt;+20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;History → Income → Debt&lt;/td&gt;
&lt;td&gt;+40&lt;/td&gt;
&lt;td&gt;−10&lt;/td&gt;
&lt;td&gt;+20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;History → Debt → Income&lt;/td&gt;
&lt;td&gt;+40&lt;/td&gt;
&lt;td&gt;−10&lt;/td&gt;
&lt;td&gt;+20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average&lt;/td&gt;
&lt;td&gt;+35&lt;/td&gt;
&lt;td&gt;−10&lt;/td&gt;
&lt;td&gt;+25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The final SHAP contributions are therefore:&lt;/p&gt;

&lt;p&gt;Income: +35&lt;br&gt;
Debt: −10&lt;br&gt;
Credit history: +25&lt;/p&gt;

&lt;p&gt;Adding them together:&lt;/p&gt;

&lt;p&gt;35 − 10 + 25 = 50&lt;/p&gt;

&lt;p&gt;So the contributions exactly reconstruct the model's prediction.&lt;/p&gt;

&lt;p&gt;The extra 10-point interaction bonus is effectively shared between income and credit history.&lt;/p&gt;

&lt;p&gt;This is the basic idea of Shapley fairness.&lt;/p&gt;

&lt;p&gt;The SHAP Formula&lt;/p&gt;

&lt;p&gt;In general, the contribution of feature i can be written as:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
φᵢ = Σ [ |S|! (n − |S| − 1)! / n! ] × [f(S ∪ {i}) − f(S)]&lt;/p&gt;

&lt;p&gt;You do not need to memorize this formula to use SHAP.&lt;/p&gt;

&lt;p&gt;In simple terms, it calculates the feature's average marginal contribution across different subsets of the other features, with appropriate weights so that the possible feature orderings are treated fairly.&lt;/p&gt;

&lt;p&gt;What Does "Additive" Mean in SHAP?&lt;/p&gt;

&lt;p&gt;The A in SHAP stands for Additive.&lt;/p&gt;

&lt;p&gt;One of the most useful properties of SHAP is that the individual feature contributions add up to the model's output.&lt;/p&gt;

&lt;p&gt;In simplified form:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
Prediction =&lt;br&gt;
Base value&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SHAP(feature 1)&lt;/li&gt;
&lt;li&gt;SHAP(feature 2)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;SHAP(feature n)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thebase value represents the model's expected output before considering the specific features of the case being explained.&lt;/p&gt;

&lt;p&gt;Each SHAP value then moves the prediction higher or lower.&lt;/p&gt;

&lt;p&gt;In our loan example:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
Base value = 0&lt;/p&gt;

&lt;p&gt;Income       = +35&lt;br&gt;
Debt         = −10&lt;br&gt;
Credit history = +25&lt;/p&gt;

&lt;p&gt;Final prediction = 50&lt;/p&gt;

&lt;p&gt;Why Are SHAP Values Useful?&lt;/p&gt;

&lt;p&gt;SHAP is popular because its approach is based on several desirable properties.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Local Accuracy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The feature contributions add up to the model's output.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Missingness&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A feature that is missing does not receive a contribution.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Consistency&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a feature becomes more important in the model, its SHAP contribution should not decrease under the SHAP framework.&lt;/p&gt;

&lt;p&gt;These properties make SHAP particularly useful when we need explanations that are mathematically grounded.&lt;/p&gt;

&lt;p&gt;SHAP in Practice&lt;/p&gt;

&lt;p&gt;Calculating exact Shapley values can become computationally expensive because the number of possible feature combinations grows rapidly as the number of features increases.&lt;/p&gt;

&lt;p&gt;For example, with 30 features, there are already more than a billion possible subsets.&lt;/p&gt;

&lt;p&gt;The Python shap library therefore provides specialized explainers.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Explainer&lt;/th&gt;
&lt;th&gt;Common Use&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TreeExplainer&lt;/td&gt;
&lt;td&gt;Random forests, XGBoost, LightGBM, gradient boosting&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinearExplainer&lt;/td&gt;
&lt;td&gt;Linear and logistic regression&lt;/td&gt;
&lt;td&gt;Very fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepExplainer&lt;/td&gt;
&lt;td&gt;Deep neural networks&lt;/td&gt;
&lt;td&gt;Fast/approximate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KernelExplainer&lt;/td&gt;
&lt;td&gt;General black-box models&lt;/td&gt;
&lt;td&gt;Slower&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For tree-based models, TreeExplainer is usually the most convenient choice.&lt;/p&gt;

&lt;p&gt;Hands-On: Explaining a Real Model with SHAP&lt;/p&gt;

&lt;p&gt;Let's train a gradient boosting classifier using scikit-learn's built-in breast cancer dataset.&lt;/p&gt;

&lt;p&gt;This dataset is being used only as a teaching example. It should not be used to make real medical decisions.&lt;/p&gt;

&lt;p&gt;First, install the required libraries:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
pip install shap scikit-learn matplotlib&lt;/p&gt;

&lt;p&gt;Then use the following code:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
import shap&lt;br&gt;
from sklearn.datasets import load_breast_cancer&lt;br&gt;
from sklearn.ensemble import GradientBoostingClassifier&lt;br&gt;
from sklearn.model_selection import train_test_split&lt;/p&gt;

&lt;p&gt;data = load_breast_cancer(as_frame=True)&lt;br&gt;
X, y = data.data, data.target&lt;/p&gt;

&lt;p&gt;X_train, X_test, y_train, y_test = train_test_split(&lt;br&gt;
    X, y, test_size=0.2, random_state=42&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;model = GradientBoostingClassifier(random_state=42)&lt;br&gt;
model.fit(X_train, y_train)&lt;/p&gt;

&lt;p&gt;print("Test accuracy:", round(model.score(X_test, y_test), 3))&lt;/p&gt;

&lt;p&gt;explainer = shap.TreeExplainer(model)&lt;br&gt;
shap_values = explainer(X_test)&lt;/p&gt;

&lt;p&gt;i = 0&lt;br&gt;
print("Base value:", round(float(shap_values.base_values[i]), 3))&lt;/p&gt;

&lt;p&gt;top = sorted(&lt;br&gt;
    zip(X_test.columns, shap_values.values[i]),&lt;br&gt;
    key=lambda x: abs(x[1]),&lt;br&gt;
    reverse=True&lt;br&gt;
)[:5]&lt;/p&gt;

&lt;p&gt;for name, value in top:&lt;br&gt;
    print(f"{name:25s} {value:+.3f}")&lt;/p&gt;

&lt;p&gt;You can also verify SHAP's additive property:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
total = shap_values.base_values[i] + shap_values.values[i].sum()&lt;/p&gt;

&lt;p&gt;print("Base + SHAP values:", round(float(total), 3))&lt;/p&gt;

&lt;p&gt;raw = model.decision_function(X_test.iloc[[i]])[0]&lt;/p&gt;

&lt;p&gt;print("Model raw output:", round(float(raw), 3))&lt;/p&gt;

&lt;p&gt;Example Output&lt;/p&gt;

&lt;p&gt;One example run produces:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
Test accuracy: 0.956&lt;/p&gt;

&lt;p&gt;mean concave points     +1.140&lt;br&gt;
worst concave points    +1.119&lt;br&gt;
worst perimeter         +0.820&lt;br&gt;
worst radius            +0.582&lt;br&gt;
worst area              +0.566&lt;/p&gt;

&lt;p&gt;Base + sum of SHAP : 7.055&lt;br&gt;
Model raw output : 7.055&lt;/p&gt;

&lt;p&gt;The important part is the final two values:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
Base + sum of SHAP : 7.055&lt;br&gt;
Model raw output   : 7.055&lt;/p&gt;

&lt;p&gt;They match exactly, demonstrating SHAP's additive property.&lt;/p&gt;

&lt;p&gt;Understanding SHAP Units&lt;/p&gt;

&lt;p&gt;One important detail is that SHAP values do not always represent probabilities.&lt;/p&gt;

&lt;p&gt;For this classifier, the values are expressed in log-odds.&lt;/p&gt;

&lt;p&gt;The raw model output of approximately 7.055 log-odds corresponds to a probability of roughly 99.9% for the benign class.&lt;/p&gt;

&lt;p&gt;Positive SHAP values push the prediction toward the benign class, while negative values push it toward the malignant class.&lt;/p&gt;

&lt;p&gt;Reading SHAP Visualizations&lt;/p&gt;

&lt;p&gt;SHAP provides several useful plots.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Waterfall Plot&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A waterfall plot explains one prediction.&lt;/p&gt;

&lt;p&gt;It starts with the base value and shows how individual features push the prediction higher or lower until the final model output is reached.&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
shap.plots.waterfall(shap_values[0], max_display=8)&lt;/p&gt;

&lt;p&gt;This is useful when you want to answer:&lt;/p&gt;

&lt;p&gt;Why did the model make this particular prediction?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SHAP Bar Plot&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A bar plot shows which features have the largest average absolute SHAP values across the dataset.&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
shap.plots.bar(shap_values, max_display=8)&lt;/p&gt;

&lt;p&gt;Longer bars indicate features with larger average effects on predictions.&lt;/p&gt;

&lt;p&gt;This is useful when you want to understand:&lt;/p&gt;

&lt;p&gt;Which features does my model rely on most overall?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SHAP Beeswarm Plot&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A beeswarm plot provides more detail by showing both the importance and direction of feature effects.&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
shap.plots.beeswarm(shap_values, max_display=8)&lt;/p&gt;

&lt;p&gt;It is particularly useful for seeing how feature values influence predictions across many observations.&lt;/p&gt;

&lt;p&gt;Things to Be Careful About&lt;/p&gt;

&lt;p&gt;SHAP is powerful, but it should not be interpreted as a perfect explanation of reality.&lt;/p&gt;

&lt;p&gt;Correlated Features&lt;/p&gt;

&lt;p&gt;Correlated features can share credit.&lt;/p&gt;

&lt;p&gt;For example, worst radius, worst perimeter, and worst area measure related characteristics. SHAP may distribute their contribution among them.&lt;/p&gt;

&lt;p&gt;SHAP Explains the Model, Not Reality&lt;/p&gt;

&lt;p&gt;If the model learned an unusual pattern from the training data, SHAP will explain that pattern faithfully.&lt;/p&gt;

&lt;p&gt;It does not prove that a feature causes an outcome.&lt;/p&gt;

&lt;p&gt;Computational Cost&lt;/p&gt;

&lt;p&gt;Model-agnostic explainers such as KernelExplainer can become slow on large datasets.&lt;/p&gt;

&lt;p&gt;Background Data Matters&lt;/p&gt;

&lt;p&gt;The base value depends on the reference/background data used by the explainer. Therefore, that data should reasonably represent the population you want to study.&lt;/p&gt;

&lt;p&gt;Don't Overinterpret Tiny Values&lt;/p&gt;

&lt;p&gt;A very small SHAP value may not be practically important. Focus on features showing clearer effects.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;SHAP gives machine learning practitioners a practical way to look inside model predictions.&lt;/p&gt;

&lt;p&gt;Instead of simply saying:&lt;/p&gt;

&lt;p&gt;"The model predicted this."&lt;/p&gt;

&lt;p&gt;you can ask:&lt;/p&gt;

&lt;p&gt;"Which features pushed the prediction in this direction, and by how much?"&lt;/p&gt;

&lt;p&gt;The key idea is simple:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
Model prediction =&lt;br&gt;
Base value + contribution of each feature&lt;/p&gt;

&lt;p&gt;For individual predictions, waterfall plots are especially useful. For understanding overall feature importance, bar plots and beeswarm plots can provide a broader view.&lt;/p&gt;

&lt;p&gt;If you are working with a tree-based model, trying SHAP can be a great first step toward making your machine learning model easier to understand.&lt;/p&gt;

&lt;p&gt;Have you used SHAP on a real project? What did it reveal about your model? Share your experience in the comments.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>machinelearning</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A* Search on grid based pathfinding with obstacle avoidance</title>
      <dc:creator>saksham780</dc:creator>
      <pubDate>Tue, 06 Oct 2026 13:00:55 +0000</pubDate>
      <link>https://dev.to/saksham780/a-search-on-grid-based-pathfinding-with-obstacle-avoidance-1knm</link>
      <guid>https://dev.to/saksham780/a-search-on-grid-based-pathfinding-with-obstacle-avoidance-1knm</guid>
      <description>&lt;p&gt;A* Pathfinding in Python: Build a Grid Navigation Engine Around Obstacles&lt;/p&gt;

&lt;p&gt;How a smart heuristic guides a shortest-path search, with working Python code.&lt;/p&gt;

&lt;p&gt;Every time an enemy in a video game walks around a wall to find you, or a warehouse robot moves between shelves to reach a package, something has to work out the route. One of the most widely used algorithms for this kind of problem is A* (pronounced “A-star”).&lt;/p&gt;

&lt;p&gt;The idea is simple: instead of exploring every direction equally, A* asks which available position looks most promising. It combines the cost of the path so far with an estimate of the remaining distance.&lt;/p&gt;

&lt;p&gt;In this post, we will build an A* pathfinder in Python for a grid with walls.&lt;/p&gt;

&lt;p&gt;The problem we are solving&lt;/p&gt;

&lt;p&gt;We have a 2D grid. Each cell is either open (&lt;code&gt;0&lt;/code&gt;) or a wall (&lt;code&gt;1&lt;/code&gt;). We start in one cell and want to reach another, moving up, down, left, or right.&lt;/p&gt;

&lt;p&gt;Breadth-first search (BFS) can also solve this problem when every move has the same cost. A* can reduce unnecessary exploration by using information about where the goal is.&lt;/p&gt;

&lt;p&gt;How A* thinks: f(n) = g(n) + h(n)&lt;/p&gt;

&lt;p&gt;A* gives every candidate cell a score and expands the cell with the lowest score first.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;g(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cost so far&lt;/td&gt;
&lt;td&gt;Exact cost from the start to the current cell.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;h(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Heuristic&lt;/td&gt;
&lt;td&gt;Estimate of the remaining cost to the goal.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;f(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total score&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;g(n) + h(n)&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a four-direction grid, Manhattan distance is a standard heuristic:&lt;br&gt;
text&lt;/p&gt;

&lt;p&gt;h(a, b) = |a.row - b.row| + |a.col - b.col|&lt;/p&gt;

&lt;p&gt;Manhattan distance does not overestimate the true cost in this setting, which preserves the shortest-path guarantee.&lt;/p&gt;

&lt;p&gt;The Python implementation&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
import heapq&lt;/p&gt;

&lt;p&gt;class Node:&lt;br&gt;
    def &lt;strong&gt;init&lt;/strong&gt;(self, row, col):&lt;br&gt;
        self.row = row&lt;br&gt;
        self.col = col&lt;br&gt;
        self.g = float("inf")&lt;br&gt;
        self.h = 0&lt;br&gt;
        self.f = float("inf")&lt;br&gt;
        self.parent = None&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def __lt__(self, other):
    return self.f &amp;lt; other.f
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;def manhattan(a, b):&lt;br&gt;
    return abs(a.row - b.row) + abs(a.col - b.col)&lt;/p&gt;

&lt;p&gt;def astar(grid, start, goal):&lt;br&gt;
    rows, cols = len(grid), len(grid[0])&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for r, c in (start, goal):
    if not (0 &amp;lt;= r &amp;lt; rows and 0 &amp;lt;= c &amp;lt; cols) or grid[r][c] == 1:
        return []

start_node = Node(*start)
goal_node = Node(*goal)

start_node.g = 0
start_node.h = manhattan(start_node, goal_node)
start_node.f = start_node.h

open_heap = [(start_node.f, start_node)]
open_lookup = {start: start_node}
closed = set()

moves = [(-1, 0), (1, 0), (0, -1), (0, 1)]

while open_heap:
    _, current = heapq.heappop(open_heap)
    pos = (current.row, current.col)

    if pos in closed:
        continue

    open_lookup.pop(pos, None)

    if pos == goal:
        path = []
        while current:
            path.append((current.row, current.col))
            current = current.parent
        return path[::-1]

    closed.add(pos)

    for dr, dc in moves:
        nr, nc = current.row + dr, current.col + dc

        if not (0 &amp;lt;= nr &amp;lt; rows and 0 &amp;lt;= nc &amp;lt; cols):
            continue
        if grid[nr][nc] == 1 or (nr, nc) in closed:
            continue

        new_g = current.g + 1
        neighbor = open_lookup.get((nr, nc))

        if neighbor is None:
            neighbor = Node(nr, nc)
            neighbor.h = manhattan(neighbor, goal_node)
            open_lookup[(nr, nc)] = neighbor
        elif new_g &amp;gt;= neighbor.g:
            continue

        neighbor.parent = current
        neighbor.g = new_g
        neighbor.f = new_g + neighbor.h
        heapq.heappush(open_heap, (neighbor.f, neighbor))

return []
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Seeing it work&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
from astar import astar&lt;/p&gt;

&lt;p&gt;grid = [&lt;br&gt;
    [0, 0, 0, 0, 0, 0],&lt;br&gt;
    [0, 1, 1, 1, 1, 0],&lt;br&gt;
    [0, 0, 0, 0, 1, 0],&lt;br&gt;
    [0, 1, 1, 0, 1, 0],&lt;br&gt;
    [0, 0, 0, 0, 0, 0],&lt;br&gt;
]&lt;/p&gt;

&lt;p&gt;path = astar(grid, start=(0, 0), goal=(4, 5))&lt;br&gt;
print("Path found in", len(path) - 1, "steps")&lt;br&gt;
print(path)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Output:

text
Path found in 9 steps
[(0, 0), (1, 0), (2, 0), (3, 0), (4, 0),
 (4, 1), (4, 2), (4, 3), (4, 4), (4, 5)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The route can be visualised as:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
S . . . . .&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;# # # # .&lt;/li&gt;
&lt;li&gt;. . . # .&lt;/li&gt;
&lt;li&gt;# # . # .&lt;/li&gt;
&lt;li&gt;* * * * G&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How fast is it?&lt;/p&gt;

&lt;p&gt;In the worst case, A* may need to examine many or even all cells. With a binary heap, a common rough bound for this implementation is O(N log N) for N discovered cells. In practice, a good heuristic can keep the search focused.&lt;/p&gt;

&lt;p&gt;Memory usage is O(N) because the algorithm stores information about discovered cells.&lt;/p&gt;

&lt;p&gt;Common mistakes to avoid&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mixing up row and column.&lt;/li&gt;
&lt;li&gt;Forgetting the closed-set check.&lt;/li&gt;
&lt;li&gt;Using a heuristic that overestimates the remaining cost when you need a shortest-path guarantee.&lt;/li&gt;
&lt;li&gt;Not validating the start and goal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where to go from here&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add diagonal movement and use octile distance.&lt;/li&gt;
&lt;li&gt;Add weighted terrain.&lt;/li&gt;
&lt;li&gt;Explore Jump Point Search or hierarchical pathfinding.&lt;/li&gt;
&lt;li&gt;Visualise the open and closed sets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Final takeaway&lt;/p&gt;

&lt;p&gt;A* connects a simple mathematical idea with practical software. The formula &lt;code&gt;f(n) = g(n) + h(n)&lt;/code&gt;, a priority queue, and a few sets are enough to build a shortest-path solver for a four-direction grid.&lt;/p&gt;

&lt;p&gt;Once you understand this version, the same structure can be adapted to game maps, road networks, warehouse robots, and other navigation problems.&lt;/p&gt;

&lt;p&gt;Suggested tags: Python, Algorithms, A-Star, Pathfinding, Data Structures, DSA, Programming&lt;/p&gt;

</description>
      <category>python</category>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>programming</category>
    </item>
    <item>
      <title>Stimulate TRIE data structure</title>
      <dc:creator>saksham780</dc:creator>
      <pubDate>Tue, 06 Oct 2026 12:34:14 +0000</pubDate>
      <link>https://dev.to/saksham780/stimulate-trie-data-structure-273m</link>
      <guid>https://dev.to/saksham780/stimulate-trie-data-structure-273m</guid>
      <description>&lt;p&gt;How to Build a Fast Search-as-You-Type Autocomplete Engine Using Tries&lt;/p&gt;

&lt;p&gt;A practical guide to prefix trees in Python, with code you can run and extend.&lt;/p&gt;

&lt;p&gt;You type two letters into a search bar and, before you have even decided on the third, a list of suggestions is already waiting. It feels like magic, but it is really a data structure doing its job. Behind many autocomplete boxes is a system that can answer a simple question quickly: “What starts with these letters?”&lt;br&gt;
If you have ever tried to build this with a database query or a loop over a list, you may have noticed the problem: it works well with a small list and becomes less attractive as the number of words grows. A Trie, also called a prefix tree, is a data structure designed specifically for prefix-based lookups.&lt;br&gt;
In this post, we will see why the obvious approach can become slow, how a Trie works, and how to build a simple autocomplete engine in Python from scratch.&lt;/p&gt;

&lt;p&gt;Why the obvious approach gets slow&lt;/p&gt;

&lt;p&gt;Imagine an online shop with 100,000 product names. A customer types "cat". A simple solution is to check every product and keep the names that start with "cat". In Python, that could be a loop using starts with; in SQL, it could be a prefix query such as WHERE name LIKE 'cat%'.&lt;/p&gt;

&lt;p&gt;With N words, you may perform up to N comparisons for every keystroke. As the catalogue grows, the search box has more work to do.&lt;/p&gt;

&lt;p&gt;A database index can make prefix queries fast, and for many applications that is enough. But when you want a lightweight in-memory autocomplete engine that responds on every keystroke, a Trie is a useful alternative because it stores shared prefixes together.&lt;/p&gt;

&lt;p&gt;What is a Trie?&lt;/p&gt;

&lt;p&gt;A Trie (the name comes from "retrieval", and is usually pronounced "try") is a tree where each step down the tree adds one character. Instead of storing each word as one separate object, words are represented as paths from the root.&lt;/p&gt;

&lt;p&gt;Words with the same beginning share the same path. This means a prefix is represented once, and the node you reach tells you exactly which prefix you have matched.&lt;/p&gt;

&lt;p&gt;For example, here is how cat, to, and top can be stored. A star (*) marks a node where a complete word ends:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
(root)&lt;br&gt;
 /   \&lt;br&gt;
c     t&lt;br&gt;
|     |&lt;br&gt;
a     o*&lt;br&gt;
|     |&lt;br&gt;
t*    p*&lt;/p&gt;

&lt;p&gt;Reading down the left branch gives cat. On the right, to is a complete word, while top extends it. The star is what tells us that a node represents a complete stored word.&lt;/p&gt;

&lt;p&gt;Why is it fast?&lt;/p&gt;

&lt;p&gt;To find words beginning with c, we do not scan the entire dictionary. We start at the root, follow the c branch, and immediately reach the part of the tree containing that prefix.&lt;/p&gt;

&lt;p&gt;Finding the prefix takes O(L) time, where L is the length of the prefix. However, collecting the suggestions underneath that prefix still depends on how many matching words there are.&lt;/p&gt;

&lt;p&gt;Building a Trie in Python&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
class TrieNode:&lt;br&gt;
    def &lt;strong&gt;init&lt;/strong&gt;(self):&lt;br&gt;
        self.children = {}&lt;br&gt;
        self.is_end_of_word = False&lt;/p&gt;

&lt;p&gt;class Trie:&lt;br&gt;
    def &lt;strong&gt;init&lt;/strong&gt;(self):&lt;br&gt;
        self.root = TrieNode()&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def insert(self, word: str) -&amp;gt; None:
    node = self.root
    for char in word:
        if char not in node.children:
            node.children[char] = TrieNode()
        node = node.children[char]
    node.is_end_of_word = True

def search_prefix(self, prefix: str) -&amp;gt; list:
    node = self.root
    for char in prefix:
        if char not in node.children:
            return []
        node = node.children[char]

    results = []
    self._collect(node, prefix, results)
    return results

def _collect(self, node: TrieNode, word: str, results: list) -&amp;gt; None:
    if node.is_end_of_word:
        results.append(word)

    for char, child in node.children.items():
        self._collect(child, word + char, results)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;if &lt;strong&gt;name&lt;/strong&gt; == "&lt;strong&gt;main&lt;/strong&gt;":&lt;br&gt;
    engine = Trie()&lt;br&gt;
    words = ["cat", "car", "cart", "dog", "dodge", "deer", "testing"]&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for word in words:
    engine.insert(word)

user_input = "ca"
suggestions = engine.search_prefix(user_input)

print(f"User typed: '{user_input}'")
print(f"Suggestions: {suggestions}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;p&gt;text&lt;br&gt;
User typed: 'ca'&lt;br&gt;
Suggestions: ['cat', 'car', 'cart']&lt;/p&gt;

&lt;p&gt;How the code works&lt;/p&gt;

&lt;p&gt;Inserting a word&lt;/p&gt;

&lt;p&gt;insert() walks through the word one character at a time. If a character branch does not exist, it creates a new node. When the last character is reached, is_end_of_word is set to True.&lt;/p&gt;

&lt;p&gt;Searching by prefix&lt;/p&gt;

&lt;p&gt;search_prefix() first follows the typed prefix. If a character is missing, no stored word starts with that prefix. It then uses _collect() to traverse the matching subtree and gather complete words.&lt;/p&gt;

&lt;p&gt;Time and space complexity&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Complexity&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Insert a word&lt;/td&gt;
&lt;td&gt;O(L)&lt;/td&gt;
&lt;td&gt;L is the length of the word.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check a prefix&lt;/td&gt;
&lt;td&gt;O(L)&lt;/td&gt;
&lt;td&gt;Depends on the prefix length.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get suggestions&lt;/td&gt;
&lt;td&gt;O(L + M)&lt;/td&gt;
&lt;td&gt;M is the number of visited nodes while collecting matches.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;O(total characters)&lt;/td&gt;
&lt;td&gt;Shared prefixes reduce repeated storage.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Taking it to production&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frequency weights: rank popular suggestions higher.&lt;/li&gt;
&lt;li&gt;Top-k caching: store the best few completions at each node.&lt;/li&gt;
&lt;li&gt;Radix trees: compress chains of single-child nodes.&lt;/li&gt;
&lt;li&gt;Input normalization and fuzzy matching: handle case differences and typing mistakes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a Trie is not the right tool&lt;/p&gt;

&lt;p&gt;If the list is tiny, a normal list with startswith() may be simpler. For full-text search, typo tolerance, and relevance ranking across large documents, a dedicated search engine may be a better fit.&lt;/p&gt;

&lt;p&gt;Final takeaway&lt;/p&gt;

&lt;p&gt;A Trie changes autocomplete from “scan everything” into “follow the letters.” The core idea is small, but the same prefix-tree concept can support search boxes, spell checkers, keyboard suggestions, and other prefix-based features.&lt;/p&gt;

&lt;p&gt;A good next exercise is to add a frequency counter and return only the top five suggestions.&lt;/p&gt;

&lt;p&gt;Suggested tags: Python, Data Structures, Algorithms, Trie, Autocomplete, Programming&lt;/p&gt;

</description>
      <category>datastructures</category>
      <category>programming</category>
      <category>python</category>
      <category>algorithms</category>
    </item>
  </channel>
</rss>
