Why are elements of my array being over written? - python-3.x

I have written a simple function in Python which aims to find, if from two elements a and b, one can be obtained from another by swapping at most one pair of elements in one of the arrays.
This is my function:
def areSimilar(a, b):
test = 0
for i in range(len(b)):
for j in range(len(b)):
b2 = b
b2[i] = b[j]
b2[j] = b[i]
if a == b2:
test = 1
return(test==1)
The issue is that upon inspecting b, it has changed even though I don't actually perform any calculations on b - what's going on!!??

(EDITED: to better address the second point)
There are two issues with your code:
When you do b2 = b this just creates another reference to the underlying object. If b is mutable, any change made to b2 will be reflected in b too.
When a single swapping suffices there is no need to test further, but if you keep on looping the test will be successful again with i and j swapped, so test condition is hit either never or (at least -- depending on the amount of duplicates) twice. While this would not lead to incorrect results, it would normally be regarded as an error in the logic.
To fix your code, you could just create a copy of b. Assuming that by Python arrays you actually mean Python lists one way of doing it would be to create a new list every time by replacing b2 = b with b2 = list(b). A more efficient approach is to perform the swapping on b itself (and swap back):
def are_similar(a, b):
for i in range(len(b)):
for j in range(len(b)):
b[i], b[j] = b[j], b[i]
if a == b:
b[i], b[j] = b[j], b[i] # swap back
return True
else:
b[i], b[j] = b[j], b[i] # swap back
return False
print(are_similar([1, 1, 2, 3], [1, 2, 1, 3]))
# True
print(are_similar([1, 1, 2, 3], [3, 2, 1, 1]))
# False
By contrast, you can see how inefficient (while correct) the copying-based approach is:
def are_similar2(a, b):
for i in range(len(b)):
for j in range(len(b)):
b2 = list(b)
b2[i] = b[j]
b2[j] = b[i]
if a == b2:
return True
return False
print(are_similar2([1, 1, 2, 3], [1, 2, 1, 3]))
# True
print(are_similar2([1, 1, 2, 3], [3, 2, 1, 1]))
# False
with much worse timings, even on relatively small inputs:
a = [1, 1, 2, 3] + list(range(100))
b = [1, 2, 1, 3] + list(range(100))
%timeit are_similar(a, b)
# 10000 loops, best of 3: 22.9 µs per loop
%timeit are_similar2(a, b)
# 10000 loops, best of 3: 73.9 µs per loop

I would got with Sadap's code, but if you want to copy, use :
import copy
def areSimilar(a, b):
test = 0
for i in range(len(b)):
for j in range(len(b)):
b2 = copy.deepcopy(b)
b2[i] = copy.deepcopy(b[j])
b2[j] = copy.deepcopy(b[i])
if a == b2:
test = 1
if test == 1:
return True
else:
return False

Related

How to check a list of lists against a list of lists and count their overlaps? (Python)

I have one list a containing 100 lists and one list x containing 4 lists (all of equal length). I want to test the lists in a against those in x. My goal is to find out how often numbers in a "touch" those in x. Stated differently, all the lists are points on a line and the lines in a should not touch (or cross) those in x.
EDIT
In the code, I am testing each line in a (e.g. a1, a2 ... a100) first against x1, then against x2, x3 and x4. A condition and a counter check whether the a's touch the x's. Note: I am not interested in counting how many items in a1, for example, touch x1. Once a1 and x1 touch, I count that and can move on to a2, and so on.
However, the counter does not properly update. It seems that it does not tests a against all x. Any suggestions on how to solve this? Here is my code.
EDIT
I have updated the code so that the problem is easier to replicate.
x = [[10, 11, 12], [14, 15, 16]]
a = [[11, 10, 12], [15, 17, 20], [11, 14, 16]]
def touch(e, f):
e = np.array(e)
f = np.array(f)
lastitems = []
counter = 0
for lst in f:
if np.all(e < lst): # This is the condition
lastitems.append(lst[-1]) # This allows checking the end values
else:
counter += 1
c = counter
return c
touch = touch(x, a)
print(touch)
The result I get is:
2
But I expect this:
1
2
I'm unsure of what exactly is the result you expect, your example and description are still not clear. Anyway, this is what I guess you want. If you want more details, you can uncomment some lines i.e. those with #
i = 0
for j in x:
print("")
#print(j)
counter = 0
for k in a:
inters = set(j).intersection(k)
#print(k)
#print(inters)
if inters:
counter += 1
#print("yes", counter)
#else:
#print("nope", counter)
print(i, counter)
i += 1
which prints
0 2
1 2

