Related
In Python, I've seen two variable values swapped using this syntax:
left, right = right, left
Is this considered the standard way to swap two variable values or is there some other means by which two variables are by convention most usually swapped?
Python evaluates expressions from left to right. Notice that while
evaluating an assignment, the right-hand side is evaluated before the
left-hand side.
Python docs: Evaluation order
That means the following for the expression a,b = b,a :
The right-hand side b,a is evaluated, that is to say, a tuple of two elements is created in the memory. The two elements are the objects designated by the identifiers b and a, that were existing before the instruction is encountered during the execution of the program.
Just after the creation of this tuple, no assignment of this tuple object has still been made, but it doesn't matter, Python internally knows where it is.
Then, the left-hand side is evaluated, that is to say, the tuple is assigned to the left-hand side.
As the left-hand side is composed of two identifiers, the tuple is unpacked in order that the first identifier a be assigned to the first element of the tuple (which is the object that was formerly b before the swap because it had name b)
and the second identifier b is assigned to the second element of the tuple (which is the object that was formerly a before the swap because its identifiers was a)
This mechanism has effectively swapped the objects assigned to the identifiers a and b
So, to answer your question: YES, it's the standard way to swap two identifiers on two objects.
By the way, the objects are not variables, they are objects.
That is the standard way to swap two variables, yes.
I know three ways to swap variables, but a, b = b, a is the simplest. There is
XOR (for integers)
x = x ^ y
y = y ^ x
x = x ^ y
Or concisely,
x ^= y
y ^= x
x ^= y
Temporary variable
w = x
x = y
y = w
del w
Tuple swap
x, y = y, x
I would not say it is a standard way to swap because it will cause some unexpected errors.
nums[i], nums[nums[i] - 1] = nums[nums[i] - 1], nums[i]
nums[i] will be modified first and then affect the second variable nums[nums[i] - 1].
Does not work for multidimensional arrays, because references are used here.
import numpy as np
# swaps
data = np.random.random(2)
print(data)
data[0], data[1] = data[1], data[0]
print(data)
# does not swap
data = np.random.random((2, 2))
print(data)
data[0], data[1] = data[1], data[0]
print(data)
See also Swap slices of Numpy arrays
To get around the problems explained by eyquem, you could use the copy module to return a tuple containing (reversed) copies of the values, via a function:
from copy import copy
def swapper(x, y):
return (copy(y), copy(x))
Same function as a lambda:
swapper = lambda x, y: (copy(y), copy(x))
Then, assign those to the desired names, like this:
x, y = swapper(y, x)
NOTE: if you wanted to you could import/use deepcopy instead of copy.
That syntax is a standard way to swap variables. However, we need to be careful of the order when dealing with elements that are modified and then used in subsequent storage elements of the swap.
Using arrays with a direct index is fine. For example:
def swap_indexes(A, i1, i2):
A[i1], A[i2] = A[i2], A[i1]
print('A[i1]=', A[i1], 'A[i2]=', A[i2])
return A
A = [0, 1, 2, 3, 4]
print('For A=', A)
print('swap indexes 1, 3:', swap_indexes(A, 1, 3))
Gives us:
('For A=', [0, 1, 2, 3, 4])
('A[i1]=', 3, 'A[i2]=', 1)
('swap indexes 1, 3:', [0, 3, 2, 1, 4])
However, if we change the left first element and use it in the left second element as an index, this causes a bad swap.
def good_swap(P, i2):
j = P[i2]
#Below is correct, because P[i2] is modified after it is used in P[P[i2]]
print('Before: P[i2]=', P[i2], 'P[P[i2]]=', P[j])
P[P[i2]], P[i2] = P[i2], P[P[i2]]
print('Good swap: After P[i2]=', P[i2], 'P[P[i2]]=', P[j])
return P
def bad_swap(P, i2):
j = P[i2]
#Below is wrong, because P[i2] is modified and then used in P[P[i2]]
print('Before: P[i2]=', P[i2], 'P[P[i2]]=', P[j])
P[i2], P[P[i2]] = P[P[i2]], P[i2]
print('Bad swap: After P[i2]=', P[i2], 'P[P[i2]]=', P[j])
return P
P = [1, 2, 3, 4, 5]
print('For P=', P)
print('good swap with index 2:', good_swap(P, 2))
print('------')
P = [1, 2, 3, 4, 5]
print('bad swap with index 2:', bad_swap(P, 2))
('For P=', [1, 2, 3, 4, 5])
('Before: P[i2]=', 3, 'P[P[i2]]=', 4)
('Good swap: After P[i2]=', 4, 'P[P[i2]]=', 3)
('good swap with index 2:', [1, 2, 4, 3, 5])
('Before: P[i2]=', 3, 'P[P[i2]]=', 4)
('Bad swap: After P[i2]=', 4, 'P[P[i2]]=', 4)
('bad swap with index 2:', [1, 2, 4, 4, 3])
The bad swap is incorrect because P[i2] is 3 and we expect P[P[i2]] to be P[3]. However, P[i2] is changed to 4 first, so the subsequent P[P[i2]] becomes P[4], which overwrites the 4th element rather than the 3rd element.
The above scenario is used in permutations. A simpler good swap and bad swap would be:
#good swap:
P[j], j = j, P[j]
#bad swap:
j, P[j] = P[j], j
You can combine tuple and XOR swaps: x, y = x ^ x ^ y, x ^ y ^ y
x, y = 10, 20
print('Before swapping: x = %s, y = %s '%(x,y))
x, y = x ^ x ^ y, x ^ y ^ y
print('After swapping: x = %s, y = %s '%(x,y))
or
x, y = 10, 20
print('Before swapping: x = %s, y = %s '%(x,y))
print('After swapping: x = %s, y = %s '%(x ^ x ^ y, x ^ y ^ y))
Using lambda:
x, y = 10, 20
print('Before swapping: x = %s, y = %s' % (x, y))
swapper = lambda x, y : ((x ^ x ^ y), (x ^ y ^ y))
print('After swapping: x = %s, y = %s ' % swapper(x, y))
Output:
Before swapping: x = 10 , y = 20
After swapping: x = 20 , y = 10
I have two functions that compute the same metric. One ends up using a list comprehension to cycle through a calculation, the other uses only numpy tensor operations. The functions take in a (N, 3) array, where N is the number of points in 3D space. When N <~ 3000 the tensor function is faster, when N >~ 3000 the list comprehension is faster. Both seem to have linear time complexity in terms of N i.e two time-N lines cross at N=~3000.
def approximate_area_loop(section, num_area_divisions):
n_a_d = num_area_divisions
interp_vectors = get_section_interp_(section)
a1 = section[:-1]
b1 = section[1:]
a2 = interp_vectors[:-1]
b2 = interp_vectors[1:]
c = lambda u: (1 - u) * a1 + u * a2
d = lambda u: (1 - u) * b1 + u * b2
x = lambda u, v: (1 - v) * c(u) + v * d(u)
area = np.sum([np.linalg.norm(np.cross((x((i + 1)/n_a_d, j/n_a_d) - x(i/n_a_d, j/n_a_d)),\
(x(i/n_a_d, (j +1)/n_a_d) - x(i/n_a_d, j/n_a_d))), axis = 1)\
for i in range(n_a_d) for j in range(n_a_d)])
Dt = section[-1, 0] - section[0, 0]
return area, Dt
def approximate_area_tensor(section, num_area_divisions):
divisors = np.linspace(0, 1, num_area_divisions + 1)
interp_vectors = get_section_interp_(section)
a1 = section[:-1]
b1 = section[1:]
a2 = interp_vectors[:-1]
b2 = interp_vectors[1:]
c = np.multiply.outer(a1, (1 - divisors)) + np.multiply.outer(a2, divisors) # c_areas_vecs_divs
d = np.multiply.outer(b1, (1 - divisors)) + np.multiply.outer(b2, divisors) # d_areas_vecs_divs
x = np.multiply.outer(c, (1 - divisors)) + np.multiply.outer(d, divisors) # x_areas_vecs_Divs_divs
u = x[:, :, 1:, :-1] - x[:, :, :-1, :-1] # u_areas_vecs_Divs_divs
v = x[:, :, :-1, 1:] - x[:, :, :-1, :-1] # v_areas_vecs_Divs_divs
sub_area_norm_vecs = np.cross(u, v, axis = 1) # areas_crosses_Divs_divs
sub_areas = np.linalg.norm(sub_area_norm_vecs, axis = 1) # areas_Divs_divs (values are now sub areas)
area = np.sum(sub_areas)
Dt = section[-1, 0] - section[0, 0]
return area, Dt
Why does the list comprehension version work faster at large N? Surely the tensor version should be faster? I'm wondering if it's something to do with the size of the calculations meaning it's too big to be done in cache? Please ask if I haven't included enough information, I'd really like to get to the bottom of this.
The bottleneck in the fully vectorized function was indeed in the np.linalg.norm as #hpauljs comment suggested.
Norm was used only to get the magnitude of all the vectors contained in axis 1. A much simpler and faster method was to just:
sub_areas = np.sqrt((sub_area_norm_vecs*sub_area_norm_vecs).sum(axis = 1))
This gives exactly the same results and sped up the code by up to 25 times faster than the loop implementation (even when the loop doesn't use linalg.norm either).
I have a tree as shown below:
I need to find leaves such that they have the same ancestor. For an example, in the above tree, we have two nodes with the same ancestor. Can someone suggest me a way to do that?
With the help of those two answers, I tried the following to find the pair of leaves with the common parent and then I need to join those two leaves and update the tree. But, that did not give me the correct pair of leaves and did not update the tree correctly. Can you please find the mistake here and help me with that?
def common_parent(T, n1, n2):
for n1 in N:
for n2 in N:
if T.neighbors(n1) == T.neighbors(n2):
return (n1, n2)
nodes_pairs = []
for n1 in N:
for n2 in N:
if n1 != n2 and common_parent(T, n1,n2):
nodes_pairs.append(common_ancestor(T, n1,n2))
print(nodes_pairs)
for n1 in N:
for n2 in N:
if T.neighbors(n1) == T.neighbors(n2):
T.add_edge(n1, n2, weight='distance')
print(T.edges())
Can be done like that:
import networkx as nx
import matplotlib.pyplot as plt
from collections import defaultdict
G = nx.Graph()
## setup, borrowed from https://necromuralist.github.io/data_science/posts/distance-in-social-networks/
left = tuple("AAKBCCFEDEIE")
right = tuple("KBBCFEGFEIJH")
G.add_edges_from(list(zip(left, right)))
##
# retrieve nodes of degree=1
k1corona = list(nx.k_corona(G, 1))
# and their parents
nodes = { node: list(G[node])[0] for _set in k1corona for node in _set }
# counting leaves for each parent
parents = defaultdict(int)
for node,parent in nodes.items():
parents[parent]+=1
# filtering out loners
leaves = [ node for node,parent in nodes.items() if parents[parent]>=2 ]
# drawing
pos = nx.spring_layout(G,random_state=0)
nx.draw_networkx(G, pos=pos, with_labels=True)
nx.draw_networkx_nodes(G.subgraph(leaves), pos=pos, with_labels=True, node_color='blue')
plt.show()
Find nodes with a common ancestor
create tree
G = nx.balanced_tree(2, 2, create_using=None)
plotting
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos)
nx.draw_networkx_edges(G,pos)
nx.draw_networkx_labels(G,pos, font_color='w')
plt.axis('off')
plt.show()
def common_ancestor(G, n1, n2):
if nx.shortest_path_length(G, n1, n2) == 2:
return (n1, n2)
nodes_pairs = []
for n1 in G.nodes():
for n2 in G.nodes():
if n1 != n2 and common_ancestor(G, n1,n2):
nodes_pairs.append(common_ancestor(G, n1,n2))
nodes_pairs
[out]:
[(0, 3),
(0, 4),
(0, 5),
(0, 6),
(1, 2),
(2, 1),
(3, 0),
(3, 4),
(4, 0),
(4, 3),
(5, 0),
(5, 6),
(6, 0),
(6, 5)]
I want to add and multiply two polynomials. A function takes two arguments like add([(4,3),(3,0)],[(-4,3),(2,1)]).So, the polynomial looks like
4x^3 + 3 and -4x^3 + 2x
I want to add and multiply both these two polynomials without using any library.
I have created a simplified version for both addition and multiplication by creating a blank list that can store the coefficients from constant terms to the co-eff of highest exponents. The logic is simply to update the coefficients and creating a list containing tuple pairs of the format (co-eff, exponent)
def add(p1,p2):
x = [0]*(max(p1[0][1],p2[0][1])+1)
for i in p1+p2:
x[i[1]]+=i[0]
res = [(x[i],i) for i in range(len(x)) if x[i]!=0]
res.sort(key = lambda r: r[1], reverse= True)
return res
def mul(p1,p2):
x = [0]*(p1[0][1]*p2[0][1]+1)
for i in p1:
for j in p2:
x[i[1]+j[1]]+=i[0]*j[0]
res = [(x[i],i) for i in range(len(x)) if x[i]!=0]
res.sort(key = lambda r: r[1], reverse= True)
return res
pls note that this code works only for non negative exponents
addition and multiplication of the polynomials you referred in the question yields the following results
add([(4,3),(3,0)],[(-4,3),(2,1)]) = [(2, 1), (3, 0)]
mul([(4,3),(3,0)],[(-4,3),(2,1)]) = [(-16, 6), (8, 4), (-12, 3), (6, 1)]
For addition I have written a method
def poly_add( x, y):
r = []
min_len = min( len(x), len(y))
for i in range(min_len):
if x[i][1] == y[i][1]:
m = x[i][0] + y[i][0]
if m != 0:
r.append((m, x[i][1]))
if x[i][1] != y[i][1]:
r.append((y[i]))
r.append((x[i]))
return r
I am trying to sort 4 integers input by the user into numerical order using only the min() and max() functions in python. I can get the highest and lowest number easily, but cannot work out a combination to order the two middle numbers? Does anyone have an idea?
So I'm guessing your input is something like this?
string = input('Type your numbers, separated by a space')
Then I'd do:
numbers = [int(i) for i in string.strip().split(' ')]
amount_of_numbers = len(numbers)
sorted = []
for i in range(amount_of_numbers):
x = max(numbers)
numbers.remove(x)
sorted.append(x)
print(sorted)
This will sort them using max, but min can also be used.
If you didn't have to use min and max:
string = input('Type your numbers, separated by a space')
numbers = [int(i) for i in string.strip().split(' ')]
numbers.sort() #an optional reverse argument possible
print(numbers)
LITERALLY just min and max? Odd, but, why not. I'm about to crash, but I think the following would work:
# Easy
arr[0] = max(a,b,c,d)
# Take the smallest element from each pair.
#
# You will never take the largest element from the set, but since one of the
# pairs will be (largest, second_largest) you will at some point take the
# second largest. Take the maximum value of the selected items - which
# will be the maximum of the items ignoring the largest value.
arr[1] = max(min(a,b)
min(a,c)
min(a,d)
min(b,c)
min(b,d)
min(c,d))
# Similar logic, but reversed, to take the smallest of the largest of each
# pair - again omitting the smallest number, then taking the smallest.
arr[2] = min(max(a,b)
max(a,c)
max(a,d)
max(b,c)
max(b,d)
max(c,d))
# Easy
arr[3] = min(a,b,c,d)
For Tankerbuzz's result for the following:
first_integer = 9
second_integer = 19
third_integer = 1
fourth_integer = 15
I get 1, 15, 9, 19 as the ascending values.
The following is one of the forms that gives symbolic form of the ascending values (using i1-i4 instead of first_integer, etc...):
Min(i1, i2, i3, i4)
Max(Min(i4, Max(Min(i1, i2), Min(i3, Max(i1, i2))), Max(i1, i2, i3)), Min(i1, i2, i3, Max(i1, i2)))
Max(Min(i1, i2), Min(i3, Max(i1, i2)), Min(i4, Max(i1, i2, i3)))
Max(i1, i2, i3, i4)
It was generated by a 'bubble sort' using the Min and Max functions of SymPy (a python CAS):
def minmaxsort(v):
"""return a sorted list of the elements in v using the
Min and Max functions.
Examples
========
>>> minmaxsort(3, 2, 1)
[1, 2, 3]
>>> minmaxsort(1, x, y)
[Min(1, x, y), Max(Min(1, x), Min(y, Max(1, x))), Max(1, x, y)]
>>> minmaxsort(1, y, x)
[Min(1, x, y), Max(Min(1, y), Min(x, Max(1, y))), Max(1, x, y)]
"""
from sympy import Min, Max
v = list(v)
v0 = Min(*v)
for j in range(len(v)):
for i in range(len(v) - j - 1):
w = v[i:i + 2]
v[i:i + 2] = [Min(*w), Max(*w)]
v[0] = v0
return v
I have worked it out.
min_integer = min(first_integer, second_integer, third_integer, fourth_integer)
mid_low_integer = min(max(first_integer, second_integer), max(third_integer, fourth_integer))
mid_high_integer = max(min(first_integer, second_integer), min(third_integer, fourth_integer))
max_integer = max(first_integer, second_integer, third_integer, fourth_integer)