§ Linearity of expectation for sampling


# process 1
def val(c): return 1 + ord(c) - 'a'
def process(addx, addy):
    s = 0
    ndraws = 10
    for _ in range(ndraws):
        x = random.choice("abcde"),  # draw a random chit
        y = random.choice(x*5+"abcde") # draw a random chit, dependent on first random chit.
        if addx: s += val(x)
        if addy: x += val(y)
    return s






§ Linearity of expectation is purity


Suppose we write:
x = random.choice("abcde")
y = random.choice("abcde")
s =  val(x) + val(y)


E[s] = E[val(x) + val(y)]
= E[val(x)] + E[val(y)]
= 2 E[val(x)]


§ "Deriving" equivalence for two processses using purity



def rhsI():
    sx = 0; sy = 0
    ndraws = 10
    for _ in range(ndraws):
        x = random.choice("abcde"),  # draw a random chit
        y = random.choice(x1*5+"abcde") # draw a random chit, dependent on first random chit.
        sx += val(x)

    for _ in range(ndraws):
        x = random.choice("abcde"),  # draw a random chit
        y = random.choice(x2*5+"abcde") # draw a random chit, dependent on first random chit.
        sy += val(y)
    return sx + sy


def rhsII():
    sx = 0; sy = 0
    ndraws = 10

    # loop fusion is safe, because even though random.choice has a side effect, the order
    # of calling random.choice does not matter. It commutes with other random ops.
    for _ in range(ndraws):
        x1 = random.choice("abcde"),  # draw a random chit
        y1 = random.choice(x1*5+"abcde") # draw a random chit, dependent on first random chit.
        sx += val(x1)
        # loop fusion
        x2 = random.choice("abcde"),  # draw a random chit
        y2 = random.choice(x2*5+"abcde") # draw a random chit, dependent on first random chit.
        sy += val(y2)
    return sx + sy


def rhsIII():
    sx = 0; sy = 0
    ndraws = 10

    # once again, expectation purifies randomness. So within the context of expecattion, we can
    # replace `x2` with `x1` with `x1`
    for _ in range(ndraws):
        x1 = random.choice("abcde"),  # draw a random chit
        y1 = random.choice(x1*5+"abcde") # draw a random chit, dependent on first random chit.
        sx += val(x1)
        # loop fusion
        x2 = x1
        y2 = y1
        sy += val(y2)
    return sx + sy


def rhsIV():
    sx = 0; sy = 0
    ndraws = 10

    # once again, expectation purifies randomness. So within the context of expecattion, we can
    # replace `x2` with `x1` with `x1`
    for _ in range(ndraws):
        x1 = random.choice("abcde"),  # draw a random chit
        y1 = random.choice(x1*5+"abcde") # draw a random chit, dependent on first random chit.
        sx += val(x1)
        sy += val(y1)
    return sx + sy

§ For N=1N=1, the expected number of turns is 11.