Printing fibonacci series using lists in python

I am relatively a noobie in programming and trying to learn python. I was trying to implement a Fibonacci series into a list within 10.
fibo= [0,1]
for k in range(11):
i= fibo[-1]
j = fibo[-2]
k= fibo[i]+fibo[j]
fibo.append(k)
k=+1
print(fibo)
Not sure what I did wrong? Any help is really appreciated!
Output:
[0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
You can use this code to print fibonacci series upto N.
N = int(input()) # Length of fibonacci series
fibo = [0, 1]
a = fibo[0]
b = fibo[1]
for each in range(2, N):
c = a + b
a, b = b, c
fibo.append(c)
print(fibo[:N])
OUTPUT
N = 10
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
I can see few issues here:
fibo = [0,1]
# Your loop should start at index 2, otherwise
# you will try to access fibo[-2] and fibo[-1] with k=0
# and fibo[-1] and fibo[0] with k=1
# Also, if you only want 10 values, you should change 11
# to 10 since fibo already has two values.
# for k in range(11):
for k in range(2, 10):
# You don't want the content of fibo, but the indexes!
# i = fibo[-1]
# j = fibo[-2]
i = k - 1
j = k - 2
# You are already using k, so you need to use an other variable
# k = fibo[i] + fibo[j]
v = fibo[i] + fibo[j]
fibo.append(v)
# You don't need k+=1 because it will be incremented
# according to range(2, 10). The loop will start with
# k = 2 and it will stop when k = 9
# k += 1
print(fibo)
Your code did not crash because you technically can access fibo[-1] and fibo[-2]. It will respectively return the last value of your array, and the value before the last one.

Polynomials and dictionaries

i have this exercise about polynomials and dictionaries which i did (see below) but i am sure there is a better and easier way to solve it (for question 2 and 3). can anyone show me another way to approach question 2 or 3? Thanks.
here is the exercice:
In this exercise we want to work with polynomials of any degree. Each polynomial can be represented by a dictionary, whose keys correspond to the powers of x, and the values ​​to the coefficients. For example, to represent the polynomial x ^ 6 + 3 * x ^ 2, we can use the dictionary: {6: 1, 2: 3}
1. Write a function evaluer(p, x) that takes a polynomial p and a number x into arguments, and returns the value of polynomial at point x.
Execution example:
evaluer({3: 1, 1: 2, 0: -1}, 2)
OUT: 11
2. Write a function somme_polynomes(p1, p2) which takes two polynomials (dictionaries) into arguments and which
returns a new dictionary representing the sum of the two polynomials p1 and p2.
Execution example:
somme_polynomes ({3: 1, 2: 1, 0: 1}, {4: 2, 2: 3})
OUT: {0: 1, 2: 4, 3: 1, 4: 2}
3. Write a function produit_polynomes(p1, p2) that takes two polynomials as arguments and returns the product of two polynomials in a new dictionary.
Execution example:
produit_polynomes ({3: 1, 2: 1, 0: 1}, {4: 2, 2: 3})
OUT: {2: 3, 4: 5, 5: 3, 6: 2, 7: 2}
here is what i did:
# 1)
def evaluer(p,x):
c = 0
for key,value in p.items():
c += value*(x**key)
return c
# 2)
def somme_polynomes(p1,p2):
p3 = {}
for key,value in p1.items():
for k,v in p2.items():
p3.update({key:value})
p3.update({k:v})
for key in p1:
if key in p2:
add = p1[key]+p2[key]
p3.update({key:add})
if add == 0:
del p3[key]
return p3
# 3)
def produit_polynomes(p1,p2):
p3 = {}
for key,value in p1.items():
for k,v in p2.items():
if key+k in p3:
p3[key+k] += value*v
else:
p3.update({key+k:value*v})
return p3
Your code is fine, here are alternative ways of doing it using more of Python's language (generator expression, dict comprehension) and library (itertools, collections):
def evaluer(p, x):
return sum(v * x**k for k, v in p.items())
def somme_polynomes(p1, p2):
return {k: p1.get(k, 0) + p2.get(k, 0) for k in p1.keys() | p2.keys()}
import itertools as it
from collection import defaultdict
def produit_polynomes(p1, p2):
p3 = defaultdict(int)
for k1, k2 in it.product(p1, p2):
p3[k1+k2] += p1[k1]*p2[k2]
return dict(p3)
If you want to avoid importing any modules then produit_polnomes() could be written without the conditional as:
def produit_polynomes(p1,p2):
p3 = {}
for k1, v1 in p1.items():
for k2, v2 in p2.items():
p3[k1+k2] = p3.get(k1+k2, 0) + v1*v2
return p3
Exercise 2 can be done in a more Pythonic way by using set union for keys and dict comprehension for sums:
def somme_polynomes(p1, p2):
return {p: p1.get(p, 0) + p2.get(p, 0) for p in p1.keys() | p2.keys()}
Exercise 3 on the other hand is best done using nested loops aggregating products to the sum of keys, which is what you are already doing. The only slight enhancement I would make is to use the setdefault method to avoid the if statement:
def produit_polynomes(p1,p2):
p3 = {}
for key,value in p1.items():
for k,v in p2.items():
p3[key + k] = p3.get(key + k, 0) + value * v
return p3

Quicksort partitioning infinite loop

arr = [7, 3, 5, 6, 7, 1, 8, 0, 4, 9, 6, 2]
def partitioning(arr, l, d):
pivot = 5
while l <= d:
while arr[l] < pivot:
l += 1
while arr[d] > pivot:
d -= 1
arr[l], arr[d] = arr[d], arr[l]
partitioning(arr, 0, len(arr) - 1)
print(arr)
I don't understand why when putting l <= d, when l and d become the same they stop moving and keep swapping to infinity?
Your infinite loop happens when l == d and arr[l] == arr[d] == pivot. In this situation, the inner loops never do anything and the swap also doesn't do anything since the two indexes are the same (so you're swapping the pivot with itself).
You want your top loop to quit in this situation, since the array has been completely partitioned. You should change the <= on the outer loop to <, and it should work.
You'll need to change the pivot-choice logic going forward (since if the pivot is not in the list, your inner loops might run l or d off the end of the list), but I assume that picking the constant 5 is just a preliminary thing.

