Table of contents
1.
Introduction
2.
What is a Deterministic Algorithm?
2.1.
Characteristics of Deterministic Algorithms:
2.2.
Applications of Deterministic Algorithms:
2.3.
Advantages of Deterministic Algorithms
2.4.
Examples of Deterministic Algorithms
2.4.1.
Example 1: Binary Search Algorithm
2.5.
Python
2.6.
C++
2.7.
Java
2.8.
C#
2.9.
Javascript
2.9.1.
Example 2: Merge Sort Algorithm
2.10.
Python
2.11.
C++
2.12.
Java
2.13.
C#
2.14.
Javascript
3.
What is a Non-Deterministic Algorithm?
3.1.
Characteristics of Non-Deterministic Algorithms:
3.2.
Applications of Non-Deterministic Algorithms:
3.3.
Advantages of Non-Deterministic Algorithms
3.4.
Detailed Examples of Non-Deterministic Algorithms
3.4.1.
Example 1: Genetic Algorithm
3.5.
Python
3.6.
C++
3.7.
Java
3.8.
C#
3.9.
Javascript
3.9.1.
Example 2: Monte Carlo Algorithm
3.10.
Python
3.11.
C++
3.12.
Java
3.13.
C#
3.14.
Javascript
4.
Difference between Deterministic and Non-Deterministic Algorithms
5.
Frequently Asked Questions
5.1.
Can deterministic algorithms be used for problems that seem inherently nondeterministic?
5.2.
How do nondeterministic algorithms perform in terms of efficiency compared to deterministic algorithms?
5.3.
Are there any specific industries or fields where nondeterministic algorithms are particularly prevalent?
6.
Conclusion
Last Updated: Oct 8, 2024
Medium

Deterministic and Non-Deterministic Algorithms

Author Riya Singh
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Algorithms are the foundation of computer science, which dictates how computers process data and solve problems. Deterministic and nondeterministic algorithms are two fundamental classes of algorithms in computer science. Deterministic algorithms always produce the same output for a given input, which is followed by a predictable sequence of steps. In contrast, nondeterministic algorithms can yield different outputs for the same input, which opens multiple paths simultaneously to solve problems efficiently.

 Deterministic and Non-Deterministic Algorithms

In this article, we will talk about both of these types, their advantages, disadvantages, characteristics, and different examples.

What is a Deterministic Algorithm?

A deterministic algorithm is one where, given a particular input, the algorithm will always produce the same output and follow the same sequence of states. It operates under the principle of predictability and consistency. The outcome of a deterministic algorithm can be precisely determined based on its input.

Characteristics of Deterministic Algorithms:

  • Predictability: The same input will always result in the same output.
  • Fixed Steps: The algorithm follows a clear set of rules or steps.
  • No Randomness: There is no element of randomness or probability in the process.
  • Easy to Debug: Predictable behavior makes them easier to test and debug.

Applications of Deterministic Algorithms:

  • Sorting and Searching: Algorithms like QuickSort, MergeSort, and Binary Search.
  • Mathematical Calculations: Algorithms used in calculating functions in mathematics and physics.
  • Data Compression: Algorithms like Huffman coding and Run-length encoding.
  • Cryptography: Algorithms for encryption and decryption like AES (Advanced Encryption Standard).

Advantages of Deterministic Algorithms

Deterministic algorithms are foundational in many areas of computer science and technology. Their predictability and reliability offer several advantages:

  • Consistency and Reliability: The foremost benefit of deterministic algorithms is their consistent output for a given input. This reliability is crucial in applications where the same result is needed every time, such as in financial calculations or deterministic simulations in engineering.
     
  • Ease of Testing and Verification: Due to their predictable nature, deterministic algorithms are easier to test and verify. This is especially important in software development and debugging, where being able to replicate results is essential for identifying and fixing issues.
     
  • Optimization Opportunities: The fixed nature of deterministic algorithms often allows for more straightforward optimization. Since the execution path is predictable, developers can focus on optimizing specific parts of the algorithm for performance improvements.
     
  • Simplicity in Implementation: Generally, deterministic algorithms are simpler to implement compared to nondeterministic ones. Their straightforward nature, devoid of randomness or probabilistic elements, makes them more accessible, especially for foundational computer science education and application.
     
  • Reproducibility in Scientific Research: In scientific computing, deterministic algorithms ensure that experiments can be reliably replicated, which is a cornerstone of scientific research. This reproducibility is essential for validating results and building upon existing research.

