Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License.

Thursday, June 6, 2013

Simulating ICSE marks (Hacking into the Indian Education System)


Ref: http://deedy.quora.com/Hacking-into-the-Indian-Education-System?srid=3THA&share=1

I read article above and could not resist trying to simulate the behavior. The spike problem pointed out in the article is similar to one that happens with Hash functions. If the grader grade in certain increments, e.g. 0, 2, 4, 8, 10, or 0, 5, 10, it is possible to replicate the plots.

The plots below are generated assuming there is are internal grades for some project and a number of questions with some grades per question. I also assume that the grader grades in some increments, e.g. e.g. 0, 2, 4, 8, 10, or 0, 5, 10. I generated random samples for 100,000 students for both internal and external marks. The source code is at the end. The first set of plots is without any bias, i.e. the grader was not biased to give good or bad grades. The second set of plots assumes three different types of graders, each one is either not biased (0), biased to give good marks (positive bias) or biased to give bad marks (negative bias).

Without Bias: We can evidence of spikes in almost all cases below, except the last one.







With Bias: With bias, we can still see spikes and also see skew, and bimodal distributions.






Conclusion: It is posible that ICSE graders are grading questions in some common increments and they are not necessarily rigging the grades.


Python Source Code:

With Bias:
from __future__ import division

import math
import numpy
import pylab


def marks2(n=100, internal_marks=[0, 10, 20],

           per_question_marks=[0, 2, 4, 8, 10], total=100, figname=None,
           bias1=0, bias2=0, bias3=0):

    m1 = marks(n, internal_marks, per_question_marks, total, figname, bias1)

    m2 = marks(n, internal_marks, per_question_marks, total, figname, bias2)

    m3 = marks(n, internal_marks, per_question_marks, total, figname, bias3)

    m = numpy.concatenate([m1, m2, m3])

    x = numpy.arange(total+2)

    y, _ = numpy.histogram(m, x)

    print "Mean=%s, stderr=%s min=%s max=%s median=%s" % \

        (m.mean(), m.std() / math.sqrt(len(m)), m.min(), m.max(), numpy.median(m))

    pylab.plot(x[:-1], y, 'r-o')

    pylab.grid()

    pylab.title("Internal=%s, External=%s,\nn=%s, bias1=%d, bias2=%d bias3=%d" % (str(internal_marks), str(per_question_marks), n, bias1, bias2, bias3))

    pylab.xlabel("marks")

    pylab.draw()


def marks(n=100, internal_marks=[0, 10, 20],

          per_question_marks=[0, 2, 4, 8, 10], total=100, figname=None, bias=0):

    m = numpy.zeros(n)

    for i in range(n):

        m[i] = marks_one_student(internal_marks, per_question_marks, total, bias)

    return m

def pos(n, bias):

    return numpy.max([0, numpy.min([numpy.random.randint(n) + bias, n-1])])



def marks_one_student(internal_marks, per_question_marks, total, bias):

    nquestions = (total - numpy.max(internal_marks)) // numpy.max(per_question_marks)

    assert numpy.max(internal_marks) + \

        nquestions * numpy.max(per_question_marks) == total

    marks = internal_marks[pos(len(internal_marks), bias)]

    for i in range(nquestions):

        marks += per_question_marks[pos(len(per_question_marks), bias)]


    return marks

Without Bias:

from __future__ import division



import math

import numpy

import pylab



def marks(n=100, internal_marks=[0, 10, 20],

          per_question_marks=[0, 2, 4, 8, 10], total=100, figname=None):

    m = numpy.zeros(n)

    for i in range(n):

        m[i] = marks_one_student(internal_marks, per_question_marks, total)



    print "Mean=%s, stderr=%s min=%s max=%s median=%s" % \

        (m.mean(), m.std() / math.sqrt(len(m)), m.min(), m.max(), numpy.median(m))

    x = numpy.arange(total+2)

    y, _ = numpy.histogram(m, x)



    print len(x), x

    print len(y), y



    pylab.plot(x[:-1], y, 'r-o')

    pylab.grid()

    pylab.title("Internal=%s, External=%s,\nn=%s, Mean=%.1f, Median=%.1f" % (str(internal_marks), str(per_question_marks), n, m.mean(), numpy.median(m)))

    pylab.xlabel("marks")

    pylab.draw()



    if figname:

        pylab.savefig(figname)



    return m





