I'm stuck with pset6 similarities more. When I want to insert the tuples into my matrix, I keep getting the message 'NoneType' object does not support item assignment. If I change none to 0 or to (None, None), I get the same message : int/tuple object does not support item assignment. What am I overlooking?
def distances(a, b):
"""Calculate edit distance from a to b"""
# create matrix of dimensions len(a) + 1 and len(b) + 1 and fill with zeros
matrix = [[None for i in range(len(b) + 1)] for j in range(len(a) + 1)]
matrix[0][0] = (0, None)
for x in range (1, len(a) + 1):
matrix[x][0] = (x, Operation.INSERTED)
for y in range (1, len(b) + 1):
matrix[0][y] = (y, Operation.DELETED)
for x in range(1, len(a) + 1):
for y in range(1, len(b) + 1):
if a[x - 1] == b[y - 1]:
matrix[x][y] = (min(matrix[x - 1][y - 1][0], matrix[x][y - 1][0], matrix[x - 1][y][0]), None)
else:
matrix[x][y][0] = min(matrix[x - 1][y - 1][0] + 1, matrix[x][y - 1][0] + 1, matrix[x - 1][y][0] + 1)
if matrix[x][y][0] == matrix[x - 1][y - 1][0] + 1:
matrix[x][y][1] = Operation.SUBSTITUTED
if matrix[x][y][0] == matrix[x][y - 1][0] + 1:
matrix[x][y][1] = Operation.INSERTED
else:
matrix[x][y][1] = Operation.DELETED
return matrix
The tuple object does not support item assignment error message means Python tuples can't be changed in-place, but they can be overlaid by new tuples. Try creating a new tuple and assigning it to matrix[x][y], rather than assigning the tuple elements themselves.
Related
Calculate the n member of the sequence given by the formulas
a[2 * n] = a[n] + 1
a[2 * n + 2] = a[2 * n + 1] - a[n]
a[0] = a[1] = 1
n > 0
I've tried a lot of variants, but I can't find correct one.
n = int(input())
a = [0 for i in range(n + 3)]
a[0] = a[1] = 1
i = 1
while i * 2 + 2 < n + 3:
a[2 * i] = a[i] + 1;
a[2 * i + 1] = a[2 * i + 2] + a[i]
a[2 * i + 2] = a[2 * i + 1] - a[i]
i += 1
print(a[n])
We should first compute the expected output for the first few numbers to let us have an idea what the sequence is like first,
a[0] = a[1] = 1
Substitute n = 1 in the first recurrence relation gives
a[2] = a[1] + 1 = 2
Substitute n = 1 in the second recurrence relation gives
a[4] = a[3] - a[1]
But a[4] = a[2] + 1 = 3 according to the first recurrence relation, so 3 = a[3] - 1, which gives a[3] = 4
We have a = {1, 1, 2, 4, 3, ... }
Your program gives a = {1, 1, 2, 1, 3, ...}
What went wrong in your program?
We notice that when i = 1, the line a[2 * i + 1] = a[2 * i + 2] + a[i] evaluates to a[3] = a[4] + a[1]. However, at that time, a[4] is not evaluated yet, causing an incorrect output.
The issue, therefore, lies in how you order your statements in the while loop. Make sure that statements in your loop only make use of values that will not be changed later.
How should we do that?
if we manipulate the second recurrence relation as follows:
a[2 * i + 2] = a[2 * i + 1] - a[i]
a[2 * i + 1] = a[2 * (i + 1)] + a[i]
Using the first recurrence relation, we have
a[2 * i + 1] = a[i + 1] + 1 + a[i]
which should resolve the issue since 2 * n + 1 > n + 1 for all positive n.
After modifying the second statement, you check that every element in a is computed and you should be done.
Note
One more thing to note is that the third statement is redundant since the first statement covers all even elements in a already.
In fact, a more efficient approach, in particular a logarithmic solution exist2 if you only have to calculated the nth member of the sequence.
I found decision
n = int(input())
k = n if n % 2 == 0 else n + 1
a = [None for i in range(k + 1)]
a[0] = a[1] = 1
def fill_list(a):
while None in a:
i = 1
while i * 2 <= k:
if a[i] != None:
a[2 * i] = a[i] + 1
i += 1
i = 1
while i * 2 + 2 <= k:
if a[i * 2 + 2] != None and a[i] != None:
a[i * 2 + 1] = a[i * 2 + 2] + a[i]
i += 1
fill_list(a)
print(a[n])
Your second formula gives a[2n+2] = a[2n+1] - a[n]. That can be rewritten: a[2n+1] = a[2n+2] + a[n] which is a[n+1] + a[n] + 1 from the first formula.
We can use this to write a simple dynamic programming algorithm that runs in linear time:
def A(n):
a = [1] * (n+1)
for i in range(2, n+1):
if i%2 == 0:
a[i] = a[i//2] + 1
else:
a[i] = a[i//2] + a[i//2+1] + 1
return a[n]
However, we can note that we can solve this in logarithmic time, by noting that we can compute both a[n] and a[n+1] from a[n//2] and a[n//2+1].
If n is even, then a[n]=a[n//2]+1 and a[n+1]=a[n//2]+a[n//2+1]+1.
And if n is odd, then a[n]=a[n//2]+a[n//2+1]+1 and a[n+1]=a[n//2+1]+1.
These are just applications of the formulas we have already.
This gives us this solution:
def A2(n):
if n == 0:
return 1, 1
if n == 1:
return 1, 2
a, b = A2(n//2)
if n % 2 == 0:
return a+1, a+b+1
else:
return a+b+1, b+1
Note that this returns 2 values, but for all n, A(n) == A2(n)[0].
def distances(a, b):
"""Calculate edit distance from a to b"""
# declare matrix and set top left box to 0 and none
cost = [[(0,0) for x in range(len(a)+1)] for y in range(len(b)+1)]
cost[0][0] = (0, None)
# fill in first row
for i in range(1, len(a)+1):
cost[i][0] = (i, Operation.DELETED)
#fill in first column
for j in range(1, len(b)+1):
cost[0][j] = (j, Operation.INSERTED)
# fill in rest of the table from left to right, one row at a time
i = 1
j = 1
while i < len(a) + 1:
while j < len(b) + 1:
if a[i-1] == b[j-1]:
cost[i][j] = (cost[i-1][j-1], Operation.SUBSTITUTED)
j += 1
else:
subcost = min(cost[i-1][j][0], cost[i][j-1][0], cost[i-1][j-1][0])
if subcost == cost[i-1][j][0]:
cost[i][j] = (cost[i-1][j][0] + 1, Operation.DELETED)
j += 1
elif subcost == cost[i][j-1][0]:
cost[i][j] = (cost[i][j-1][0] + 1, Operation.INSERTED)
j += 1
elif subcost == cost[i-1][j-1][0]:
cost[i][j] = (cost[i-1][j-1][0] + 1, Operation.SUBSTITUTED)
j += 1
i += 1
j = 1
return cost
Gives me this error message:
TypeError: '<' not supported between instances of 'tuple' and 'int'
and specifies
subcost = min(cost[i-1][j][0], cost[i][j-1][0], cost[i-1][j-1][0])
as the problem line
Each cost[i][j][0] should be specifying the first element of the jth tuple in the ith list of cost, which should be an int, yet it's saying they're tuples and I don't get why.
In the main method below,
1st : I've tried to pass a.copy() and b.copy() as arguments. Though the solve_linear_equations method returned a valid solution, it still tampers with the original arguments a[][] and b[].
2nd : Then, I've tried to define two distinct variables as tmp_a = a.copy() and tmp_b = b.copy(). Using these new variables as method parameters did not help either: not always, but usually, the method tampers with the original array values a[][] and b[].
I think, there is some kind of a tricky issue concerning Python internals that I couldn't realize. Can anybody give a hand?
import random
def data_create(n):
a, x, b = [], [], []
for i in range(n):
a.append([])
s = random.randint(0, 2)
x.append(random.randint(0, 1000) / 1000)
if s:
x[i] *= -1
for j in range(n):
s = random.randint(0, 2)
a[i].append(random.randint(0, 1000) / 1000)
if s:
a[i][j] *= -1
for i in range(n):
b.append(0.0)
for j in range(n):
b[i] += a[i][j] * x[j]
return a, x, b
def solve_linear_equations(n, a, x):
for i in range(n - 1):
max_row = i
max_val = abs(a[i][i])
for j in range(i + 1, n):
if abs(a[j][i]) > max_val:
max_val = abs(a[j][i])
max_row = j
if max_row != i:
x[i], x[max_row] = x[max_row], x[i]
a[i], a[max_row] = a[max_row].copy(), a[i].copy()
x[i] /= a[i][i]
for j in range(i + 1, n):
a[i][j] /= a[i][i]
a[i][i] = 1.0
for j in range(i + 1, n):
x[j] -= x[i] * a[j][i]
for k in range(i + 1, n):
a[j][k] -= a[i][k] * a[j][i]
a[j][i] = 0.0
x[n - 1] /= a[n - 1][n - 1]
a[n - 1][n - 1] = 1.0
for i in range(n - 1, 0, -1):
for j in range(i - 1, -1, -1):
x[j] -= x[i] * a[j][i]
for k in range(i, n):
a[j][k] -= a[i][k] * a[j][i]
return x
def main():
n = 3
a, x, b = data_create(n)
print("x\n", x)
print("a\n", a)
print("b\n", b, "\n")
tmp_a = a.copy() # creating a copy of a[][]
tmp_b = b.copy() # creating a copy of b[]
print("tmp_a\n", tmp_a)
print("tmp_b\n", tmp_b, "\n")
print("x\n", solve_linear_equations(n, tmp_a, tmp_b))
print("a\n", a)
print("b\n", b, "\n")
if __name__ == "__main__":
main()
Instead of
tmp_a = a.copy() # creating a copy of a[][]
tmp_b = b.copy() # creating a copy of b[]
do
import copy
...
tmp_a = copy.deepcopy(a) # creating a deep copy of a[][]
tmp_b = copy.deepcopy(b) # creating a deep copy of b[]
This is because list.copy() only goes one level down and you are seeing unwanted changes two levels down.
UPDATE:
I've been trying to calculate the number of comparisons made in a quicksort implementation (code below), but I noticed that the number of comparisons is only correctly computed when I take out the bold bit in the line below.
left_comparisons, left_list = quick_sort(unsorted_list [:i - 1], l, i - 2)
I have two questions to ask:
Why should the bold bit affect the total comparisons made?
Why does input_list[l:r + 1] sometimes have a length of 0? Shouldn't this algorithm ensure that the base case always has 1 element in it?
$
def quick_sort(unsorted_list, l, r):
if len(unsorted_list[l:r + 1]) <= 1:
return 0, unsorted_list
else:
# choose_pivot(input_list) # TODO implement properly later. cd return input_list here.
pivot = unsorted_list[l]
i = l + 1
num_comparisons = r - l
for j in xrange(l + 1, r + 1):
if unsorted_list[j] < pivot:
temp = unsorted_list[i]
unsorted_list[i] = unsorted_list[j]
unsorted_list[j] = temp
i += 1
unsorted_list[l] = unsorted_list[i - 1]
unsorted_list[i - 1] = pivot
left_comparisons, left_list = quick_sort(unsorted_list[:i - 1], l, i - 2)
right_comparisons, right_list = quick_sort(unsorted_list, i, len(unsorted_list) - 1)
return left_comparisons + num_comparisons + right_comparisons, left_list[:i - 1] + [pivot] + right_list[i:]
I'd like to align two lists in a similar way to what difflib.Differ would do except I want to be able to define a match function for comparing items, not just use string equality, and preferably a match function that can return a number between 0.0 and 1.0, not just a boolean.
So, for example, say I had the two lists:
L1 = [('A', 1), ('B', 3), ('C', 7)]
L2 = ['A', 'b', 'C']
and I want to be able to write a match function like this:
def match(item1, item2):
if item1[0] == item2:
return 1.0
elif item1[0].lower() == item2.lower():
return 0.5
else:
return 0.0
and then do:
d = Differ(match_func=match)
d.compare(L1, L2)
and have it diff using the match function. Like difflib, I'd rather the algorithm gave more intuitive Ratcliff-Obershelp type results rather than a purely minimal Levenshtein distance.
I just wrote this implementation of Needleman-Wunsch and it seems to do what I want:
def nw_align(a, b, replace_func, insert, delete):
ZERO, LEFT, UP, DIAGONAL = 0, 1, 2, 3
len_a = len(a)
len_b = len(b)
matrix = [[(0, ZERO) for x in range(len_b + 1)] for y in range(len_a + 1)]
for i in range(len_a + 1):
matrix[i][0] = (insert * i, UP)
for j in range(len_b + 1):
matrix[0][j] = (delete * j, LEFT)
for i in range(1, len_a + 1):
for j in range(1, len_b + 1):
replace = replace_func(a[i - 1], b[j - 1])
matrix[i][j] = max([
(matrix[i - 1][j - 1][0] + replace, DIAGONAL),
(matrix[i][j - 1][0] + insert, LEFT),
(matrix[i - 1][j][0] + delete, UP)
])
i, j = len_a, len_b
align_a = ""
align_b = ""
while (i, j) != (0, 0):
if matrix[i][j][1] == DIAGONAL:
align_a += a[i - 1]
align_b += b[j - 1]
i -= 1
j -= 1
elif matrix[i][j][1] == LEFT:
align_a += "-"
align_b += b[j - 1]
j -= 1
else: # UP
align_a += a[i - 1]
align_b += "-"
i -= 1
return align_a[::-1], align_b[::-1]
I recently ran across a discussion of an algorithm called patience diff that sounds rather simple. You could try implementing that yourself, and then of course you can have it use whatever comparison algorithm you like.