Exercise 3 solution import random rng = random.Random(3) reference = "".join(rng.choice("ACGT") for _ in range(60)) def mutate(s, n): s = list(s) for i in rng.sample(range(len(s)), n): s[i] = rng.choice([b for b in "ACGT" if b != s[i]]) return "".join(s) samples = {"s1": mutate(reference, 2), "s2": mutate(reference, 5), "s3": mutate(reference, 9)} def hamming(a, b): return sum(x != y for x, y in zip(a, b)) print(reference) for name, s in samples.items(): diffs = [i for i, (x, y) in enumerate(zip(reference, s)) if x != y] print(name, hamming(reference, s), round(1 - hamming(reference, s) / len(reference), 3), diffs) names = list(samples) for a in names: print(a, [hamming(samples[a], samples[b]) for b in names]) Output: CCGTAATGCCTTTCCCTAACAGAGTTTTTCGAACTCGTGTTGTCGAGCGACGGAATTAGA s1 2 0.967 [26, 57] s2 5 0.917 [18, 26, 27, 49, 55] s3 9 0.85 [2, 15, 17, 21, 24, 32, 35, 37, 45] s1 [0, 6, 11] s2 [6, 0, 14] s3 [11, 14, 0] Reading it: the seed makes the invented reference and the mutated samples identical each run. Each sample differs from the reference at as many positions as it was given changes, and the identity falls as the changes rise: 0.967, 0.917, 0.85. The table shows the pairwise distances between samples; s1 and s2 are 6 apart, not 7, because both changed position 26 (they each changed it to a different base, so they still disagree there, but it is one position rather than two). Distances between samples are at most the sum of their distances from the reference.