def marks_one_student(internal_marks, per_question_marks, total):

    nquestions = (total - numpy.max(internal_marks)) // numpy.max(per_question_marks)

    assert numpy.max(internal_marks) + \

        nquestions * numpy.max(per_question_marks) == total



    marks = internal_marks[numpy.random.randint(0, len(internal_marks))]



    for i in range(nquestions):

        marks += per_question_marks[numpy.random.randint(0, len(per_question_marks))]



    return marks


Wednesday, May 8, 2013

Min and Max of 2 numbers


Problem: Find minimum and maximum of given 2 numbers.

Solution in Python:

def max(n, m):
    """
    Author: Mayur P Srivastava
    """

    if n >= m:
        return n
    return m

def min(n, m):
    """
    Author: Mayur P Srivastava
    """

    if n < m:
        return n
    return m


Concepts Learned: Logical conditions.

Cricket net run rate


Problem: Calculate net run rate for a cricket match, given runs scored by team A (batted first), overs played by team A, runs scored by team B, overs played by team B, winning team name, whether team A was bowled out, whether team B was bowled out, total number of overs in one innings.

Solution in Python:


from __future__ import division

import math

def net_run_rate(runsA, oversA, runsB, oversB,
                 winning_team,
                 bowled_outA=False,
                 bowled_outB=False,
                 total_overs=50,
                 eps=1e-8):
    """
    Author: Mayur P Srivastava
    Reference: http://en.wikipedia.org/wiki/Net_run_rate
    """

    assert winning_team in ['AB', 'A', 'B']

    if winning_team == 'AB':
        return 0.0, 0.0

    oversA = parse_overs(oversA)
    oversB = parse_overs(oversB)

    if abs(oversA - total_overs) > eps:
        bowled_outA = True

    if abs(oversB - total_overs) > eps and winning_team == 'A':
        bowled_outB = True

    if bowled_outA:
        oversA = total_overs
    if bowled_outB:
        oversB = total_overs

    rrA = runsA / oversA
    rrB = runsB / oversB
    
    if winning_team == 'A':
        nrrA = rrA - rrB
        nrrB = -nrrA
    else:
        nrrB = rrB - rrA
        nrrA = -nrrB

    return (nrrA, nrrB), (runsA, runsB), (oversA, oversB)


def parse_overs(o):
    completed_overs = math.floor(o)

    balls = math.floor(0.5 + 10 * (o - completed_overs))
    assert balls >= 0 and balls < 6
    
    return completed_overs + balls / 6.0



Concepts Learned: Maths


Cricket run rate


Problem: Compute current run rate and required run rate for a cricket game. Given: current score, target score, number of overs bowled, total number of overs.

Solution in Python:


from __future__ import division

import math

def calculate_run_rates(current_score, target_score, current_overs, total_overs):
    """

    Author: Mayur P Srivastava

    In overs, fraction part represents number of balls,
    e.g. 5.1, 5.2, 5.3, 5.4, 5.5, 6.0
    """

    current_overs = parse_overs(current_overs)
    total_overs   = parse_overs(total_overs)

    if current_overs > 0:
        current_rr = current_score / current_overs
    else:
        current_rr = 0

    remaining_overs = total_overs - current_overs
    runs_to_win     = target_score - current_score + 1

    required_rr = runs_to_win / remaining_overs


def parse_overs(o):
    completed_overs = math.floor(o)

    balls = math.floor(0.5 + 10 * (o - completed_overs))
    assert balls >= 0 and balls < 6
    
    return completed_overs + balls / 6.0

Concepts Learned: Maths

Matrix Addition


Problem: Add the given 2 matrixes.

Solution in Python:


def add(A, B):
    """
    Author: Mayur P Srivastava
    """

    m1, n1 = shape(A)
    m2, n2 = shape(B)

    if not can_add(m1, n1, m2, n2):
        return None

    C = create_matrix(m1, n2)

    for i in range(m1):
        for j in range(n2):
            C[i][j] = A[i][j] + B[i][j]

    return C

def shape(A):
    m = len(A)   
    n = 0
    for row in A:
        n2 = len(row)
        if n == 0:
            n = n2
        elif n != n2:
            assert False

    return m, n

def can_add(m1, n1, m2, n2):
    return m1 == m2 and n1 == n2

def create_matrix(m, n, value=0):
    matrix = []
    for i in range(m):
        row = []
        for j in range(n):
            row.append(value)
        matrix.append(row)
    return matrix



Concepts Learned: Nested loops and Maths

Matrix Multiplication


Problem: Multiply the given 2 matrixes.