Examples of Deterministic Algorithms

Example 1: Binary Search Algorithm

Binary Search is a classic example of a deterministic algorithm used in searching. It operates by repeatedly dividing a sorted array in half and comparing the middle element with the target value. If the middle element is the target, the search is successful. If not, the algorithm eliminates the half in which the target cannot lie and repeats the process on the remaining half.

Implementation :

  • Python
  • C++
  • Java
  • C#
  • Javascript

Python

def binary_search(arr, target):
low = 0
high = len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
return -1

# Example usage
arr = [1, 3, 5, 7, 9]
target = 5
result = binary_search(arr, target)
print("Index of target is:", result)
You can also try this code with Online Python Compiler
Run Code

C++

#include <iostream>
#include <vector>

int binary_search(const std::vector<int>& arr, int target) {
int low = 0;
int high = arr.size() - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}

int main() {
std::vector<int> arr = {1, 3, 5, 7, 9};
int target = 5;
int result = binary_search(arr, target);
std::cout << "Index of target is: " << result << std::endl;
return 0;
}
You can also try this code with Online C++ Compiler
Run Code

Java

public class BinarySearch {

public static int binary_search(int[] arr, int target) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}

public static void main(String[] args) {
int[] arr = {1, 3, 5, 7, 9};
int target = 5;
int result = binary_search(arr, target);
System.out.println("Index of target is: " + result);
}
}
You can also try this code with Online Java Compiler
Run Code

C#

using System;

public class BinarySearch {
public static int BinarySearchMethod(int[] arr, int target) {
int low = 0;
int high = arr.Length - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}

public static void Main() {
int[] arr = {1, 3, 5, 7, 9};
int target = 5;
int result = BinarySearchMethod(arr, target);
Console.WriteLine("Index of target is: " + result);
}
}

Javascript

function binarySearch(arr, target) {
let low = 0;
let high = arr.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
if (arr[mid] === target) {
return mid;
} else if (arr[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}

const arr = [1, 3, 5, 7, 9];
const target = 5;
const result = binarySearch(arr, target);
console.log("Index of target is:", result);
You can also try this code with Online Javascript Compiler
Run Code

Output

Index of target is: 2

Example 2: Merge Sort Algorithm

Merge Sort is a deterministic sorting algorithm known for its efficiency and stability. It adopts a divide-and-conquer approach, dividing the list into halves, sorting each half, and then merging them back together in sorted order.

Implementation :

  • Python
  • C++
  • Java
  • C#
  • Javascript

Python

def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
L = arr[:mid]
R = arr[mid:]

merge_sort(L)
merge_sort(R)

i = j = k = 0

while i < len(L) and j < len(R):
if L[i] < R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1

while i < len(L):
arr[k] = L[i]
i += 1
k += 1

while j < len(R):
arr[k] = R[j]
j += 1
k += 1

# Example usage
arr = [12, 11, 13, 5, 6, 7]
merge_sort(arr)
print("Sorted array is:", arr)
You can also try this code with Online Python Compiler
Run Code

C++

#include <iostream>
#include <vector>

void merge(std::vector<int>& arr, int const left, int const mid, int const right) {
auto const subArrayOne = mid - left + 1;
auto const subArrayTwo = right - mid;

std::vector<int> leftArray(subArrayOne), rightArray(subArrayTwo);

for (auto i = 0; i < subArrayOne; i++)
leftArray[i] = arr[left + i];
for (auto j = 0; j < subArrayTwo; j++)
rightArray[j] = arr[mid + 1 + j];

auto indexOfSubArrayOne = 0, indexOfSubArrayTwo = 0;
int indexOfMergedArray = left;

while (indexOfSubArrayOne < subArrayOne && indexOfSubArrayTwo < subArrayTwo) {
if (leftArray[indexOfSubArrayOne] <= rightArray[indexOfSubArrayTwo]) {
arr[indexOfMergedArray] = leftArray[indexOfSubArrayOne];
indexOfSubArrayOne++;
} else {
arr[indexOfMergedArray] = rightArray[indexOfSubArrayTwo];
indexOfSubArrayTwo++;
}
indexOfMergedArray++;
}

while (indexOfSubArrayOne < subArrayOne) {
arr[indexOfMergedArray] = leftArray[indexOfSubArrayOne];
indexOfSubArrayOne++;
indexOfMergedArray++;
}

while (indexOfSubArrayTwo < subArrayTwo) {
arr[indexOfMergedArray] = rightArray[indexOfSubArrayTwo];
indexOfSubArrayTwo++;
indexOfMergedArray++;
}
}

void mergeSort(std::vector<int>& arr, int const begin, int const end) {
if (begin >= end)
return;

auto mid = begin + (end - begin) / 2;
mergeSort(arr, begin, mid);
mergeSort(arr, mid + 1, end);
merge(arr, begin, mid, end);
}

int main() {
std::vector<int> arr = {12, 11, 13, 5, 6, 7};
mergeSort(arr, 0, arr.size() - 1);

std::cout << "Sorted array is: ";
for (int i : arr)
std::cout << i << " ";
std::cout << std::endl;
return 0;
}
You can also try this code with Online C++ Compiler
Run Code

Java

import java.util.Arrays;

public class MergeSort {

public static void mergeSort(int[] arr, int left, int right) {
if (left < right) {
int mid = (left + right) / 2;

mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);

merge(arr, left, mid, right);
}
}

public static void merge(int[] arr, int left, int mid, int right) {
int[] L = Arrays.copyOfRange(arr, left, mid + 1);
int[] R = Arrays.copyOfRange(arr, mid + 1, right + 1);

int i = 0, j = 0, k = left;
while (i < L.length && j < R.length) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}

while (i < L.length) {
arr[k] = L[i];
i++;
k++;
}

while (j < R.length) {
arr[k] = R[j];
j++;
k++;
}
}

public static void main(String[] args) {
int[] arr = {12, 11, 13, 5, 6, 7};
mergeSort(arr, 0, arr.length - 1);
System.out.println("Sorted array is: " + Arrays.toString(arr));
}
}
You can also try this code with Online Java Compiler
Run Code

