Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 64 additions & 75 deletions src/main/java/com/thealgorithms/backtracking/SudokuSolver.java
Original file line number Diff line number Diff line change
@@ -1,61 +1,49 @@
package com.thealgorithms.backtracking;

/**
* Sudoku Solver using Backtracking Algorithm
* Solves a 9x9 Sudoku puzzle by filling empty cells with valid digits (1-9)
* SudokuSolver implements the backtracking algorithm to solve a standard 9Γ—9 Sudoku puzzle.
* The puzzle is represented as a 2D array where 0 indicates an empty cell.
*
* @author Navadeep0007
* <p>Algorithm:
* - Iterates through empty cells and tries numbers 1-9
* - Validates placement against row, column, and 3Γ—3 subgrid constraints
* - Backtracks if no valid number is found
* - Returns true if a solution exists
*
* <p>Wikipedia: https://en.wikipedia.org/wiki/Sudoku_solving_algorithms
*
* @author TheAlgorithms
*/
public final class SudokuSolver {

private static final int GRID_SIZE = 9;
private static final int SIZE = 9;
private static final int SUBGRID_SIZE = 3;
private static final int EMPTY_CELL = 0;
private static final int EMPTY = 0;

private SudokuSolver() {
// Utility class, prevent instantiation
// Utility class - prevent instantiation
}

/**
* Solves the Sudoku puzzle using backtracking
* Solves the given Sudoku board using backtracking. Modifies the board
* in-place.
*
* @param board 9x9 Sudoku board with 0 representing empty cells
* @return true if puzzle is solved, false otherwise
* @param board 9Γ—9 2D array representing the Sudoku board (0 = empty cell)
* @return true if a valid solution exists, false otherwise
*/
public static boolean solveSudoku(int[][] board) {
if (board == null || board.length != GRID_SIZE) {
return false;
}
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
if (board[row][col] == EMPTY) {
for (int num = 1; num <= SIZE; num++) {
if (isValid(board, row, col, num)) {
board[row][col] = num;

for (int row = 0; row < GRID_SIZE; row++) {
if (board[row].length != GRID_SIZE) {
return false;
}
}

return solve(board);
}

/**
* Recursive helper method to solve the Sudoku puzzle
*
* @param board the Sudoku board
* @return true if solution is found, false otherwise
*/
private static boolean solve(int[][] board) {
for (int row = 0; row < GRID_SIZE; row++) {
for (int col = 0; col < GRID_SIZE; col++) {
if (board[row][col] == EMPTY_CELL) {
for (int number = 1; number <= GRID_SIZE; number++) {
if (isValidPlacement(board, row, col, number)) {
board[row][col] = number;

if (solve(board)) {
if (solveSudoku(board)) {
return true;
}

// Backtrack
board[row][col] = EMPTY_CELL;
board[row][col] = EMPTY;
}
}
return false;
Expand All @@ -66,86 +54,87 @@ private static boolean solve(int[][] board) {
}

/**
* Checks if placing a number at given position is valid
* Checks if placing a number at a given position is valid.
*
* @param board the Sudoku board
* @param row row index
* @param col column index
* @param number number to place (1-9)
* @return true if placement is valid, false otherwise
* @param num number to place (1-9)
* @return true if placement is valid
*/
private static boolean isValidPlacement(int[][] board, int row, int col, int number) {
return !isNumberInRow(board, row, number) && !isNumberInColumn(board, col, number) && !isNumberInSubgrid(board, row, col, number);
private static boolean isValid(int[][] board, int row, int col, int num) {
return isRowValid(board, row, num) && isColumnValid(board, col, num)
&& isSubgridValid(board, row, col, num);
}

/**
* Checks if number exists in the given row
* Checks if a number already exists in the given row.
*
* @param board the Sudoku board
* @param row row index
* @param number number to check
* @return true if number exists in row, false otherwise
* @param num number to check
* @return true if number is not in row
*/
private static boolean isNumberInRow(int[][] board, int row, int number) {
for (int col = 0; col < GRID_SIZE; col++) {
if (board[row][col] == number) {
return true;
private static boolean isRowValid(int[][] board, int row, int num) {
for (int col = 0; col < SIZE; col++) {
if (board[row][col] == num) {
return false;
}
}
return false;
return true;
}

/**
* Checks if number exists in the given column
* Checks if a number already exists in the given column.
*
* @param board the Sudoku board
* @param col column index
* @param number number to check
* @return true if number exists in column, false otherwise
* @param num number to check
* @return true if number is not in column
*/
private static boolean isNumberInColumn(int[][] board, int col, int number) {
for (int row = 0; row < GRID_SIZE; row++) {
if (board[row][col] == number) {
return true;
private static boolean isColumnValid(int[][] board, int col, int num) {
for (int row = 0; row < SIZE; row++) {
if (board[row][col] == num) {
return false;
}
}
return false;
return true;
}

/**
* Checks if number exists in the 3x3 subgrid
* Checks if a number already exists in the 3Γ—3 subgrid.
*
* @param board the Sudoku board
* @param row row index
* @param col column index
* @param number number to check
* @return true if number exists in subgrid, false otherwise
* @param num number to check
* @return true if number is not in subgrid
*/
private static boolean isNumberInSubgrid(int[][] board, int row, int col, int number) {
int subgridRowStart = row - row % SUBGRID_SIZE;
int subgridColStart = col - col % SUBGRID_SIZE;
private static boolean isSubgridValid(int[][] board, int row, int col, int num) {
int subgridRow = row - row % SUBGRID_SIZE;
int subgridCol = col - col % SUBGRID_SIZE;

for (int i = subgridRowStart; i < subgridRowStart + SUBGRID_SIZE; i++) {
for (int j = subgridColStart; j < subgridColStart + SUBGRID_SIZE; j++) {
if (board[i][j] == number) {
return true;
for (int i = subgridRow; i < subgridRow + SUBGRID_SIZE; i++) {
for (int j = subgridCol; j < subgridCol + SUBGRID_SIZE; j++) {
if (board[i][j] == num) {
return false;
}
}
}
return false;
return true;
}

/**
* Prints the Sudoku board
* Prints the Sudoku board in a readable format.
*
* @param board the Sudoku board
* @param board the Sudoku board to print
*/
public static void printBoard(int[][] board) {
for (int row = 0; row < GRID_SIZE; row++) {
for (int row = 0; row < SIZE; row++) {
if (row % SUBGRID_SIZE == 0 && row != 0) {
System.out.println("-----------");
}
for (int col = 0; col < GRID_SIZE; col++) {
for (int col = 0; col < SIZE; col++) {
if (col % SUBGRID_SIZE == 0 && col != 0) {
System.out.print("|");
}
Expand All @@ -154,4 +143,4 @@ public static void printBoard(int[][] board) {
System.out.println();
}
}
}
}
110 changes: 110 additions & 0 deletions src/main/java/com/thealgorithms/ciphers/ElGamalCipher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.thealgorithms.ciphers;

import java.math.BigInteger;
import java.security.SecureRandom;

/**
* Simple ElGamal implementation for educational purposes.
*
* <p>Note: This implementation is intended for educational / illustrative use in
* algorithm libraries. Real-world cryptographic usage requires careful review,
* authenticated encryption, parameter validation, constant-time implementations,
* and using well-reviewed libraries.
*
* <p>References:
* https://en.wikipedia.org/wiki/ElGamal_encryption
*
* Author: TheAlgorithms
*/
public final class ElGamalCipher {

private static final SecureRandom RANDOM = new SecureRandom();

private ElGamalCipher() {
// Utility class
}

/** Public key container. */
public static final class PublicKey {
public final BigInteger p; // prime modulus
public final BigInteger g; // generator
public final BigInteger h; // h = g^x mod p

public PublicKey(final BigInteger p, final BigInteger g, final BigInteger h) {
this.p = p;
this.g = g;
this.h = h;
}
}

/** Private key container. */
public static final class PrivateKey {
public final BigInteger p;
public final BigInteger x; // secret exponent

public PrivateKey(final BigInteger p, final BigInteger x) {
this.p = p;
this.x = x;
}
}

/** Ciphertext pair (c1, c2). */
public static final class CipherText {
public final BigInteger c1;
public final BigInteger c2;

public CipherText(final BigInteger c1, final BigInteger c2) {
this.c1 = c1;
this.c2 = c2;
}
}

/**
* Generates an ElGamal keypair.
*
* @param bitLength size of prime modulus (e.g., 2048)
* @return an array where [0]=PublicKey and [1]=PrivateKey
*/
public static Object[] generateKeyPair(final int bitLength) {
final BigInteger p = BigInteger.probablePrime(bitLength, RANDOM);
// find a generator g in [2, p-2] (this simple approach picks a candidate; for
// production use proper safe-prime/generator selection)
final BigInteger g = BigInteger.valueOf(2);
final BigInteger x = new BigInteger(bitLength - 2, RANDOM).mod(p.subtract(BigInteger.TWO)).add(BigInteger.ONE);
final BigInteger h = g.modPow(x, p);
final PublicKey pub = new PublicKey(p, g, h);
final PrivateKey priv = new PrivateKey(p, x);
return new Object[] {pub, priv};
}

/**
* Encrypts a message m (0 < m < p).
*
* @param m message as BigInteger (must be less than p)
* @param pub the public key
* @return ciphertext pair
*/
public static CipherText encrypt(final BigInteger m, final PublicKey pub) {
if (m.compareTo(BigInteger.ZERO) <= 0 || m.compareTo(pub.p) >= 0) {
throw new IllegalArgumentException("Message out of range");
}
final BigInteger y = new BigInteger(pub.p.bitLength() - 1, RANDOM).mod(pub.p.subtract(BigInteger.TWO)).add(BigInteger.ONE);
final BigInteger c1 = pub.g.modPow(y, pub.p);
final BigInteger s = pub.h.modPow(y, pub.p); // shared secret
final BigInteger c2 = s.multiply(m).mod(pub.p);
return new CipherText(c1, c2);
}

/**
* Decrypts a ciphertext using the private key.
*
* @param ct ciphertext
* @param priv private key
* @return decrypted message as BigInteger
*/
public static BigInteger decrypt(final CipherText ct, final PrivateKey priv) {
final BigInteger s = ct.c1.modPow(priv.x, priv.p);
final BigInteger sInv = s.modInverse(priv.p);
return ct.c2.multiply(sInv).mod(priv.p);
}
}
Loading
Loading