Given a n*n chessboard and the knight position (x, y), each time the knight is to move, it chooses one of eight possible moves uniformly at random (even if the piece would go off the chessboard) and moves there. The knight continues moving until it has made exactly k moves or has moved off the chessboard. The task is to find the probability that the knight remains on the board after it has stopped moving.
Note: A chess knight can make eight possible moves. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction.
Examples:
Input: n = 8, x = 0, y = 0, k = 1
Output: 0.25
Explanation: The knight starts at (0, 0) and after taking one step it will lie inside the board in only 2 out of 8 positions which are (1, 2) and (2, 1). Thus the probability will be 2/8 = 0.25.Input : n = 8, x = 0, y = 0, k = 3
Output: 0.125Input: n = 4, x = 1, y = 2, k = 4
Output: 0.024414
Table of Content
Using Top-Down Dp (Memoization) - O(n*n*k) Time and O(n*n*k) Space
The probability of knight to remain in the chessboard after k moves is equal to the average of probability of knight at previous eight positions after k - 1 moves. Similarly, probability after k-1 moves depends upon average of probability after k-2 moves. The idea is to use memoization to store the probabilities of previous moves and find their average to calculate the final result.
To do so, create a 3d array memo[][][], where memo[i][j][k] stores the probability of knight to be at cell (i, j) after k moves. If k is zero, i.e. the initial state is reached, return 1, else explore previous eight positions and find the average of their probabilities.
// C++ program to find the probability of the
// knight to remain inside the chessboard
#include <bits/stdc++.h>
using namespace std;
// recursive function to calculate
// knight probability
double knightProbability(int n, int x, int y, int k,
vector<vector<vector<double>>> &memo){
// Base case, initial probability
if(k == 0) return 1.0;
// check if already calculated
if(memo[x][y][k] != -1) return memo[x][y][k];
vector<vector<int>> directions = {{1, 2}, {2, 1}, {2, -1},
{1, -2}, {-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}};
memo[x][y][k] = 0;
double cur = 0.0;
// for every position reachable from (x,y)
for(auto d:directions){
int u = x + d[0];
int v = y + d[1];
// if this position lie inside the board
if (u >= 0 && u < n && v >= 0 && v < n)
cur += knightProbability(n, u, v, k-1, memo) / 8.0;
}
return memo[x][y][k] = cur;
}
// Function to find the probability
double findProb(int n, int x, int y, int k) {
// Initialize memo to store results
vector<vector<vector<double>>> memo(n,
vector<vector<double>>(n,
vector<double> (k+1, -1)));
return knightProbability(n, x, y, k, memo);
}
int main(){
int n = 8, x = 0, y = 0, k = 3;
cout << findProb(n, x, y, k) << endl;
return 0;
}
// Java program to find the probability of the
// knight to remain inside the chessboard
class GfG {
// recursive function to calculate
// knight probability
static double knightProbability(int n, int x,
int y, int k, double[][][] memo) {
// Base case, initial probability
if (k == 0) return 1.0;
// check if already calculated
if (memo[x][y][k] != -1) return memo[x][y][k];
int[][] directions = {{1, 2}, {2, 1}, {2, -1}, {1, -2},
{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}};
memo[x][y][k] = 0;
double cur = 0.0;
// for every position reachable from (x, y)
for (int[] d : directions) {
int u = x + d[0];
int v = y + d[1];
// if this position lies inside the board
if (u >= 0 && u < n && v >= 0 && v < n)
cur += knightProbability(n, u, v, k - 1, memo) / 8.0;
}
return memo[x][y][k] = cur;
}
// Function to find the probability
static double findProb(int n, int x, int y, int k) {
// Initialize memo to store results
double[][][] memo = new double[n][n][k + 1];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int m = 0; m <= k; m++) {
memo[i][j][m] = -1;
}
}
}
return knightProbability(n, x, y, k, memo);
}
public static void main(String[] args) {
int n = 8, x = 0, y = 0, k = 3;
System.out.println(findProb(n, x, y, k));
}
}
# Python program to find the probability of the
# knight to remain inside the chessboard
# recursive function to calculate
# knight probability
def knightProbability(n, x, y, k, memo):
# Base case, initial probability
if k == 0:
return 1.0
# check if already calculated
if memo[x][y][k] != -1:
return memo[x][y][k]
directions = [
[1, 2], [2, 1], [2, -1], [1, -2],
[-1, -2], [-2, -1], [-2, 1], [-1, 2]
]
memo[x][y][k] = 0
cur = 0.0
# for every position reachable from (x, y)
for d in directions:
u = x + d[0]
v = y + d[1]
# if this position lies inside the board
if 0 <= u < n and 0 <= v < n:
cur += knightProbability(n, u, v, k - 1, memo) / 8.0
memo[x][y][k] = cur
return cur
# Function to find the probability
def findProb(n, x, y, k):
# Initialize memo to store results
memo = [[[-1 for _ in range(k + 1)]
for _ in range(n)] for _ in range(n)]
return knightProbability(n, x, y, k, memo)
n, x, y, k = 8, 0, 0, 3
print(findProb(n, x, y, k))
// C# program to find the probability of the
// knight to remain inside the chessboard
using System;
class GfG {
// recursive function to calculate
// knight probability
static double KnightProbability(int n, int x,
int y, int k, double[,,] memo) {
// Base case, initial probability
if (k == 0) return 1.0;
// check if already calculated
if (memo[x, y, k] != -1) return memo[x, y, k];
int[,] directions = {{1, 2}, {2, 1}, {2, -1}, {1, -2},
{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}};
memo[x, y, k] = 0;
double cur = 0.0;
// for every position reachable from (x, y)
for (int i = 0; i < 8; i++) {
int u = x + directions[i, 0];
int v = y + directions[i, 1];
// if this position lies inside the board
if (u >= 0 && u < n && v >= 0 && v < n) {
cur += KnightProbability(n, u, v, k - 1, memo) / 8.0;
}
}
return memo[x, y, k] = cur;
}
// Function to find the probability
static double FindProb(int n, int x, int y, int k) {
// Initialize memo to store results
double[,,] memo = new double[n, n, k + 1];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int m = 0; m <= k; m++) {
memo[i, j, m] = -1;
}
}
}
return KnightProbability(n, x, y, k, memo);
}
static void Main() {
int n = 8, x = 0, y = 0, k = 3;
Console.WriteLine(FindProb(n, x, y, k));
}
}
// JavaScript program to find the probability of the
// knight to remain inside the chessboard
// recursive function to calculate
// knight probability
function knightProbability(n, x, y, k, memo) {
// Base case, initial probability
if (k === 0) return 1.0;
// check if already calculated
if (memo[x][y][k] !== -1) return memo[x][y][k];
const directions = [
[1, 2], [2, 1], [2, -1], [1, -2],
[-1, -2], [-2, -1], [-2, 1], [-1, 2]
];
memo[x][y][k] = 0;
let cur = 0.0;
// for every position reachable from (x, y)
for (let d of directions) {
const u = x + d[0];
const v = y + d[1];
// if this position lies inside the board
if (u >= 0 && u < n && v >= 0 && v < n) {
cur += knightProbability(n, u, v, k - 1, memo) / 8.0;
}
}
return memo[x][y][k] = cur;
}
// Function to find the probability
function findProb(n, x, y, k) {
// Initialize memo to store results
const memo = Array.from({ length: n }, () =>
Array.from({ length: n }, () => Array(k + 1).fill(-1)));
return knightProbability(n, x, y, k, memo).toFixed(6);
}
const n = 8, x = 0, y = 0, k = 3;
console.log(findProb(n, x, y, k));
Output
0.125
Using Bottom-Up Dp (Tabulation) - O(n*n*k) Time and O(n*n*k) Space
The above approach can be optimized using bottom-up tabulation, reducing the extra space required for recursive stack. The idea is to maintain a 3D array dp[][][], where dp[i][j][k] stores the probability of knight to be at cell (i, j) after k moves. Initialize the 0th state of dp with value 1. For each subsequent move, the probability of knight will be equal to average of probability of previous 8 positions after k-1 moves.
// C++ program to find the probability of the
// knight to remain inside the chessboard
#include <bits/stdc++.h>
using namespace std;
// Function to find the probability
double findProb(int n, int x, int y, int k) {
// Initialize dp to store results of each step
vector<vector<vector<double>>> dp(n,
vector<vector<double>>(n,
vector<double> (k+1)));
// Initialize dp for step 0
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
dp[i][j][0] = 1.0;
}
}
vector<vector<int>> directions = {
{1, 2}, {2, 1}, {2, -1}, {1, -2},
{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}
};
for (int move = 1; move <= k; move++) {
// find probability for cell (i, j)
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
double cur = 0.0;
// for every position reachable from (x,y)
for (auto d:directions) {
int u = i + d[0];
int v = j + d[1];
// if this position lie inside the board
if (u >= 0 && u < n && v >= 0 && v < n)
cur += dp[u][v][move - 1] / 8.0;
}
// store the result
dp[i][j][move] = cur;
}
}
}
// return the result
return dp[x][y][k];
}
int main(){
int n = 8, x = 0, y = 0, k = 3;
cout << findProb(n, x, y, k) << endl;
return 0;
}
// Java program to find the probability of the
// knight to remain inside the chessboard
import java.util.*;
class GfG {
// Function to find the probability
static double findProb(int n, int x, int y, int k) {
// Initialize dp to store results of each step
double[][][] dp = new double[n][n][k + 1];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dp[i][j][0] = 1;
}
}
int[][] directions = {
{1, 2}, {2, 1}, {2, -1}, {1, -2},
{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}
};
for (int move = 1; move <= k; move++) {
// find probability for cell (i, j)
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
double cur = 0.0;
// for every position reachable from (x, y)
for (int[] d : directions) {
int u = i + d[0];
int v = j + d[1];
// if this position lies inside the board
if (u >= 0 && u < n && v >= 0 && v < n) {
cur += dp[u][v][move - 1] / 8.0;
}
}
// store the result
dp[i][j][move] = cur;
}
}
}
// return the result
return dp[x][y][k];
}
public static void main(String[] args) {
int n = 8, x = 0, y = 0, k = 3;
System.out.println(findProb(n, x, y, k));
}
}
# Python program to find the probability of the
# knight to remain inside the chessboard
# Function to find the probability
def findProb(n, x, y, k):
# Initialize dp to store results of each step
dp = [[[0 for _ in range(k + 1)]
for _ in range(n)] for _ in range(n)]
for i in range(n):
for j in range(n):
dp[i][j][0] = 1.0
directions = [[1, 2], [2, 1], [2, -1], [1, -2],
[-1, -2], [-2, -1], [-2, 1], [-1, 2]]
for move in range(1, k + 1):
# find probability for cell (i, j)
for i in range(n):
for j in range(n):
cur = 0.0
# for every position reachable from (x, y)
for d in directions:
u = i + d[0]
v = j + d[1]
# if this position lies inside the board
if 0 <= u < n and 0 <= v < n:
cur += dp[u][v][move - 1] / 8.0
# store the result
dp[i][j][move] = cur
# return the result
return dp[x][y][k]
if __name__ == "__main__":
n, x, y, k = 8, 0, 0, 3
print(findProb(n, x, y, k))
// C# program to find the probability of the
// knight to remain inside the chessboard
using System;
class GfG {
// Function to find the probability
static double findProb(int n, int x, int y, int k) {
// Initialize dp to store results of each step
double[,,] dp = new double[n, n, k + 1];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dp[i, j, 0] = 1.0;
}
}
int[,] directions = {{1, 2}, {2, 1}, {2, -1}, {1, -2},
{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}};
for (int move = 1; move <= k; move++) {
// find probability for cell (i, j)
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
double cur = 0.0;
// for every position reachable from (x, y)
for (int d = 0; d < directions.GetLength(0); d++) {
int u = i + directions[d, 0];
int v = j + directions[d, 1];
// if this position lies inside the board
if (u >= 0 && u < n && v >= 0 && v < n) {
cur += dp[u, v, move - 1] / 8.0;
}
}
// store the result
dp[i, j, move] = cur;
}
}
}
// return the result
return dp[x, y, k];
}
static void Main(string[] args) {
int n = 8, x = 0, y = 0, k = 3;
Console.WriteLine(findProb(n, x, y, k));
}
}
// JavaScript program to find the probability of the
// knight to remain inside the chessboard
// Function to find the probability
function findProb(n, x, y, k) {
// Initialize dp to store results of each step
let dp = Array.from({ length: n }, () =>
Array.from({ length: n }, () => Array(k + 1).fill(0))
);
// Initialize dp for step 0
for (let i = 0; i < n; ++i) {
for (let j = 0; j < n; ++j) {
dp[i][j][0] = 1.0;
}
}
let directions = [[1, 2], [2, 1], [2, -1], [1, -2],
[-1, -2], [-2, -1], [-2, 1], [-1, 2]];
for (let move = 1; move <= k; move++) {
// find probability for cell (i, j)
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
let cur = 0.0;
// for every position reachable from (x, y)
for (let d of directions) {
let u = i + d[0];
let v = j + d[1];
// if this position lies inside the board
if (u >= 0 && u < n && v >= 0 && v < n) {
cur += dp[u][v][move - 1] / 8.0;
}
}
// store the result
dp[i][j][move] = cur;
}
}
}
// return the result
return dp[x][y][k].toFixed(6);
}
let n = 8, x = 0, y = 0, k = 3;
console.log(findProb(n, x, y, k));
Output
0.125
Using Space Optimized Dp - O(n*n*k) Time and O(n*n) Space
The above approach requires only previous state of probabilities to calculate the current state, thus only the previous store need to be stored. The idea is to create two 2d arrays prevMove[][] and currMove[][], where
- prevMove[i][j] stores the probability of knight to be at (i, j) up to previous move. It is initialized with value 1 for initial state.
- currMove[i][j] stores the probability of current state.
Operate similar to the above approach and at end of each iteration update prevMove[][] with value stored in currMove[][].
// C++ program to find the probability of the
// knight to remain inside the chessboard
#include <bits/stdc++.h>
using namespace std;
// Function to find the probability
double findProb(int n, int x, int y, int k) {
// dp to store results of previous move
vector<vector<double>> prevMove(n, vector<double>(n, 1));
// dp to store results of current move
vector<vector<double>> currMove(n, vector<double>(n, 0));
vector<vector<int>> directions = {
{1, 2}, {2, 1}, {2, -1}, {1, -2},
{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}
};
for (int move = 1; move <= k; move++) {
// find probability for cell (i, j)
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
double cur = 0.0;
// for every position reachable from (x,y)
for (auto d:directions) {
int u = i + d[0];
int v = j + d[1];
// if this position lie inside the board
if (u >= 0 && u < n && v >= 0 && v < n)
cur += prevMove[u][v] / 8.0;
}
// store the result
currMove[i][j] = cur;
}
}
// update previous state
prevMove = currMove;
}
// return the result
return prevMove[x][y];
}
int main(){
int n = 8, x = 0, y = 0, k = 3;
cout << findProb(n, x, y, k) << endl;
return 0;
}
// Java program to find the probability of the
// knight to remain inside the chessboard
class GfG {
// Function to find the probability
static double findProb(int n, int x, int y, int k) {
// dp to store results of previous move
double[][] prevMove = new double[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
prevMove[i][j] = 1.0;
}
}
// dp to store results of current move
double[][] currMove = new double[n][n];
int[][] directions = {
{1, 2}, {2, 1}, {2, -1}, {1, -2},
{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}
};
for (int move = 1; move <= k; move++) {
// find probability for cell (i, j)
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
double cur = 0.0;
// for every position reachable from (x,y)
for (int[] d : directions) {
int u = i + d[0];
int v = j + d[1];
// if this position lies inside the board
if (u >= 0 && u < n && v >= 0 && v < n)
cur += prevMove[u][v] / 8.0;
}
// store the result
currMove[i][j] = cur;
}
}
// update previous state
for (int i = 0; i < n; i++) {
System.arraycopy(currMove[i], 0, prevMove[i], 0, n);
}
}
// return the result
return prevMove[x][y];
}
public static void main(String[] args) {
int n = 8, x = 0, y = 0, k = 3;
System.out.println(findProb(n, x, y, k));
}
}
# Python program to find the probability of the
# knight to remain inside the chessboard
def findProb(n, x, y, k):
# dp to store results of previous move
prevMove = [[1.0] * n for _ in range(n)]
# dp to store results of current move
currMove = [[0.0] * n for _ in range(n)]
directions = [
[1, 2], [2, 1], [2, -1], [1, -2],
[-1, -2], [-2, -1], [-2, 1], [-1, 2]
]
for move in range(1, k + 1):
# find probability for cell (i, j)
for i in range(n):
for j in range(n):
cur = 0.0
# for every position reachable from (x,y)
for d in directions:
u, v = i + d[0], j + d[1]
# if this position lies inside the board
if 0 <= u < n and 0 <= v < n:
cur += prevMove[u][v] / 8.0
# store the result
currMove[i][j] = cur
# update previous state
prevMove = [row[:] for row in currMove]
# return the result
return prevMove[x][y]
if __name__ == "__main__":
n, x, y, k = 8, 0, 0, 3
print(findProb(n, x, y, k))
// C# program to find the probability of the
// knight to remain inside the chessboard
using System;
class GfG {
// Function to find the probability
static double findProb(int n, int x, int y, int k) {
// dp to store results of previous move
double[,] prevMove = new double[n, n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
prevMove[i, j] = 1.0;
// dp to store results of current move
double[,] currMove = new double[n, n];
int[,] directions = {
{1, 2}, {2, 1}, {2, -1}, {1, -2},
{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2}
};
for (int move = 1; move <= k; move++) {
// find probability for cell (i, j)
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
double cur = 0.0;
// for every position reachable from (x,y)
for (int d = 0; d < directions.GetLength(0); d++) {
int u = i + directions[d, 0];
int v = j + directions[d, 1];
// if this position lies inside the board
if (u >= 0 && u < n && v >= 0 && v < n)
cur += prevMove[u, v] / 8.0;
}
// store the result
currMove[i, j] = cur;
}
}
// update previous state
Array.Copy(currMove, prevMove, n * n);
}
// return the result
return prevMove[x, y];
}
static void Main() {
int n = 8, x = 0, y = 0, k = 3;
Console.WriteLine(findProb(n, x, y, k));
}
}
// JavaScript program to find the probability of the
// knight to remain inside the chessboard
function findProb(n, x, y, k) {
// dp to store results of previous move
let prevMove = Array.from({ length: n },
() => Array(n).fill(1.0));
// dp to store results of current move
let currMove = Array.from({ length: n },
() => Array(n).fill(0.0));
const directions = [
[1, 2], [2, 1], [2, -1], [1, -2],
[-1, -2], [-2, -1], [-2, 1], [-1, 2]
];
for (let move = 1; move <= k; move++) {
// find probability for cell (i, j)
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
let cur = 0.0;
// for every position reachable from (x,y)
for (let d of directions) {
let u = i + d[0];
let v = j + d[1];
// if this position lies inside the board
if (u >= 0 && u < n && v >= 0 && v < n)
cur += prevMove[u][v] / 8.0;
}
// store the result
currMove[i][j] = cur;
}
}
// update previous state
prevMove = currMove.map(row => [...row]);
}
// return the result
return prevMove[x][y].toFixed(6);
}
let n = 8, x = 0, y = 0, k = 3;
console.log(findProb(n, x, y, k));
Output
0.125