Why learn programming in GPT era?
What Is Computer Science?
Computer science is computational problem solving i.e., solving problems by the use of computation. Programming languages and computers are just tools we use for computational problem solving.
In the study of computer science, foundational principles of computation remain constant. However, the field is dynamic due to the technology change. While the core principles provide stability, the endless advancements and innovations are driven by our creativity. What can be achieved through computation is bound only by our imagination.
Computer Science is a very vast field, with breadth and depth to it. Computer science encompasses a wide range of areas, including software engineering (which focuses on designing and implementing large software systems), database management, computer networks, computer graphics, computer simulation, data mining, information security, programming language design, systems programming, computer architecture, human-computer interaction, robotics, and artificial intelligence, and others.
What is Computation?
To solve a problem computationally, we need
A representation of problem
An algorithm to solve the problem (using representation)
What is Representation?
The representation of a problem refers to the way in which a problem is defined, modeled, or expressed in a format that can be processed or analyzed by a computer.
To understand more clearly, let's take an example of a shortest path in a graph problem.
Problem Statement: Find the shortest path between two nodes in a graph where each edge has a non-negative weight.
Representation:
It can be represented using a graph representation.
Graph: A collection of nodes (vertices) and edges connecting these nodes.
Nodes: Represented as vertices in the graph.
Edges: Represented as weighted connections between nodes.
Graph is a conceptual thing and to represent it in computer we need to use data structures like adjacency matrix or adjacency list.
Adjacency Matrix: A 2D array where the element at position [i][j] represents the weight of the edge from node i to node j. If there is no edge, the value is typically set to infinity.
Adjacency List: An array where each index represents a node and contains a list of pairs (neighbor, weight) for each adjacent node.
Below is the python code for graph representation using adjacency list.
graph = {
'A': [('B', 1), ('C', 4)],
'B': [('C', 2), ('D', 5)],
'C': [('D', 1)],
'D': []
}
What is an algorithm?
An algorithm is a finite number of clearly described, unambiguous steps that can be systematically followed to produce a desired result for given input in a finite amount of time
Let's take the same shortest path in graph problem and find an algorithm that can solve it.
Problem Statement: Find the shortest path between two nodes in a graph where each edge has a non-negative weight.
Algorithm: Dijkstra’s Algorithm (uses the previously defined representation to solve the problem)
Step 1: Initialization | Set the distance to the source node to 0 and to all other nodes to infinity |
Create a priority queue (min-heap) to hold nodes and their current distances from the source. | |
Insert the source node into the priority queue with a distance of 0 | |
Step 2: Processing | While the priority queue is not empty: |
1. Extract the node with the smallest distance (let's call it current_node). | |
2. For each neighbor of current_node, calculate the potential new distance to that neighbor. | |
3. If the calculated distance is smaller than the currently known distance, update the distance and add the neighbor to the priority queue with the new distance. | |
Step 3: Termination | When the priority queue is empty, the shortest distances to all nodes from the source are finalized. |
Step 4: Output | Return the shortest path distances from the source node to all other nodes. |
Below is the code to solve shortest path in graph problem in python.
import heapq
def dijkstra(graph, start):
distances = {node: float('infinity') for node in graph}
distances[start] = 0
priority_queue = [(0, start)]
while priority_queue:
current_distance, current_node = heapq.heappop(priority_queue)
if current_distance > distances[current_node]:
continue
for neighbor, weight in graph[current_node]:
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(priority_queue, (distance, neighbor))
return distances
# Example usage
graph = {
'A': [('B', 1), ('C', 4)],
'B': [('C', 2), ('D', 5)],
'C': [('D', 1)],
'D': []
}
shortest_paths = dijkstra(graph, 'A')
print(shortest_paths)
Only the relevant detail of the problem needs to be represented, and all the irrelevant details can be left out. Abstraction in complex systems is done by hiding the underlying details and exposing only the essential features.
There can be some limits to Computational Problem Solving such as
Time Complexity: Can the problem be solved in a reasonable time? Time complexity is a measure of the amount of time an algorithm takes to complete as a function of the size of its input. It's typically expressed using Big O notation, which classifies algorithms according to their worst-case or upper bound performance.
Space Complexity: The amount of memory required by an algorithm relative to the input size. Do we have enough space to run the algorithm? Space complexity measures the amount of memory an algorithm uses as a function of the size of its input. It’s important for understanding how an algorithm scales with large inputs, especially in scenarios where memory resources are limited.
Problem Complexity: Some problems are inherently complex and may not be solvable efficiently with current algorithms or computational resources. For example, problems involving large-scale data analysis, real-time processing, or complex simulations may push the boundaries of current computational capabilities. For many complex problems, finding exact solutions may be computationally impractical, leading to the use of approximation algorithms or heuristics. Additionally, some problems are theoretically unsolvable within the framework of algorithmic computation. For example, the Halting Problem shows the limitations of computation, showing that certain problems cannot be resolved algorithmically.
Also, any algorithm that correctly solves a given problem must solve the problem in a reasonable amount of time, otherwise it is of limited practical use.
Computer Algorithms
Computer algorithms are central to computer science. They provide step-by-step methods of computation that a machine ca
An algorithm design paradigm is a general strategy used to solve problems by structuring and organizing algorithms in a systematic way. These paradigms provide a framework for designing algorithms and can be applied to a wide range of problems.
There are various paradigms of Algorithm Design:
Brute Force: Try all possible solutions to find the best one. Often simple but inefficient for large problems.
Approach: Directly implement the problem's definition without optimization.
Use Cases: Useful for problems where the solution space is small or when other methods are too complex.
Example: Finding the shortest path in a graph by examining all possible paths
Divide and Conquer: Break the problem into smaller subproblems, solve each subproblem independently, and then combine the results.
Approach: Recursively divide the problem into smaller instances until reaching a base case.
Use Cases: Efficient for problems that can be naturally divided into smaller, similar problems.
Example: Merge sort (divides the array into halves, sorts each half, and merges them).
Greedy: Make a series of choices, each of which looks best at the moment, i.e. choosing the local best option, in the hope of finding a global optimum.
Approach: Select the locally optimal solution at each step with the hope that this will lead to a globally optimal solution.
Use Cases: Works well for problems where local choices lead to a global optimum.
Example: Kruskal’s and Prim’s algorithms for finding the Minimum Spanning Tree, Huffman coding for data compression.
Dynamic Programming: Solve problems by breaking them down into overlapping subproblems and storing their solutions to avoid redundant work.
Approach: Use a table to keep track of solutions to subproblems, solving each subproblem only once.
Use Cases: Effective for optimization problems with overlapping subproblems and optimal substructure.
Example: Fibonacci sequence computation, Knapsack problem, and Longest Common Subsequence
Backtracking: Explore all possible solutions incrementally, abandoning a solution as soon as it is determined that it cannot be completed to a valid solution.
Approach: Use recursion to explore all potential solutions, and backtrack when a solution path fails to satisfy constraints.
Use Cases: Suitable for constraint satisfaction problems and puzzles.
Example: N-Queens problem, Sudoku solver, and Subset Sum problem.
Randomized Algorithm: Use randomization to achieve good performance on average, often simplifying the design or improving the expected performance.
Approach: Incorporate random choices into the algorithm, which may lead to different outcomes each time it is run.
Use Cases: When deterministic algorithms are too complex or inefficient.
Example: Randomized Quick sort, Monte Carlo methods, and Las Vegas algorithms.
Approximation: Find solutions that are close to the optimal solution within a known factor.
Approach: Use heuristics or specific strategies to find good-enough solutions with performance guarantees.
Use Cases: Problems where finding the exact solution is impractical due to high computational cost.
Example: Approximation algorithms for the Traveling Salesman Problem and Vertex Cover problem.
Reduction: Transform one problem into another to solve it more easily.
Approach: Use known solutions or algorithms for the transformed problem to solve the original problem.
Use Cases: When a problem can be reduced to a known problem with an efficient solution.
Example: Reducing a problem to a simpler problem like reducing the Hamiltonian Path problem to the Hamiltonian Cycle problem.
Heuristic: Use practical methods to find good-enough solutions quickly, often when exact solutions are impractical.
Approach: They use problem-specific knowledge or empirical techniques to guide the search for solutions, aiming for good performance rather than optimality.
Use Cases: Heuristics are used in problems where finding an optimal solution is too expensive or where exact algorithms are not feasible due to time or resource constraints.
Examples:
Greedy Algorithms: Make a series of local optimal choices.
Local Search Algorithms: Such as Hill Climbing or Simulated Annealing, which iteratively improve solutions.
Metaheuristics: Such as Genetic Algorithms, which guide other heuristics for better exploration of the solution space.
For any problem in which a brute-force approach is impractical to use, more efficient problem-solving methods must be discovered that find either an exact or an approximate solution to the problem.
Difference between heuristic and approximation algorithm
Heuristic
Do not provide formal guarantees about the quality of the solution. They aim to be practical and efficient but may vary in solution quality.
Focus on finding a good solution quickly, without necessarily providing any guarantees about how close it is to the best possible solution.
Often used in practice when exact or approximation algorithms are too complex or when no good approximation exists.
Approximation
Provide formal guarantees on how close the solution is to the optimal, usually expressed in terms of an approximation ratio or bound.
Focus on ensuring that the solution is within a certain factor of the optimal solution, even if the exact solution is not feasible to compute.
Used when formal bounds on solution quality are needed, particularly in optimization problems with known approximation bounds.
Complexity Classes
In computational complexity theory, problems are categorized into complexity classes based on their computational requirements:
P (Polynomial Time): Problems that can be solved in polynomial time relative to the input size. Example: Sorting an array.
NP (Nondeterministic Polynomial Time): Problems for which a solution can be verified in polynomial time but finding the solution might not be feasible in polynomial time. Example: The traveling salesman problem.
NP-Complete: A subset of NP problems that are as hard as any problem in NP. If a polynomial-time solution exists for one NP-complete problem, it exists for all NP problems. Example: The knapsack problem.
NP-Hard: Problems that are at least as hard as NP-complete problems but are not necessarily in NP (i.e., they might not have a solution verifiable in polynomial time). Example: The Halting problem.
PSPACE: Problems that can be solved using a polynomial amount of memory. Example: Solving games with perfect information.
EXPTIME: Problems that can be solved in exponential time. Example: Certain types of puzzles or games.
Complexity helps determine the feasibility of solving problems and guides the development of efficient algorithms and approaches.
Computer Hardware
When we think about computers, we often imagine sleek screens and powerful software. However, the heart of any computer system lies in its hardware—the tangible, physical components that make everything work. We need hardware to perform computation on.
Binary Representation
At the core of all these hardware components is a fundamental concept: binary representation. While we use base 10 for everyday counting, computers operate using binary (0s and 1s). This binary system is the foundation of all digital computing, translating complex data into a format that the hardware can process and understand.
The Central Processing Unit (CPU)
CPU is often referred to as the “brain” of the computer, the CPU is where all the action happens. It processes instructions, performs calculations, and manages data flow within the system. Every task, from running a complex application to performing simple calculations, relies on the CPU’s ability to execute commands efficiently.
Graphics Processing Unit (GPU)
While the CPU handles general processing tasks, the GPU (Graphics Processing Unit) is specialized for rendering images and video. Originally designed for graphics-intensive applications like gaming, GPUs are now also used for parallel processing tasks in fields such as machine learning and scientific computations. Their ability to handle multiple tasks simultaneously makes them crucial for both visual and computational performance.
Main Memory
Main memory, also known as RAM (Random Access Memory), is where a computer temporarily stores data that is actively being used or processed. This volatile memory is crucial for quick access to information and smooth operation of programs. However, it loses its contents when the power is turned off, which is why we need secondary storage for long-term data retention.
Secondary Memory
For long-term storage, computers rely on secondary memory. This includes hard drives, solid-state drives, optical discs, and USB flash drives. Unlike main memory, secondary memory is nonvolatile, meaning it retains data even when the power is off. It’s where all your files, applications, and system data are stored permanently.
Input and Output Devices
Input devices, like keyboards and mouse, allow users to interact with the computer. On the other hand, output devices such as monitors and printers provide feedback and results from the computer. Together, these devices form the interface through which we communicate with our machines.
Buses and Data Transfer
Buses are crucial for data movement within a computer system. They act as communication channels between the CPU, memory, and peripheral devices, ensuring that data flows smoothly and efficiently across the different components.
Communication Devices
In today’s interconnected world, communication devices are crucial for connecting to networks and the internet. Wi-Fi cards enable wireless connections to local networks and the internet, allowing for flexibility and mobility. Modems, on the other hand, connect your computer to the broader internet through various types of connections such as DSL, cable, or fiber optics. These devices are essential for accessing online resources, conducting business, and staying connected in a digital world.
Computer Software
Computer software is a set of program instructions, including related data and documentation, that can be executed by computer. This can be in the form of instructions on paper, or in digital form. While system software is intrinsic to a computer system, application software fulfills users’ needs, such as a photo-editing program.
Operating System
An operating system is software that has the job of managing the hardware resources of a given computer and providing a particular user interface.
Application Software
This includes programs designed to perform specific tasks for users. Examples include word processors (like Microsoft Word), web browsers (like Google Chrome), and media players (like VLC). Application software leverages the underlying OS and hardware to perform tasks ranging from document editing to media playback.
Development Software
Also known as development tools, these include compilers, debuggers, and integrated development environments (IDEs). These tools assist programmers in writing, testing, and debugging code. Examples include Visual Studio, Eclipse, and IntelliJ IDEA.
System Utilities
These are specialized programs that help manage and tune the computer system. They include antivirus software, disk cleanup tools, and system monitoring utilities. They ensure the system runs efficiently and securely.
The Process of Computational Problem Solving
Computational problem solving does not simply involve the act of computer programming. It is a process , with programming being only one of the steps. Before a program is written, a design for the program must be developed. And before a design can be developed, the problem to be solved must be well understood. Once written, the program must be thoroughly tested.
Analyze Problem:
Clearly understand the problem
Know what constitutes a solution
Describe Data and Algorithms
Determine what type of data is needed
Determine how data is to be structured
Find and/or design appropriate algorithms
Implement Program
Represent data within programming language
Implement algorithms in programming language
Test and Debug
Test the program on a selected set of problem instances
Correct and understand the causes of any errors found
Why Learn Programming at all?
Learning programming in the GPT era is still highly valuable for several reasons:
Enhanced Problem-Solving Skills: Programming develops critical thinking and problem-solving skills. Understanding how to write and debug code helps you approach complex problems logically and systematically, which is beneficial beyond just coding tasks.
Customization and Control: While GPT and other AI tools can assist with code generation and automation, knowing how to program allows you to customize solutions to fit specific needs and gain full control over your projects. You can tailor algorithms, integrate systems, and adapt solutions in ways that general-purpose AI tools may not fully accommodate.
Understanding AI and Technology: Learning programming provides a deeper understanding of how AI systems like GPT work. This knowledge can help you leverage these tools more effectively, create better prompts, and integrate AI into your projects with a clearer grasp of their limitations and capabilities.
Innovation and Creativity: Programming empowers you to create and innovate beyond the capabilities of existing tools. It allows you to build new applications, develop unique features, and explore creative solutions that AI tools alone might not generate.
Job Market Relevance: Despite advances in AI, programming skills are still in high demand across various industries. Knowledge of programming can open up diverse career opportunities in software development, data science, cybersecurity, and more.
Learning and Growth: Programming fosters continuous learning and growth. As technology evolves, new programming languages, frameworks, and paradigms emerge. Engaging with programming keeps you adaptable and up-to-date with technological advancements.
Interdisciplinary Applications: Programming skills are valuable in many fields, including scientific research, finance, healthcare, and the arts. Understanding programming can enhance your ability to work on interdisciplinary projects and apply technology creatively in various domains.
Collaboration and Communication: Knowing how to code improves your ability to collaborate with developers, engineers, and tech professionals. It enhances communication and helps you understand and contribute to tech-driven projects effectively.
Overall, while AI tools like ChatGPT and Gemini offer significant assistance, programming remains a fundamental skill that empowers you to innovate, solve problems, and understand technology in a deeper and more meaningful way.
Subscribe to my newsletter
Read articles from Jyoti Maurya directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Jyoti Maurya
Jyoti Maurya
I create cross platform mobile apps with AI functionalities. Currently a PhD Scholar at Indira Gandhi Delhi Technical University for Women, Delhi. M.Tech in Artificial Intelligence (AI).