Randomizing two lists and maintaining order in Python 3.4

I'm basically asking the exact same question as was asked here, but for Python 3.4.0.
In 3.4.0, this code:
a = ["Spears", "Adele", "NDubz", "Nicole", "Cristina"]
b = [1, 2, 3, 4, 5]
combined = zip(a, b)
random.shuffle(combined)
a[:], b[:] = zip(*combined)
does not work. What is the correct way to do this in 3.4.0?
In python 3, zip returns a zip object (i.e. it's itertools.izip from python 2).
You need to force it to materialize the list:
combined = list(zip(a, b))
If memory was tight, you can write your own shuffle function to avoid the need to create the zipped list. The one from Python is not very complicated
def shuffle(self, x, random=None, int=int):
"""x, random=random.random -> shuffle list x in place; return None.
Optional arg random is a 0-argument function returning a random
float in [0.0, 1.0); by default, the standard random.random.
Do not supply the 'int' argument.
"""
randbelow = self._randbelow
for i in reversed(range(1, len(args[0]))):
# pick an element in x[:i+1] with which to exchange x[i]
j = randbelow(i+1) if random is None else int(random() * (i+1))
x[i], x[j] = x[j], x[i]
Your function could be this:
def shuffle2(a, b):
for i in reversed(range(1, len(a))):
j = int(random.random() * (i+1))
a[i], a[j] = a[j], a[i]
b[i], b[j] = b[j], b[i]
To shuffle an arbitrary number of lists in unison
def shuffle_many(*args):
for i in reversed(range(1, len(args[0]))):
j = int(random.random() * (i+1))
for x in args:
x[i], x[j] = x[j], x[i]
eg
>>> import random
>>> def shuffle_many(*args):
... for i in reversed(range(1, len(args[0]))):
... j = int(random.random() * (i+1))
... for x in args:
... x[i], x[j] = x[j], x[i]
...
>>> a = ["Spears", "Adele", "NDubz", "Nicole", "Cristina"]
>>> b = [1, 2, 3, 4, 5]
>>> shuffle_many(a, b)
>>> a
['Adele', 'Spears', 'Nicole', 'NDubz', 'Cristina']
>>> b
[2, 1, 4, 3, 5]
Change combined = zip(a,b) to combined = list(zip(a,b)). You need a list, not an iterator, in order to shuffle in place.
In Python 3, zip returns an iterator rather than a list, so cast it to a list before shuffling it:
combined = list(zip(a, b))

Resources