Finding the midpoint of tuple of any length in python - python

I need to take a tuple of any length and preforming an operation to return the midpoint. However, I need to function to work with a tuple of any length so I'm not sure how to go about it.
def findMidpoint(P: tuple, Q: tuple) -> tuple:
user_input1 = input('Enter space-separated integers: ')
P = tuple(int(item) for item in user_input1.split())
user_input2 = input('Enter space-separated integers: ')
Q = tuple(int(item) for item in user_input2.split())
Midpoint
pass
def main():
# use to test the findMidpoint function
pass
if __name__ == "__main__":
main()

Okay, taking some liberties here with what you're asking, but assuming what you want is to find the midpoint of any two points in an N-dimensional space, you can average the value of each point axis-wise. For example:
P = (px, py)
Q = (qx, qy)
midpoint = ( (px + qx)*0.5, (py + qy)*0.5 )
Obviously, for more dimensions you have to extend this. A general N-dimensional solution, with your code, can make use of zip:
def findMidpoint(P: tuple, Q: tuple) -> tuple:
return tuple((q + p) / 2 for p, q in zip(P, Q))
def main():
# use to test the findMidpoint function
assert findMidpoint(P=(0, 0), Q=(2, 2)) == (1, 1)
assert findMidpoint(P=(0, 0, 0), Q=(2, 2, 2)) == (1, 1, 1)
assert findMidpoint(P=(-2, -2, -2), Q=(2, 2, 2)) == (0, 0, 0)
if __name__ == "__main__":
main()
This assumes P and Q are the same length. If they're not, you could go one further and use zip_longest:
from itertools import zip_longest
def findMidpoint(P: tuple, Q: tuple) -> tuple:
return tuple((q + p) / 2 for p, q in zip_longest(P, Q, fillvalue=0))
def main():
# use to test the findMidpoint function
assert findMidpoint(P=(0, 0), Q=(2, 2, 2)) == (1, 1, 1)
if __name__ == "__main__":
main()
This would essentially say "if no coordinate is given for an axis, assume it is zero".

Related

Function to calculate the average distance from a set of tuples (Python)

I need to implement a function that from a given set of points, specified by a pair of integers returns the average distance between the points. If there are less points that 2 in the set, it raises a Value Error.
distance is computed using the formula:
d=sqrt ((x1−x2)**2+(y1−y2)**2)
I'm struggling to get the loop to work, but it gives me an error that types.Genericaliases has no len(). Realised that this has something to do with the input being a set, but now I don't know how to resolve this:
def average_distance(points: set[tuple[int,int]]) -> float:
from math import sqrt
points = list[input()]
list_dist =[]
for index in range(0, len(points)):
coordinate = points[index] # tuple in the set points
x1 = coordinate[0] # first el in the pair
y1 = coordinate[1] # second el in the pair
next_coordinate = points[index +1]
x2 = next_coordinate[0]
y2 = next_coordinate[1]
distance = math.sqrt(((x1-x2)**2)+((y1-y2)**2))
list_dist.append(distance)
total_dist = 0
for dist in distance:
total_dist += dist
avg_dist = total_dist//(len(distance))
return avg_dist
So
print (average_distance({(1,2), (3,4), (5,6)}))
Expected output:
3.7712
Would be grateful for your advice on this.
Many thanks
Shorter solution using the library more:
from statistics import mean
from math import dist
from itertools import combinations, starmap
def average_distance(points):
return mean(starmap(dist, combinations(points, 2)))
print(average_distance({(1,2), (3,4), (5,6)}))
Output:
3.771236166328254
Here is my implementation for both the average distance between every group of two points given sequentially and all combinations of points. Take a look.
from itertools import combinations
from math import sqrt
from typing import List, NamedTuple
class Point(NamedTuple):
x: float
y: float
def distance(p1: Point, p2: Point) -> float:
return sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2)
def avg_dist_between_all_points(points: List[Point]) -> float:
c = list(combinations(points, 2))
return sum(distance(*pair) for pair in c) / len(c)
def avg_dist_between_seq_points(points: List[Point]) -> float:
c = [points[i : i + 2] for i in range(len(points) - 1)]
return sum(distance(*pair) for pair in c) / len(c)
if __name__ == "__main__":
input_str = input("points (ex: 1,2 3,4 5,6): ")
point_strs = input_str.split(" ")
points: List[Point] = []
for s in point_strs:
x, y = s.split(",")
points.append(Point(float(x), float(y)))
print(avg_dist_between_all_points(points))
print(avg_dist_between_seq_points(points))
This yields:
➜ ./avgdist.py
points (ex: 1,2 3,4 5,6): 1,2 3,4 5,6
3.771236166328254
2.8284271247461903