C#

using System;

public class MergeSort {
public static void MergeSortAlgorithm(int[] arr, int left, int right) {
if (left < right) {
int mid = (left + right) / 2;

MergeSortAlgorithm(arr, left, mid);
MergeSortAlgorithm(arr, mid + 1, right);

Merge(arr, left, mid, right);
}
}

private static void Merge(int[] arr, int left, int mid, int right) {
int[] L = new int[mid - left + 1];
int[] R = new int[right - mid];

Array.Copy(arr, left, L, 0, mid - left + 1);
Array.Copy(arr, mid + 1, R, 0, right - mid);

int i = 0, j = 0, k = left;
while (i < L.Length && j < R.Length) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}

while (i < L.Length) {
arr[k] = L[i];
i++;
k++;
}

while (j < R.Length) {
arr[k] = R[j];
j++;
k++;
}
}

public static void Main() {
int[] arr = {12, 11, 13, 5, 6, 7};
MergeSortAlgorithm(arr, 0, arr.Length - 1);
Console.WriteLine("Sorted array is: " + String.Join(" ", arr));
}
}

Javascript

function mergeSort(arr, left, right) {
if (left < right) {
const mid = Math.floor((left + right) / 2);

mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);

merge(arr, left, mid, right);
}
}

function merge(arr, left, mid, right) {
const L = arr.slice(left, mid + 1);
const R = arr.slice(mid + 1, right + 1);

let i = 0, j = 0, k = left;
while (i < L.length && j < R.length) {
if (L[i] < R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}

while (i < L.length) {
arr[k] = L[i];
i++;
k++;
}

while (j < R.length) {
arr[k] = R[j];
j++;
k++;
}
}

const arr = [12, 11, 13, 5, 6, 7];
mergeSort(arr, 0, arr.length - 1);
console.log("Sorted array is:", arr);
You can also try this code with Online Javascript Compiler
Run Code

Output

Sorted array is: [5, 6, 7, 11, 12, 13]

What is a Non-Deterministic Algorithm?

A non-deterministic algorithm is one where the same input can lead to multiple possible outcomes. Unlike deterministic algorithms, these do not follow a single clear path through execution. Instead, they explore various paths or possibilities, often simultaneously.

Characteristics of Non-Deterministic Algorithms:

  • Multiple Possible Outcomes: The same input might result in different outputs on different runs.
  • Probabilistic Elements: Often involves randomness or probability in decision-making.
  • Parallel Path Exploration: Can explore multiple solution paths simultaneously.
  • Complexity: Generally more complex than deterministic algorithms.

Applications of Non-Deterministic Algorithms:

  • Machine Learning and AI: Used in algorithms for training neural networks where random initialization of weights is involved.
  • Heuristic Search Algorithms: In optimization problems, like traveling salesman problem, where multiple paths are explored.
  • Randomized Algorithms: Such as in Monte Carlo simulations, used in financial modeling and physics.
  • Cryptography: Certain cryptographic protocols use nondeterministic algorithms for encryption and key generation.

Advantages of Non-Deterministic Algorithms

Non-deterministic algorithms have unique advantages, particularly in fields where exploration of multiple possibilities or handling uncertainty is necessary.

  • Handling Ambiguity: They excel in scenarios where the problem itself or the data involved is ambiguous or has inherent uncertainty.
     
  • Exploration of Multiple Solutions: These algorithms can explore a wide range of potential solutions simultaneously, which is particularly useful in optimization and search problems.
     
  • Speed and Efficiency in Certain Tasks: In some cases, nondeterministic algorithms can find solutions faster than deterministic ones by avoiding the need to explore every possible path.
     
  • Adaptability: They are adaptable to changes and can work with incomplete information, making them suitable for real-world, dynamic environments.
     
  • Enhanced Creativity and Innovation: In fields like AI and machine learning, nondeterministic algorithms can lead to innovative solutions and creative problem-solving approaches that deterministic algorithms might not uncover.

Detailed Examples of Non-Deterministic Algorithms

Example 1: Genetic Algorithm

Genetic Algorithms (GAs) are inspired by the process of natural selection. They are used to find optimal or near-optimal solutions to complex problems by generating a population of candidate solutions and then iteratively selecting, breeding, and mutating these candidates in a way that is analogous to biological evolution.

Basic Implementation Concept:

  • Initialization: Start with a randomly generated population of solutions.
     
  • Selection: Evaluate the fitness of each individual and select the fittest ones.
     
  • Crossover: Combine parts of good solutions to create new solutions.
     
  • Mutation: Introduce random changes to some individuals.
     
  • Repeat: Repeat the process over several generations.

GAs are widely used in optimization problems, scheduling, and machine learning.

Let's see the code Implementation : 

  • Python
  • C++
  • Java
  • C#
  • Javascript

Python

import random

target = "Hello, World!"
characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ, !"

def generate_individual(length):
return ''.join(random.choice(characters) for _ in range(length))

def calculate_fitness(individual):
return sum(ch1 == ch2 for ch1, ch2 in zip(individual, target))

def mutate(individual):
index = random.randint(0, len(individual) - 1)
new_char = random.choice(characters)
return individual[:index] + new_char + individual[index+1:]

def crossover(parent1, parent2):
index = random.randint(1, len(parent1) - 1)
return parent1[:index] + parent2[index:]

population = [generate_individual(len(target)) for _ in range(100)]
generation = 0

while True:
population = sorted(population, key=lambda x: -calculate_fitness(x))
if calculate_fitness(population[0]) == len(target):
break
next_generation = population[:2] # Keep best two
while len(next_generation) < len(population):
parent1, parent2 = random.sample(population[:50], 2) # Select from the best 50%
child = crossover(parent1, parent2)
if random.random() < 0.1: # Mutation chance
child = mutate(child)
next_generation.append(child)
population = next_generation
generation += 1

print(f"Target reached in generation {generation}: {population[0]}")
You can also try this code with Online Python Compiler
Run Code

C++

#include <iostream>
#include <vector>
#include <string>
#include <random>
#include <algorithm>

std::string target = "Hello, World!";
std::string characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ, !";
std::random_device rd;
std::mt19937 gen(rd());

std::string generate_individual(int length) {
std::uniform_int_distribution<> dist(0, characters.size() - 1);
std::string result;
for (int i = 0; i < length; ++i) {
result += characters[dist(gen)];
}
return result;
}

int calculate_fitness(const std::string& individual) {
int fitness = 0;
for (size_t i = 0; i < individual.size(); ++i) {
if (individual[i] == target[i])
++fitness;
}
return fitness;
}

std::string mutate(std::string individual) {
std::uniform_int_distribution<> dist_index(0, individual.size() - 1);
std::uniform_int_distribution<> dist_char(0, characters.size() - 1);
int index = dist_index(gen);
individual[index] = characters[dist_char(gen)];
return individual;
}

std::string crossover(const std::string& parent1, const std::string& parent2) {
std::uniform_int_distribution<> dist(1, parent1.size() - 1);
int index = dist(gen);
return parent1.substr(0, index) + parent2.substr(index);
}

int main() {
std::vector<std::string> population(100);
std::generate(population.begin(), population.end(), [&] { return generate_individual(target.size()); });
int generation = 0;

while (true) {
std::sort(population.begin(), population.end(), [](const std::string& a, const std::string& b) {
return calculate_fitness(a) > calculate_fitness(b);
});

if (calculate_fitness(population[0]) == target.size()) {
break;
}

std::vector<std::string> next_generation = { population[0], population[1] };
std::uniform_int_distribution<> dist(0, 49);
std::uniform_real_distribution<> mutation_chance(0.0, 1.0);

while (next_generation.size() < population.size()) {
const auto& parent1 = population[dist(gen)];
const auto& parent2 = population[dist(gen)];
auto child = crossover(parent1, parent2);
if (mutation_chance(gen) < 0.1) {
child = mutate(child);
}
next_generation.push_back(child);
}

population = std::move(next_generation);
++generation;
}

std::cout << "Target reached in generation " << generation << ": " << population[0] << std::endl;
return 0;
}
You can also try this code with Online C++ Compiler
Run Code

Java

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class GeneticAlgorithm {

private static final String target = "Hello, World!";
private static final String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ, !";
private static Random random = new Random();

private static String generateIndividual(int length) {
StringBuilder result = new StringBuilder(length);
for (int i = 0; i < length; i++) {
result.append(characters.charAt(random.nextInt(characters.length())));
}
return result.toString();
}

private static int calculateFitness(String individual) {
int fitness = 0;
for (int i = 0; i < individual.length(); i++) {
if (individual.charAt(i) == target.charAt(i)) {
fitness++;
}
}
return fitness;
}

private static String mutate(String individual) {
char[] chars = individual.toCharArray();
int index = random.nextInt(chars.length);
chars[index] = characters.charAt(random.nextInt(characters.length()));
return new String(chars);
}

private static String crossover(String parent1, String parent2) {
int index = 1 + random.nextInt(parent1.length() - 1);
return parent1.substring(0, index) + parent2.substring(index);
}

public static void main(String[] args) {
List<String> population = new ArrayList<>();
for (int i = 0; i < 100; i++) {
population.add(generateIndividual(target.length()));
}
int generation = 0;

while (true) {
Collections.sort(population, (a, b) -> Integer.compare(calculateFitness(b), calculateFitness(a)));
if (calculateFitness(population.get(0)) == target.length()) {
break;
}
List<String> nextGeneration = new ArrayList<>(List.of(population.get(0), population.get(1)));
while (nextGeneration.size() < population.size()) {
String parent1 = population.get(random.nextInt(50));
String parent2 = population.get(random.nextInt(50));
String child = crossover(parent1, parent2);
if (random.nextDouble() < 0.1) {
child = mutate(child);
}
nextGeneration.add(child);
}
population = nextGeneration;
generation++;
}

System.out.println("Target reached in generation " + generation + ": " + population.get(0));
}
}
You can also try this code with Online Java Compiler
Run Code

C#

using System;
using System.Collections.Generic;
using System.Linq;

public class GeneticAlgorithm
{
private static readonly string target = "Hello, World!";
private static readonly string characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ, !";
private static readonly Random random = new Random();

private static string GenerateIndividual(int length)
{
return new string(Enumerable.Repeat(characters, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}

private static int CalculateFitness(string individual)
{
return individual.Zip(target, (c1, c2) => c1 == c2 ? 1 : 0).Sum();
}

private static string Mutate(string individual)
{
char[] chars = individual.ToCharArray();
int index = random.Next(chars.Length);
chars[index] = characters[random.Next(characters.Length)];
return new string(chars);
}

private static string Crossover(string parent1, string parent2)
{
int index = random.Next(1, parent1.Length);
return parent1.Substring(0, index) + parent2.Substring(index);
}

public static void Main()
{
List<string> population = new List<string>();
for (int i = 0; i < 100; i++)
{
population.Add(GenerateIndividual(target.Length));
}

int generation = 0;
while (true)
{
population = population.OrderByDescending(individual => CalculateFitness(individual)).ToList();
if (CalculateFitness(population[0]) == target.Length)
{
break;
}

List<string> nextGeneration = new List<string> { population[0], population[1] };
while (nextGeneration.Count < population.Count)
{
string parent1 = population[random.Next(50)];
string parent2 = population[random.Next(50)];
string child = Crossover(parent1, parent2);
if (random.NextDouble() < 0.1)
{
child = Mutate(child);
}
nextGeneration.Add(child);
}

population = nextGeneration;
generation++;
}

Console.WriteLine("Target reached in generation " + generation + ": " + population[0]);
}
}

Javascript

function generateIndividual(length) {
const characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ, !";
return Array.from({ length }, () => characters[Math.floor(Math.random() * characters.length)]).join('');
}

function calculateFitness(individual) {
const target = "Hello, World!";
return individual.split('').reduce((count, char, index) => count + (char === target[index] ? 1 : 0), 0);
}

function mutate(individual) {
const characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ, !";
const chars = individual.split('');
const index = Math.floor(Math.random() * chars.length);
chars[index] = characters[Math.floor(Math.random() * characters.length)];
return chars.join('');
}

function crossover(parent1, parent2) {
const index = Math.floor(Math.random() * (parent1.length - 1)) + 1;
return parent1.substring(0, index) + parent2.substring(index);
}

let population = Array.from({ length: 100 }, () => generateIndividual("Hello, World!".length));
let generation = 0;

while (true) {
population.sort((a, b) => calculateFitness(b) - calculateFitness(a));
if (calculateFitness(population[0]) === "Hello, World!".length) {
break;
}

const nextGeneration = [population[0], population[1]];
while (nextGeneration.length < population.length) {
const parent1 = population[Math.floor(Math.random() * 50)];
const parent2 = population[Math.floor(Math.random() * 50)];
let child = crossover(parent1, parent2);
if (Math.random() < 0.1) {
child = mutate(child);
}
nextGeneration.push(child);
}

population = nextGeneration;
generation++;
}

console.log(`Target reached in generation ${generation}: ${population[0]}`);
You can also try this code with Online Javascript Compiler
Run Code

 

Output

output

This example demonstrates a basic genetic algorithm evolving a population of strings to match the target string "Hello, World!". It's a simplified model but effectively illustrates the core concepts of selection, crossover, and mutation.

Example 2: Monte Carlo Algorithm

Monte Carlo algorithms use randomness to solve problems that might be deterministic in principle. They are particularly useful in simulations and numerical integration.

Basic Implementation Concept:

  • Define a Domain: Set up the problem space or domain.
     
  • Random Sampling: Generate random numbers or samples within this domain.
     
  • Computation: Perform calculations based on these random samples.
     
  • Aggregation: Aggregate the results to approximate the final solution.

Monte Carlo methods are extensively used in financial risk analysis, physical simulations, and solving complex mathematical problems.

The Monte Carlo method can be demonstrated with a simple example for estimating the value of π.

  • Python
  • C++
  • Java
  • C#
  • Javascript

Python

import random

def estimate_pi(num_samples):
inside_circle = 0
for _ in range(num_samples):
x, y = random.random(), random.random()
if x**2 + y**2 <= 1.0:
inside_circle += 1
return 4 * inside_circle / num_samples

num_samples = 10000
pi_estimate = estimate_pi(num_samples)
print(f"Estimated value of pi: {pi_estimate}")
You can also try this code with Online Python Compiler
Run Code

C++

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>

double estimate_pi(int num_samples) {
int inside_circle = 0;
for (int i = 0; i < num_samples; ++i) {
double x = static_cast<double>(rand()) / RAND_MAX;
double y = static_cast<double>(rand()) / RAND_MAX;
if (x * x + y * y <= 1.0) {
inside_circle++;
}
}
return 4 * static_cast<double>(inside_circle) / num_samples;
}

int main() {
srand(time(nullptr)); // Initialize random seed
int num_samples = 10000;
double pi_estimate = estimate_pi(num_samples);
std::cout << "Estimated value of pi: " << pi_estimate << std::endl;
return 0;
}
You can also try this code with Online C++ Compiler
Run Code

Java

public class MonteCarloPi {
public static double estimatePi(int numSamples) {
int insideCircle = 0;
for (int i = 0; i < numSamples; i++) {
double x = Math.random();
double y = Math.random();
if (x * x + y * y <= 1.0) {
insideCircle++;
}
}
return 4.0 * insideCircle / numSamples;
}

public static void main(String[] args) {
int numSamples = 10000;
double piEstimate = estimatePi(numSamples);
System.out.println("Estimated value of pi: " + piEstimate);
}
}
You can also try this code with Online Java Compiler
Run Code

C#

using System;

class MonteCarloPi {
public static double EstimatePi(int numSamples) {
int insideCircle = 0;
Random random = new Random();
for (int i = 0; i < numSamples; i++) {
double x = random.NextDouble();
double y = random.NextDouble();
if (x * x + y * y <= 1.0) {
insideCircle++;
}
}
return 4.0 * insideCircle / numSamples;
}

static void Main() {
int numSamples = 10000;
double piEstimate = EstimatePi(numSamples);
Console.WriteLine("Estimated value of pi: " + piEstimate);
}
}

Javascript

function estimatePi(numSamples) {
let insideCircle = 0;
for (let i = 0; i < numSamples; i++) {
const x = Math.random();
const y = Math.random();
if (x * x + y * y <= 1.0) {
insideCircle++;
}
}
return 4 * insideCircle / numSamples;
}

const numSamples = 10000;
const piEstimate = estimatePi(numSamples);
console.log(`Estimated value of pi: ${piEstimate}`);
You can also try this code with Online Javascript Compiler
Run Code

Output

Estimated value of pi: 3.1348

This code estimates π by randomly placing points in a unit square and calculating the proportion that falls inside a quarter circle. It's a basic demonstration of how Monte Carlo methods can be used for numerical integration and approximation.

Difference between Deterministic and Non-Deterministic Algorithms

Aspect Deterministic Algorithms Non-Deterministic Algorithms
Outcome Predictability Produce the same output for a given input. Can produce different outputs for the same input.
Execution Path Follow a single, clear path through execution. Explore multiple paths, often simultaneously.
Randomness No element of randomness. Often involve randomness or probability.
Testing and Verification Easier to test and verify due to predictability. Testing can be more challenging due to variability in outcomes.
Complexity Generally simpler and more straightforward. Tend to be more complex due to multiple possible paths.
Optimization Easier to optimize as the execution path is predictable. Optimization can be more challenging but offers exploration of multiple solutions.
Applications Sorting, searching, mathematical calculations, data compression, cryptography. Machine learning, heuristic search, randomized algorithms, certain cryptographic protocols.
Advantages Consistency, ease of testing, straightforward optimization, simplicity in implementation. Handling ambiguity, exploring multiple solutions, potentially faster in certain tasks, adaptability, creativity.

Read More, floyd's algorithm

Frequently Asked Questions

Can deterministic algorithms be used for problems that seem inherently nondeterministic?

 Yes, deterministic algorithms can sometimes be adapted for problems that appear nondeterministic. This is often achieved by incorporating additional information or making assumptions that simplify the problem. However, the efficiency and practicality of using a deterministic approach in such cases depend on the specific problem and the constraints involved.

How do nondeterministic algorithms perform in terms of efficiency compared to deterministic algorithms?

The efficiency of nondeterministic algorithms varies greatly depending on the problem and the specific algorithm. In some cases, they can find solutions much faster than deterministic algorithms by exploring multiple paths simultaneously. However, this is not universally true, and for some problems, deterministic algorithms may be more efficient.

Are there any specific industries or fields where nondeterministic algorithms are particularly prevalent?

Nondeterministic algorithms are particularly prevalent in fields like artificial intelligence and machine learning, where they are used for training models and handling data with inherent uncertainties. They are also widely used in optimization problems, financial modeling (e.g., Monte Carlo simulations), and in creating heuristic algorithms for complex problem-solving.

Conclusion

In the diverse landscape of computer algorithms, understanding the distinction between deterministic and nondeterministic algorithms is crucial. Deterministic algorithms offer predictability and ease of testing, making them ideal for applications requiring consistent outcomes. On the other hand, nondeterministic algorithms excel in handling ambiguity and exploring multiple solutions, making them suitable for complex problem-solving and innovation. The choice between these two types depends on the specific requirements and constraints of the problem at hand. This exploration provides a comprehensive view of both, helping to appreciate their unique strengths and applications in the technological world.

Recommended Reading:

Difference Between IOT and M2M

You can refer to our guided paths on Code360. You can check our course to learn more about DSADBMSCompetitive ProgrammingPythonJavaJavaScript, etc. 

Also, check out some of the Guided Paths on topics such as Data Structure and AlgorithmsCompetitive ProgrammingOperating SystemsComputer Networks, DBMSSystem Design, etc., as well as some Contests, Test Series, and Interview Experiences curated by top Industry Experts.
 

Live masterclass