Solution in Python:

def multiply(A, B):
    """
    Author: Mayur P Srivastava
    """


    m1, n1 = shape(A)
    m2, n2 = shape(B)

    if not can_multiply(m1, n1, m2, n2):
        return None

    C = create_matrix(m1, n2)

    for i in range(m1):
        for j in range(n2):
            c = 0
            for k in range(n1):
                c += A[i][k] * B[k][j]
            C[i][j] = c

    return C

def shape(A):
    m = len(A)   
    n = 0
    for row in A:
        n2 = len(row)
        if n == 0:
            n = n2
        elif n != n2:
            assert False

    return m, n

def can_multiply(m1, n1, m2, n2):
    return n1 == m2

def create_matrix(m, n, value=0):
    matrix = []
    for i in range(m):
        row = []
        for j in range(n):
            row.append(value)
        matrix.append(row)
    return matrix

Concepts Learned: Maths

Divisibility


Problem: Check whether a given number n is divisible by another number m.

Solution in Python:

def is_divisible(n, m):
    """
    Author: Mayur P Srivastava
    """


    if m == 0:
        return False

    return n % m == 0

Concepts Learned: Maths

Midpoint


Problem: Compute the mid-point of two 2-dimensional points.

Solution in Python:

def compute_midpoint(x1, y1, x2, y2):
    """
    Author: Mayur P Srivastava
    """

    return (x1 + x2) / 2.0, (y1 + y2) / 2.0

Concepts Learned: Maths

Check whether matrix multiplication is possible


Problem: Given dimensions of 2 matrixes, check whether they can be multiplied.

Solution in Python:

def can_multiply(m1, n1, m2, n2):
    """
    Author: Mayur P Srivastava
    """

    return n1 == m2

Concepts Learned: Maths.

Triangle


Problem: Check whether given three 2-dimensional points form a triangle.


Solution in Python:

from __future__ import division

def check_triangle(x1, y1, x2, y2, x3, y3, eps=1e-16):
    """
    Author: Mayur P Srivastava
    """

    dx12 = x1 - x2
    dy12 = y1 - y2
    dx23 = x2 - x3
    dy23 = y2 - y3

    if dx12 == 0 and dx23 == 0:
        return False
    if abs(dx12) < eps and abs(dx23) < eps:
        return False
    if dx12 == 0 or abs(dx12) < eps:
        return True
    if dx23 == 0 or abs(dx23) < eps:
        return True

    slope12 = dy12 / dx12
    slope23 = dy23 / dx23

    return not (abs(slope12 - slope23) < eps)

Concepts Learned: Maths.

Sum of digits


Problem: Compute the sum of digits for the given number as a single digit number.

Example:

sum_of_digits(5674) = 4 (5+6+7+4=22, 2+2=4)


Solution in Python:

def sum_of_digits(n):

    """
    Author: Mayur P Srivastava
    """


    while n > 9:
        n = sum_of_digits2(n)
    return n


def sum_of_digits2(n):
    sum = 0

    while n > 0:
        sum += n % 10
        n = n // 10

    return sum

Concepts Learned: Loops and numbers.

Euclidean Distance


Problem: Compute the Euclidean distance between two 2-dimensional points.

Solution in Python:

import math

def compute_euclidean_distance(x1, y1, x2, y2):
    """
    Author: Mayur P Srivastava
    """

    dx = x2 - x1
    dy = y2 - y1
    return math.sqrt(dx*dx + dy*dy)


Concepts Learned: Numbers.


Quadratic Equation


Problem: Compute the values of x in the given quadratic equations ax^2+ bx + c for given a, b, c.


Solution in Python:



from __future__ import division

import math

def solve(a, b, c, eps=1e-12):
    """
    Author: Mayur P Srivastava
    """

    if abs(a) < eps:
        return None, None

    discriminant = b * b - 4 * a * c
    if discriminant < 0:
        return None, None

    return (-b + math.sqrt(discriminant)) / (2*a), (-b - math.sqrt(discriminant)) / (2*a)



Concepts Learned: Numbers.

Tuesday, May 7, 2013

Reverse the given string.


Problem: Reverse the given string.

Solution in Python:


