C++ program that demonstrates the use of Heaps, Priority Queues, and Recursion in a scientific computing context. We'll implement a simulation system that manages computational tasks with different priorities and uses recursion for a divide-and-conquer algorithm.
algorithm, auto, command_line_arguments, exception, iostream, namespace, range-based, sstream, std::cin, std::cout, std::cerr, std::exception, std::ifstream, std::ofstream, stdexcept, throw, vector
#include <iostream>
#include <vector>
#include <queue>
#include <functional>
#include <string>
#include <cmath>
// Structure to represent a computational task
struct ComputationalTask {
std::string name;
int priority;
double complexity;
ComputationalTask(std::string n, int p, double c) : name(n), priority(p), complexity(c) {}
// Overload the comparison operator for priority queue
bool operator<(const ComputationalTask& other) const {
return priority < other.priority;
}
};
// Priority Queue for managing computational tasks
class TaskScheduler {
private:
std::priority_queue<ComputationalTask> tasks;
public:
void addTask(const ComputationalTask& task) {
tasks.push(task);
}
ComputationalTask getNextTask() {
if (tasks.empty()) {
throw std::runtime_error("No tasks available");
}
ComputationalTask nextTask = tasks.top();
tasks.pop();
return nextTask;
}
bool hasTasks() const {
return !tasks.empty();
}
};
// Recursive function to compute factorial
unsigned long long factorial(int n) {
if (n == 0 || n == 1) {
return 1;
}
return n * factorial(n - 1);
}
// Recursive function for numerical integration using Simpson's rule
double simpsonIntegration(std::function<double(double)> f, double a, double b, int depth, int maxDepth) {
double c = (a + b) / 2;
double h = b - a;
double fa = f(a), fb = f(b), fc = f(c);
double s = (h / 6) * (fa + 4 * fc + fb);
if (depth >= maxDepth) {
return s;
}
double left = simpsonIntegration(f, a, c, depth + 1, maxDepth);
double right = simpsonIntegration(f, c, b, depth + 1, maxDepth);
return left + right;
}
int main() {
TaskScheduler scheduler;
// Add some computational tasks
scheduler.addTask(ComputationalTask("Matrix Multiplication", 3, 100.0));
scheduler.addTask(ComputationalTask("FFT", 2, 50.0));
scheduler.addTask(ComputationalTask("Neural Network Training", 1, 500.0));
scheduler.addTask(ComputationalTask("Data Preprocessing", 4, 30.0));
std::cout << "Processing tasks in priority order:\n";
while (scheduler.hasTasks()) {
ComputationalTask task = scheduler.getNextTask();
std::cout << "Executing task: " << task.name
<< " (Priority: " << task.priority
<< ", Complexity: " << task.complexity << ")\n";
}
std::cout << "\nDemonstrating recursive factorial calculation:\n";
int n = 5;
std::cout << "Factorial of " << n << " is: " << factorial(n) << "\n";
std::cout << "\nDemonstrating recursive numerical integration:\n";
auto f = [](double x) { return std::sin(x); }; // Function to integrate
double a = 0, b = M_PI;
int maxDepth = 10;
double result = simpsonIntegration(f, a, b, 0, maxDepth);
std::cout << "Integral of sin(x) from 0 to pi: " << result << "\n";
return 0;
}
This program demonstrates the following concepts in a scientific computing context:
TaskScheduler
class uses std::priority_queue
to manage computational tasks.This simulates a system where computational resources are allocated to more critical tasks first.
Recursion and Stack Frames:
factorial
function demonstrates simple recursion to calculate factorials.simpsonIntegration
function shows a more complex recursive algorithm for numerical integration using Simpson's rule.Both functions implicitly demonstrate the use of stack frames in recursion.
Scientific Computing Applications:
Numerical Integration: Demonstrates a practical application of recursion in scientific computing for approximating definite integrals.
Additional C++ Features:
std::function
is used to pass a lambda function for integration.This implementation provides examples of how heaps (via priority queues) and recursion can be applied in scientific computing scenarios:
The program also implicitly demonstrates the concept of stack frames through its recursive functions. Each recursive call creates a new stack frame, storing local variables and the return address. This is particularly evident in the simpsonIntegration
function, where the recursion depth is tracked and limited to prevent stack overflow.