Minimum moves to reach k

Given two numbers m and n, in one move you can get two new pairs:
m+n, n
m, n+m
Let's intially set m = n = 1 find the minimum number of moves so that at least one of the numbers equals k
it's guaranteed there's a solution (i.e. there exist a sequence of moves that leads to k)
For example:
given k = 5
the minimum number of moves so that m or n is equal to k is 3
1, 1
1, 2
3, 2
3, 5
Total of 3 moves.
I have come up with a solution using recursion in python, but it doesn't seem to work on big number (i.e 10^6)
def calc(m, n, k):
if n > k or m > k:
return 10**6
elif n == k or m == k:
return 0
else:
return min(1+calc(m+n, n, k), 1+calc(m, m+n, k))
k = int(input())
print(calc(1, 1, k))
How can I improve the performance so it works for big numbers?
Non-Recursive Algorithm based on Priority Queue (using Heap)
State: (sum_, m, n, path)
sum_ is current sum (i.e. m + n)
m and n are the first and second numbers
path is the sequence of (m, n) pairs to get to the current sum
In each step there are two possible moves
Replace first number by the sum
Replace second number by the sum
Thus each state generates two new states. States are prioritized by:
moves: states with a lower number of have higher priority
sum: States with higher sums have higher priority
We use a Priority Queue (Heap in this case) to process states by priority.
Code
from heapq import heappush, heappop
def calc1(k):
if k < 1:
return None, None # No solution
m, n, moves = 1, 1, 0
if m == k or n == k:
return moves, [(m, n)]
h = [] # Priority queue (heap)
path = [(m, n)]
sum_ = m + n
# Python's heapq acts as a min queue.
# We can order thing by max by using -value rather than value
# Thus Tuple (moves+1, -sum_, ...) prioritizes by 1) min moves, and 2) max sum
heappush(h, (moves+1, -sum_, sum_, n, path))
heappush(h, (moves+1, -sum_, m, sum_, path))
while h:
# Get state with lowest sum
moves, sum_, m, n, path = heappop(h)
sum_ = - sum_
if sum_ == k:
return moves, path # Found solution
if sum_ < k:
sum_ = m + n # new sum
# Replace first number with sum
heappush(h, (moves+1, -sum_, sum_, n, path + [(sum_, n)]))
# Replace second number with sum
heappush(h, (moves+1, -sum_, m, sum_, path + [(m, sum_)]))
# else:
# so just continues since sum_ > k
# Exhausted all options, so no solution
return None, None
Test
Test Code
for k in [5, 100, 1000]:
moves, path = calc1(k)
print(f'k: {k}, Moves: {moves}, Path: {path}')
Output
k: 5, Moves: 3, Path: [(1, 1), (2, 3), (2, 5)]
k: 100, Moves: 10, Path: [(1, 1), (2, 3), (5, 3), (8, 3), (8, 11),
(8, 19), (27, 19), (27, 46), (27, 73), (27, 100)]
k: 1000, Moves: 15, Path: [(1, 1), (2, 3), (5, 3), (8, 3), (8, 11),
(19, 11), (19, 30), (49, 30), (79, 30), (79, 109),
(188, 109), (297, 109), (297, 406), (297, 703), (297, 1000)]
Performance Improvement
Following two adjustments to improve performance
Not including path just number of steps (providing 3X speedup for k = 10,000
Not using symmetric pairs (provided 2x additional with k = 10, 000
By symmetric pairs, mean pairs of m, n which are the same forward and backwards, such as (1, 2) and (2, 1).
We don't need to branch on both of these since they will provide the same solution step count.
Improved Code
from heapq import heappush, heappop
def calc(k):
if k < 1:
return None, None
m, n, moves = 1, 1, 0
if m == k or n == k:
return moves
h = [] # Priority queue (heap)
sum_ = m + n
heappush(h, (moves+1, -sum_, sum_, n))
while h:
moves, sum_, m, n = heappop(h)
sum_ = - sum_
if sum_ == k:
return moves
if sum_ < k:
sum_ = m + n
steps = [(sum_, n), (m, sum_)]
heappush(h, (moves+1, -sum_, *steps[0]))
if steps[0] != steps[-1]: # not same tuple in reverse (i.e. not symmetric)
heappush(h, (moves+1, -sum_, *steps[1]))
Performance
Tested up to k = 100, 000 which took ~2 minutes.
Update
Converted solution by #גלעדברקן from JavaScript to Python to test
def g(m, n, memo):
key = (m, n)
if key in memo:
return memo[key]
if m == 1 or n == 1:
memo[key] = max(m, n) - 1
elif m == 0 or n == 0:
memo[key] = float("inf")
elif m > n:
memo[key] = (m // n) + g(m % n, n, memo)
else:
memo[key] = (n // m) + g(m, n % m, memo)
return memo[key]
def f(k, memo={}):
if k == 1:
return 0
return min(g(k, n, memo) for n in range((k // 2) + 1))
Performance of #גלעדברקן Code
Completed 100K in ~1 second
This is 120X faster than my above heap based solution.
This is an interesting problem in number theory, including linear Diophantine equations. Since there are solutions available on line, I gather that you want help in deriving the algorithm yourself.
Restate the problem: you start with two numbers characterized as 1*m+0*n, 0*m+1*n. Use the shorthand (1, 0) and (0, 1). You are looking for the shortest path to any solution to the linear Diophantine equation
a*m + b*n = k
where (a, b) is reached from starting values (1, 1) a.k.a. ( (1, 0), (0, 1) ).
So ... starting from (1, 1), how can you characterize the paths you reach from various permutations of the binary enhancement. At each step, you have two choices: a += b or b += a. Your existing algorithm already recognizes this binary search tree.
These graph transitions -- edges along a lattice -- can be characterized, in terms of which (a, b) pairs you can reach on a given step. Is that enough of a hint to move you along? That characterization is the key to converting this problem into something close to a direct computation.
We can do much better than the queue even with brute force, trying each possible n when setting m to k. Here's JavaScript code, very close to Python syntax:
function g(m, n, memo){
const key = m + ',' + n;
if (memo[key])
return memo[key];
if (m == 1 || n == 1)
return Math.max(m, n) - 1;
if (m == 0 || n == 0)
return Infinity;
let answer;
if (m > n)
answer = Math.floor(m / n) + g(m % n, n, memo);
else
answer = Math.floor(n / m) + g(m, n % m, memo);
memo[key] = answer;
return answer;
}
function f(k, memo={}){
if (k == 1)
return 0;
let best = Infinity;
for (let n=1; n<=Math.floor(k/2); n++)
best = Math.min(best, g(k, n, memo));
return best;
}
var memo = {};
var ks = [1, 2, 5, 6, 10, 100, 1000, 100000];
for (let k of ks)
console.log(`${ k }: ${ f(k, memo) }`);
By default, in Python, for a recursive function the recursion limit is set to 10^4. You can change it using sys module:
import sys
sys.setrecursionlimit(10**6)

Navigating a grid

I stumbled upon a problem at Project Euler, https://projecteuler.net/problem=15
. I solved this by combinatorics but was left wondering if there is a dynamic programming solution to this problem or these kinds of problems overall. And say some squares of the grid are taken off - is that possible to navigate? I am using Python. How should I do that? Any tips are appreciated. Thanks in advance.
You can do a simple backtrack and explore an implicit graph like this: (comments explain most of it)
def explore(r, c, n, memo):
"""
explore right and down from position (r,c)
report a rout once position (n,n) is reached
memo is a matrix which saves how many routes exists from each position to (n,n)
"""
if r == n and c == n:
# one path has been found
return 1
elif r > n or c > n:
# crossing the border, go back
return 0
if memo[r][c] is not None:
return memo[r][c]
a= explore(r+1, c, n, memo) #move down
b= explore(r, c+1, n, memo) #move right
# return total paths found from this (r,c) position
memo[r][c]= a + b
return a+b
if __name__ == '__main__':
n= 20
memo = [[None] * (n+1) for _ in range(n+1)]
paths = explore(0, 0, n, memo)
print(paths)
Most straight-forwardly with python's built-in memoization util functools.lru_cache. You can encode missing squares as a frozenset (hashable) of missing grid points (pairs):
from functools import lru_cache
#lru_cache(None)
def paths(m, n, missing=None):
missing = missing or frozenset()
if (m, n) in missing:
return 0
if (m, n) == (0, 0):
return 1
over = paths(m, n-1, missing=missing) if n else 0
down = paths(m-1, n, missing=missing) if m else 0
return over + down
>>> paths(2, 2)
6
# middle grid point missing: only two paths
>>> paths(2, 2, frozenset([(1, 1)]))
2
>>> paths(20, 20)
137846528820
There is also a mathematical solution (which is probably what you used):
def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
def paths(w, h):
return factorial(w + h) / (factorial(w) * factorial(h))
This works because the number of paths is the same as the number of ways to choose to go right or down over w + h steps, where you go right w times, which is equal to w + h choose w, or (w + h)! / (w! * h!).
With missing grid squares, I think there is a combinatoric solution, but it's very slow if there are many missing squares, so dynamic programming would probably be better there.
For example, the following should work:
missing = [
[0, 1],
[0, 0],
[0, 0],
]
def paths_helper(x, y, path_grid, missing):
if path_grid[x][y] is not None:
return path_grid[x][y]
if missing[x][y]:
path_grid[x][y] = 0
return 0
elif x < 0 or y < 0:
return 0
else:
path_count = (paths_helper(x - 1, y, path_grid, missing) +
paths_helper(x, y - 1, path_grid, missing))
path_grid[x][y] = path_count
return path_count
def paths(missing):
arr = [[None] * w for _ in range(h)]
w = len(missing[0])
h = len(missing)
return paths_helper(w, h, arr, missing)
print paths()

How to calculate sum of two polynomials?

For instance 3x^4 - 17x^2 - 3x + 5. Each term of the polynomial can be represented as a pair of integers (coefficient,exponent). The polynomial itself is then a list of such pairs like
[(3,4), (-17,2), (-3,1), (5,0)] for the polynomial as shown.
Zero polynomial, 0, is represented as the empty list [], since it has no terms with nonzero coefficients.
I want to write two functions to add and multiply two input polynomials with the same representation of tuple (coefficient, exponent):
addpoly(p1, p2)
multpoly(p1, p2)
Test Cases:
addpoly([(4,3),(3,0)], [(-4,3),(2,1)])
should give [(2, 1),(3, 0)]
addpoly([(2,1)],[(-2,1)])
should give []
multpoly([(1,1),(-1,0)], [(1,2),(1,1),(1,0)])
should give [(1, 3),(-1, 0)]
Here is something that I started with but got completely struck!
def addpoly(p1, p2):
(coeff1, exp1) = p1
(coeff2, exp2) = p2
if exp1 == exp2:
coeff3 = coeff1 + coeff2
As suggested in the comments, it is much simpler to represent polynomials as multisets of exponents.
In Python, the closest thing to a multiset is the Counter data structure. Using a Counter (or even just a plain dictionary) that maps exponents to coefficients will automatically coalesce entries with the same exponent, just as you'd expect when writing a simplified polynomial.
You can perform operations using a Counter, and then convert back to your list of pairs representation when finished using a function like this:
def counter_to_poly(c):
p = [(coeff, exp) for exp, coeff in c.items() if coeff != 0]
# sort by exponents in descending order
p.sort(key = lambda pair: pair[1], reverse = True)
return p
To add polynomials, you group together like-exponents and sum their coefficients.
def addpoly(p, q):
r = collections.Counter()
for coeff, exp in (p + q):
r[exp] += coeff
return counter_to_poly(r)
(In fact, if you were to stick with the Counter representation throughout, you could just return p + q).
To multiply polynomials, you multiply each term from one polynomial pairwise with every term from the other. And furthermore, to multiply terms, you add exponents and multiply coefficients.
def mulpoly(p, q):
r = collections.Counter()
for (c1, e1), (c2, e2) in itertools.product(p, q):
r[e1 + e2] += c1 * c2
return counter_to_poly(r)
This python code worked for me,hope this works for u too...
Addition func
def addpoly(p1,p2):
i=0
su=0
j=0
c=[]
if len(p1)==0:
#if p1 empty
return p2
if len(p2)==0:
#if p2 is empty
return p1
while i<len(p1) and j<len(p2):
if int(p1[i][1])==int(p2[j][1]):
su=p1[i][0]+p2[j][0]
if su !=0:
c.append((su,p1[i][1]))
i=i+1
j=j+1
elif p1[i][1]>p2[j][1]:
c.append((p1[i]))
i=i+1
elif p1[i][1]<p2[j][1]:
c.append((p2[j]))
j=j+1
if p1[i:]!=[]:
for k in p1[i:]:
c.append(k)
if p2[j:]!=[]:
for k in p2[j:]:
c.append(k)
return c
Multiply func
def multipoly(p1,p2):
p=[]
s=0
for i in p1:
c=[]
for j in p2:
s=i[0]*j[0]
e=i[1]+j[1]
c.append((s,e))
p=addpoly(c,p)
return p
I have come up with a solution but I'm unsure that it's optimized!
def addpoly(p1,p2):
for i in range(len(p1)):
for item in p2:
if p1[i][1] == item[1]:
p1[i] = ((p1[i][0] + item[0]),p1[i][1])
p2.remove(item)
p3 = p1 + p2
for item in (p3):
if item[0] == 0:
p3.remove(item)
return sorted(p3)
and the second one:-
def multpoly(p1,p2):
for i in range(len(p1)):
for item in p2:
p1[i] = ((p1[i][0] * item[0]), (p1[i][1] + item[1]))
p2.remove(item)
return p1

Repeat function python

I'm stuck at higher-order functions in python. I need to write a repeat function repeat that applies the function f n times on a given argument x.
For example, repeat(f, 3, x) is f(f(f(x))).
This is what I have:
def repeat(f,n,x):
if n==0:
return f(x)
else:
return repeat(f,n-1,x)
When I try to assert the following line:
plus = lambda x,y: repeat(lambda z:z+1,x,y)
assert plus(2,2) == 4
It gives me an AssertionError. I read about How to repeat a function n times but I need to have it done in this way and I can't figure it out...
You have two problems:
You are recursing the wrong number of times (if n == 1, the function should be called once); and
You aren't calling f on the returned value from the recursive call, so the function is only ever applied once.
Try:
def repeat(f, n, x):
if n == 1: # note 1, not 0
return f(x)
else:
return f(repeat(f, n-1, x)) # call f with returned value
or, alternatively:
def repeat(f, n, x):
if n == 0:
return x # note x, not f(x)
else:
return f(repeat(f, n-1, x)) # call f with returned value
(thanks to #Kevin for the latter, which supports n == 0).
Example:
>>> repeat(lambda z: z + 1, 2, 2)
4
>>> assert repeat(lambda z: z * 2, 4, 3) == 3 * 2 * 2 * 2 * 2
>>>
You've got a very simple error there, in the else block you are just passing x along without doing anything to it. Also you are applying x when n == 0, don't do that.
def repeat(f,n,x):
"""
>>> repeat(lambda x: x+1, 2, 0)
2
"""
return repeat(f, n-1, f(x)) if n > 0 else x

Categories

Resources