def reverse(s):

    """
    Author: Mayur P Srivastava
    """


    arr = list(s)    
    n   = len(arr)

    for i in range(n // 2):
        arr[i], arr[n-i-1] = arr[n-i-1], arr[i]

    return "".join(arr)


Concepts Learned: Single loop.

Capitalize first letter of each word in a sentence.


Problem: Capitalize first letter of each word in a sentence.

Example:


capitalize_words('This is a sample sentence')
This Is A Sample Sentence



Solution in Python:


def capitalize_words(sentence):
    """
    Author: Mayur P Srivastava
    """

    arr = list(sentence)

    start_of_word = False

    for i in range(len(arr)):
        c = arr[i]
        if c == ' ' or c == '\t' or c == '\n':
            start_of_word = True
        else:
            if start_of_word:
                ascii = ord(c)
                start_of_word = False
                if ascii >= 97 and ascii <= 122:
                    arr[i] = chr(ascii - 32)

    return "".join(arr)


Concepts Learned: Single loop.

Binary fraction to Decimal


Problem: Convert the given binary fraction to decimal.

Example:


binary_to_decimal_fraction(".1")
0.5

binary_to_decimal_fraction("0.0001100110011001")
0.0999908447265625

binary_to_decimal_fraction("0.01")
0.25

binary_to_decimal_fraction("0.0101100110011001")
0.3499908447265625


Solution in Python:

from __future__ import division

def binary_to_decimal_fraction(binary_str):
    """
    Author: Mayur P Srivastava
    """

    if binary_str[0] == '.':
        start_pos = 1
    elif binary_str[0] == '0' and binary_str[1] == '.':
        start_pos = 2
    else:
        assert False

    div     = 1
    decimal = 0

    for pos in range(start_pos, len(binary_str)):
        c = int(binary_str[pos])
        div *= 2

        decimal += c / div

    return decimal


Concepts Learned: Single loop, number system.

Decimal fraction to Binary


Problem: Convert the given fractional decimal to binary.

Example:


decimal_to_binary_fraction(.0)
0

decimal_to_binary_fraction(.1)
0.0001100110011001

decimal_to_binary_fraction(.25)
0.01

decimal_to_binary_fraction(.35)
0.0101100110011001



Solution in Python:


import math

def decimal_to_binary_fraction(n, max_digits=16, eps=1e-12):

    """
    Author: Mayur P Srivastava
    """

    assert n >= 0 and n < 1 

    if n < eps:
        return "0"

    binary_str = "0."
    num_digits = 0

    while num_digits < max_digits and abs(n) > eps:
        n = n * 2
        if n >= 1:
            binary_str += "1"
            n -= 1
        else:
            binary_str += "0"

        num_digits += 1

    return binary_str

Concepts Learned: Single loops, number system

Hexadecimal to Decimal


Problem: Convert the given hexadecimal string into decimal.

Example:


hex_to_decimal('198F')
6543

hex_to_decimal('19AF')
6575


Solution in Python:

def hex_to_decimal(hex_str):
    """
    Author: Mayur P Srivastava
    """

    decimal_number = 0

    n = len(hex_str)

    for i in range(n - 1, -1, -1):
        c = ord(hex_str[i])
        if c >= 65 and c <= 70:
            c = 10 + c - 65
        elif c >= 97 and c <= 102:
            c = 10 + c - 97
        elif c >= 48 and c <= 57:
            c = c - 48
        else:
            assert False

        decimal_number += 16**(n-i-1) * c

    return decimal_number


Concepts Learned: Single loop, number system.

Decimal to Hexadecimal


Problem: Convert the given decimal whole number to hexadecimal.

Example:


decimal_to_hex(32)
20

decimal_to_hex(31)
1F

decimal_to_hex(6543)
198F


Solution in Python:

def decimal_to_hex(n):
    """
    Author: Mayur P Srivastava
    """

    if n == 0:
        return "0";

    hex_str = "";

    while n > 0:
        c = n % 16
        n = n // 16
        if c > 9:
            c = chr(65 + c - 10)
        else:
            c = str(c)
        hex_str = c + hex_str

    return hex_str


Concepts Learned: Single loops, number systems

Palindrome Number


Problem: Check whether a whole number is palindrome or not.


Solution in Python:


import math

def check_for_palindrome_number(n):
    """
    Author: Mayur P Srivastava
    """

    assert n >= 0

    n_digits = int(math.floor(math.log10(n)))

    upper_mult = 10 ** n_digits
    lower_mult = 10

    while lower_mult <= upper_mult:
        lower = (n % lower_mult) // (lower_mult // 10)
        upper = (n // upper_mult) % 10

        if lower != upper:
            return False

        lower_mult *= 10
        upper_mult //=10

    return True





Concepts Learned: Single Loops, numbers.