A comprehensive, manually-coded approach to solving high-level statistical computing problems.
In C programming language, control of program execution is achieved through conditional and iterative constructs such as if, if-else, switch, while, and for.
At the core of these constructs lie Relational and Logical operators. These operators evaluate expressions and return truth values (0 or 1), thereby determining the flow of execution in structured programs.
From an examination perspective, understanding these operators is essential because they:
Relational operators are used to compare two operands. They evaluate the relationship between values and return:
| Operator | Meaning | Example Expression |
|---|---|---|
< | Less than | a < b |
> | Greater than | a > b |
<= | Less than or equal to | a <= b |
>= | Greater than or equal to | a >= b |
== | Equal to | a == b |
!= | Not equal to | a != b |
Example:
int a = 10, b = 20;
int result = (a < b);
Since 10 is less than 20, the expression evaluates to result = 1. Thus, relational operators produce integer outputs suitable for conditional evaluation.
Logical operators are used to combine two or more relational expressions. They are primarily used when multiple conditions must be tested simultaneously.
| Operator | Meaning | Symbol |
|---|---|---|
| Logical AND | True if both conditions true | && |
| Logical OR | True if at least one true | || |
| Logical NOT | Reverses truth value | ! |
AND (&&) | ||
|---|---|---|
| A | B | A && B |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
OR (||) | ||
|---|---|---|
| A | B | A || B |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
NOT (!) | |
|---|---|
| A | !A |
| 0 | 1 |
| 1 | 0 |
C language implements short-circuit evaluation:
&&): If first condition is false, second is not evaluated.||): If first condition is true, second is not evaluated.Example:
if (x != 0 && y/x > 2)
If x = 0, the second condition is never evaluated. This prevents a division-by-zero error. Thus, logical operators improve both safety and efficiency.
| Operator | Precedence Level | Associativity |
|---|---|---|
! | Highest | Right to Left |
Relational (<, >, <=, >=) | Medium | Left to Right |
==, != | Lower than relational | Left to Right |
&& | Lower | Left to Right |
|| | Lowest | Left to Right |
Understanding precedence is crucial to avoid logical errors.
= instead of ==&, |) with logical operators (&&, ||)Example of common mistake:
if (a = b) /* Incorrect: Assignment instead of comparison */
Correct form:
if (a == b)
int age = 25;
int salary = 50000;
if (age > 18 && salary > 30000)
printf("Eligible");
Both relational and logical operators work together to implement decision-making logic.
Relational and Logical operators:
They are fundamental in:
| Aspect | Key Insight | Exam Focus Area |
|---|---|---|
| Relational Operators | Compare two values | Always write return value (0/1) |
| Logical Operators | Combine conditions | Draw truth table for full marks |
| Short-Circuiting | Stops unnecessary evaluation | Mention safety advantage |
| Precedence | ! > Relational > == > && > || | Write order explicitly |
| Common Mistakes | = vs == confusion | Examiner deducts marks here |
| Application | Used in control statements | Give one practical example |
For a grouped frequency distribution, the median is calculated using the interpolation formula:
#include <stdio.h>
int main() {
int n, i;
float L, N=0, CF=0, f, h;
float freq[50], cum[50], lower[50];
printf("Enter number of classes: ");
scanf("%d", &n);
for(i=0; i<n; i++){
printf("Enter lower limit and frequency: ");
scanf("%f %f", &lower[i], &freq[i]);
N += freq[i];
}
cum[0] = freq[0];
for(i=1; i<n; i++)
cum[i] = cum[i-1] + freq[i];
for(i=0; i<n; i++){
if(cum[i] >= N/2){
L = lower[i];
f = freq[i];
if(i != 0) CF = cum[i-1];
break;
}
}
printf("Enter class width: ");
scanf("%f", &h);
float median = L + ((N/2 - CF) / f) * h;
printf("Median = %.2f", median);
return 0;
}
Coefficient of Variation (CV) measures relative dispersion and is defined as:
#include <stdio.h>
#include <math.h>
int main(){
int n, i;
float x[100], sum=0, mean, sd=0;
printf("Enter number of observations: ");
scanf("%d", &n);
for(i=0; i<n; i++){
scanf("%f", &x[i]);
sum += x[i];
}
mean = sum/n;
for(i=0; i<n; i++)
sd += pow(x[i] - mean, 2);
sd = sqrt(sd/n);
float cv = (sd/mean) * 100;
printf("Coefficient of Variation = %.2f%%", cv);
return 0;
}
Suppose that $X$ is binomial with parameters $(n, p)$. The Binomial probability mass function is given by:
To avoid calculating large factorials programmatically, we utilize the following relationship between $P\{X = k + 1\}$ and $P\{X = k\}$:
# Step 1: Input data
x <- c(0, 1, 2, 3, 4, 5, 6, 7)
f <- c(7, 6, 19, 35, 30, 23, 7, 1)
n <- 7
len <- 8
# Step 2: Compute N and Mean manually
N <- 0
sum_xf <- 0
for(i in 1:len) {
N <- N + f[i]
sum_xf <- sum_xf + (x[i] * f[i])
}
mean_x <- sum_xf / N
p <- mean_x / n
q <- 1 - p # This represents (1 - p) in the formula
# Step 3: Compute Expected Frequencies via Recurrence
expected <- numeric(len)
# Base case: Calculate P(X = 0) manually
prob_0 <- 1
for(i in 1:n) {
prob_0 <- prob_0 * q
}
expected[1] <- prob_0 * N
# Apply the exact recurrence relation: P{X=k+1} = (p/(1-p)) * ((n-k)/(k+1)) * P{X=k}
for(i in 2:len) {
k <- x[i-1] # k is the previous value of X
expected[i] <- expected[i-1] * (p / q) * ((n - k) / (k + 1))
}
# Step 4: Output
cat("x\tObserved\tExpected\n")
cat("--------------------------------\n")
for(i in 1:len) {
cat(x[i], "\t", f[i], "\t\t", round(expected[i], 2), "\n")
}
Test statistic for a single mean when variance is known:
x <- c(168, 170, 185, 172, 169, 158, 170, 175)
sigma <- 5
mu0 <- 140
critical_value <- 1.96
n <- 0
sum_x <- 0
for(val in x) {
n <- n + 1
sum_x <- sum_x + val
}
xbar <- sum_x / n
Z <- (xbar - mu0) / (sigma / (n^0.5))
abs_Z <- ifelse(Z < 0, -Z, Z)
cat("Sample Mean =", xbar, "\n")
cat("Calculated Z-statistic =", Z, "\n")
if(abs_Z > critical_value) {
cat("Decision: Reject H0 since |Z| > 1.96.\n")
} else {
cat("Decision: Fail to reject H0.\n")
}
Control statements determine the flow of execution, classified into Decision Making, Looping, and Jump Statements.
if, if-else, switchfor, while, do-whilebreak, continue, goto, return#include <stdio.h>
int main() {
int t, r, i, j;
float Y[10][10], G=0, SST=0, SSTreat=0, SSBlock=0;
float T[10]={0}, B[10]={0};
printf("Enter treatments and blocks: ");
scanf("%d %d", &t, &r);
for(i=0; i<t; i++){
for(j=0; j<r; j++){
scanf("%f", &Y[i][j]);
G += Y[i][j];
T[i] += Y[i][j];
B[j] += Y[i][j];
SST += Y[i][j]*Y[i][j];
}
}
int N = t*r;
SST -= (G*G)/N;
for(i=0; i<t; i++) SSTreat += (T[i]*T[i])/r;
SSTreat -= (G*G)/N;
for(j=0; j<r; j++) SSBlock += (B[j]*B[j])/t;
SSBlock -= (G*G)/N;
float SSError = SST - SSTreat - SSBlock;
printf("ANOVA Table\n");
printf("Treatment SS = %.2f\n", SSTreat);
printf("Block SS = %.2f\n", SSBlock);
printf("Error SS = %.2f\n", SSError);
return 0;
}
#include <stdio.h>
int main() {
int n, i;
float x[100], y[100];
float sumx=0, sumy=0, sumxy=0, sumx2=0;
float a, b;
printf("Enter number of observations: ");
scanf("%d", &n);
for(i=0; i<n; i++) {
printf("Enter X and Y: ");
scanf("%f %f", &x[i], &y[i]);
sumx += x[i];
sumy += y[i];
sumxy += x[i]*y[i];
sumx2 += x[i]*x[i];
}
b = (n*sumxy - sumx*sumy) / (n*sumx2 - sumx*sumx);
a = (sumy/n) - b*(sumx/n);
printf("Regression Equation: Y = %.2f + %.2fX", a, b);
return 0;
}
Matrices are fundamental data structures used in statistical computing and numerical analysis. A matrix is a two-dimensional rectangular array of elements arranged in rows and columns. In statistical applications, matrices are widely used for solving systems of equations, linear regression models, multivariate analysis, and scientific computing.
The programming language R provides efficient tools for matrix manipulation. However, for deeper understanding of matrix algebra, it is important to implement these operations manually rather than relying on built-in functions.
In this answer, we demonstrate how to read matrices in R and perform important matrix operations such as addition, multiplication, transpose, determinant, and inverse without using built-in functions.
A matrix can be created by specifying the number of rows and columns and then reading elements from the user.
n <- as.integer(readline("Enter number of rows: "))
m <- as.integer(readline("Enter number of columns: "))
A <- matrix(0, n, m)
for(i in 1:n) {
for(j in 1:m) {
A[i,j] <- as.numeric(readline(paste("Enter element", i, j, ": ")))
}
}
print("Matrix A:")
print(A)
Matrix addition is defined only when both matrices have the same dimensions. If $A$ and $B$ are matrices of order $n \times m$, their sum is given by:
C <- matrix(0, n, m)
for(i in 1:n) {
for(j in 1:m) {
C[i,j] <- A[i,j] + B[i,j]
}
}
print("Matrix Addition Result")
print(C)
Matrix multiplication is defined when the number of columns of the first matrix equals the number of rows of the second matrix.
C <- matrix(0, n, p)
for(i in 1:n) {
for(j in 1:p) {
for(k in 1:m) {
C[i,j] <- C[i,j] + A[i,k] * B[k,j]
}
}
}
print("Matrix Multiplication Result")
print(C)
The transpose of a matrix is obtained by interchanging rows and columns. If $A$ is an $n \times m$ matrix, then its transpose $A^T$ is an $m \times n$ matrix.
T_mat <- matrix(0, m, n)
for(i in 1:n) {
for(j in 1:m) {
T_mat[j,i] <- A[i,j]
}
}
print("Transpose of Matrix")
print(T_mat)
The determinant is defined only for square matrices and is a scalar value that provides important information about the matrix. A recursive method using cofactor expansion can be used to compute the determinant.
determinant <- function(A) {
n <- nrow(A)
if(n == 1) {
return(A[1,1])
}
if(n == 2) {
return(A[1,1]*A[2,2] - A[1,2]*A[2,1])
}
det <- 0
for(k in 1:n) {
submatrix <- A[-1, -k]
det <- det + ((-1)^(1+k)) * A[1,k] * determinant(submatrix)
}
return(det)
}
The inverse of a matrix $A$ exists only if the determinant is non-zero.
Where $adj(A)$ is the adjoint matrix. The following algorithm computes the inverse using cofactors.
inverse_matrix <- function(A) {
n <- nrow(A)
detA <- determinant(A)
if(detA == 0) {
print("Inverse does not exist")
return(NULL)
}
cof <- matrix(0, n, n)
for(i in 1:n) {
for(j in 1:n) {
submatrix <- A[-i, -j]
cof[i,j] <- ((-1)^(i+j)) * determinant(submatrix)
}
}
# Manual transpose for the adjoint matrix
adj <- matrix(0, n, n)
for(i in 1:n) {
for(j in 1:n) {
adj[j,i] <- cof[i,j]
}
}
inv <- adj / detA
return(inv)
}
solve() or det().sumx2 = 0) before starting your loop to prevent garbage value accumulation.Write a C program to compute the standard deviation for grouped frequency data.
Write an R script to compute the trace of an $n \times n$ matrix manually using a loop. Also, add logic to check if the matrix is symmetric.
trace_sum = 0, then run a single loop for (i in 1:n) and add A[i,i]. For symmetry, use nested loops to check if A[i,j] != A[j,i].