Find common union groups among tuples in a set - python

I need help to write a function that:
takes as input set of tuples
returns the number of tuples that has unique numbers
Example 1:
# input:
{(0, 1), (3, 4), (0, 0), (1, 1), (3, 3), (2, 2), (1, 0)}
# expected output: 3
The expected output is 3, because:
(3,4) and (3,3) contain common numbers, so this counts as 1
(0, 1), (0, 0), (1, 1), and (1, 0) all count as 1
(2, 2) counts as 1
So, 1+1+1 = 3
Example 2:
# input:
{(0, 1), (2, 1), (0, 0), (1, 1), (0, 3), (2, 0), (0, 2), (1, 0), (1, 3)}
# expected output: 1
The expected output is 1, because all tuples are related to other tuples by containing numbers in common.

This may not be the most efficient algorithm for it, but it is simple and looks nice.
from functools import reduce
def unisets(iterables):
def merge(fsets, fs):
if not fs: return fsets
unis = set(filter(fs.intersection, fsets))
return {reduce(type(fs).union, unis, fs), *fsets-unis}
return reduce(merge, map(frozenset, iterables), set())
us = unisets({(0,1), (3,4), (0,0), (1,1), (3,3), (2,2), (1,0)})
print(us) # {frozenset({3, 4}), frozenset({0, 1}), frozenset({2})}
print(len(us)) # 3
Features:
Input can be any kind of iterable, whose elements are iterables (any length, mixed types...)
Output is always a well-behaved set of frozensets.

this code works for me
but check it maby there edge cases
how this solution?
def count_groups(marked):
temp = set(marked)
save = set()
for pair in temp:
if pair[1] in save or pair[0] in save:
marked.remove(pair)
else:
save.add(pair[1])
save.add(pair[0])
return len(marked)
image

Related

Paths in Python/Sage

I've been working on this problem (https://imgur.com/a/nJEMfM9) asking me to plot all lattice paths in a nxn grid for the last week, and I have no idea how to proceed.
This is about as far as I've been able to get
def NE_lattice_paths(x,y):
Vn= vector([0,1])
Ve= vector([1,0])
plot(Vn) + plot(Ve, start=Vn)
I know I have to use vectors, and I have to use the "def" command to make a function, but how would I make a function that can plot every path and know to take a different one each time? What I wrote doesn't really make sense, but I could use some guidance on how to proceed. Thank you!
You can get all the paths with a nested for loop (or list comprehension).
So this will give all the paths.
def NE_lattice_paths(x,y):
paths = []
for i in range(x):
path = []
for j in range(y):
path.append((i,j))
paths.append(path)
return paths
result = NE_lattice_paths(5,3)
print(result)
result
[[(0, 0), (0, 1), (0, 2)], [(1, 0), (1, 1), (1, 2)], [(2, 0), (2, 1), (2, 2)], [(3, 0), (3, 1), (3, 2)], [(4, 0), (4, 1), (4, 2)]]
I will leave it as an excersize for the OP to do the animation...

Getting the correct max value from a list of tuples

My list of tuples look like this:
[(0, 0), (3, 0), (3, 3), (0, 3), (0, 0), (0, 6), (3, 6), (3, 9), (0, 9), (0, 6), (6, 0), (9, 0), (9, 3), (6, 3), (6, 0), (0, 3), (3, 3), (3, 6), (0, 6), (0, 3)]
It has the format of (X, Y) where I want to get the max and min of all Xs and Ys in this list.
It should be min(X)=0, max(X)=9, min(Y)=0, max(Y)=9
However, when I do this:
min(listoftuples)[0], max(listoftuples)[0]
min(listoftuples)[1], max(listoftuples)[1]
...for the Y values, the maximum value shown is 3 which is incorrect.
Why is that?
for the Y values, the maximum value shown is 3
because max(listoftuples) returns the tuple (9, 3), so max(listoftuples)[0] is 9 and max(listoftuples)[1] is 3.
By default, iterables are sorted/compared based on the values of the first index, then the value of the second index, and so on.
If you want to find the tuple with the maximum value in the second index, you need to use key function:
from operator import itemgetter
li = [(0, 0), (3, 0), ... ]
print(max(li, key=itemgetter(1)))
# or max(li, key=lambda t: t[1])
outputs
(3, 9)
Here is a simple way to do it using list comprehensions:
min([arr[i][0] for i in range(len(arr))])
max([arr[i][0] for i in range(len(arr))])
min([arr[i][1] for i in range(len(arr))])
max([arr[i][1] for i in range(len(arr))])
In this code, I have used a list comprehension to create a list of all X and all Y values and then found the min/max for each list. This produces your desired answer.
The first two lines are for the X values and the last two lines are for the Y values.
Tuples are ordered by their first value, then in case of a tie, by their second value (and so on). That means max(listoftuples) is (9, 3). See How does tuple comparison work in Python?
So to find the highest y-value, you have to look specifically at the second elements of the tuples. One way you could do that is by splitting the list into x-values and y-values, like this:
xs, ys = zip(*listoftuples)
Or if you find that confusing, you could use this instead, which is roughly equivalent:
xs, ys = ([t[i] for t in listoftuples] for i in range(2))
Then get each of their mins and maxes, like this:
x_min_max, y_min_max = [(min(L), max(L)) for L in (xs, ys)]
print(x_min_max, y_min_max) # -> (0, 9) (0, 9)
Another way is to use NumPy to treat listoftuples as a matrix.
import numpy as np
a = np.array(listoftuples)
x_min_max, y_min_max = [(min(column), max(column)) for column in a.T]
print(x_min_max, y_min_max) # -> (0, 9) (0, 9)
(There's probably a more idiomatic way to do this, but I'm not super familiar with NumPy.)

How can I add a random binary info into current 'coordinate'? (Python)

This is part of the code I'm working on: (Using Python)
import random
pairs = [
(0, 1),
(1, 2),
(2, 3),
(3, 0), # I want to treat 0,1,2,3 as some 'coordinate' (or positional infomation)
]
alphas = [(random.choice([1, -1]) * random.uniform(5, 15), pairs[n]) for n in range(4)]
alphas.sort(reverse=True, key=lambda n: abs(n[0]))
A sample output looks like this:
[(13.747649802587832, (2, 3)),
(13.668274782626717, (1, 2)),
(-9.105374057105703, (0, 1)),
(-8.267840318934667, (3, 0))]
Now I'm wondering is there a way I can give each element in 0,1,2,3 a random binary number, so if [0,1,2,3] = [0,1,1,0], (By that I mean if the 'coordinates' on the left list have the corresponding random binary information on the right list. In this case, coordinate 0 has the random binary number '0' and etc.) then the desired output using the information above looks like:
[(13.747649802587832, (1, 0)),
(13.668274782626717, (1, 1)),
(-9.105374057105703, (0, 1)),
(-8.267840318934667, (0, 0))]
Thanks!!
One way using dict:
d = dict(zip([0,1,2,3], [0,1,1,0]))
[(i, tuple(d[j] for j in c)) for i, c in alphas]
Output:
[(13.747649802587832, (1, 0)),
(13.668274782626717, (1, 1)),
(-9.105374057105703, (0, 1)),
(-8.267840318934667, (0, 0))]
You can create a function to convert your number to the random binary assigned. Using a dictionary within this function would make sense. Something like this should work where output1 is that first sample output you provide and binary_code would be [0, 1, 1, 0] in your example:
def convert2bin(original, binary_code):
binary_dict = {n: x for n, x in enumerate(binary_code)}
return tuple([binary_code[x] for x in original])
binary_code = np.random.randint(2, size=4)
[convert2bin(x[1], binary_code) for x in output1]

How Compare and Extract equal values from Nested Dictionary and Append the equal values to a List

I have the following function responsible to generate a nested dictionary with integer keys that works inside a for loop, and I want to create a updated list when equal values are found:
# The number of times I want to run
n = 2
# Number of Loops
count = 0
# Init the Hash Table
castar_hash = {}
def run_discrete_time(start, path, count):
'''
Create or Update the Nested Dic, look for equal values and append
a new list based on the 'path' input
Args:
start (list)
path (list)
count (int)
Vars:
start = A simple list with one element
path = A list of Tuples
count = The atual Loop Interaction
'''
# Inserted the 0 because the "discrete time" will init at 0
path_list.insert(0, start)
time_run = list(range(0, len(path_list)+1))
hash = {t:p for t,p in zip(time_run,path_list)}
#* Create a new Dic Key
value_list = ['value', str(count)]
value = "".join(value_list)
castar_hash.update({value:hash})
print(f'\nThe time steps is: {time_run}')
print(f'The Hash table is: {castar_hash}\n')
'''
Need the code here to find the equal values
in the castar_hash and append to a updated list
'''
return updated_list
def main():
for _ in range(n):
'''
some code here who picks the start and path from a deque
already implemented (see the inputs bellow)
'''
count += 1
run_discrete_time(start, path, count)
if __name__ == '__main__':
main()
Let me explain how the function works with inputs: Considering that the loop will run 2 times (since the number of times "n" is 2), for the first call, considering the input:
run_discrete_time([4, 6], [(4, 5), (4, 4),(4, 3),(5, 3),(6, 3),
(7, 3), (8, 3), (8, 2), (8, 1),(9, 1)],
count)
The generated nested dic will be:
castar_hash = {'value1': {0:(4, 6), 1:(4, 5), 2:(4, 4), 3:(4, 3),
4:(5, 3), 5:(6, 3), 6:(7, 3), 7:(8, 3),
8:(8, 2), 9:(8, 1), 10:(9, 1)},
For the second loop with inputs:
run_discrete_time([1, 6], [(2, 6), (4, 4), (4, 6),(4, 5), (4, 4),
(4, 3), (5, 3), (6, 3), (8, 1), (8, 3), (9, 3)],
count)
The updated nest dic will now be:
castar_hash = {'value1': {0:(4, 6), 1:(4, 5), 2:(4, 4), 3:(4, 3),
4:(5, 3), 5:(6, 3), 6:(7, 3), 7:(8, 3),
8:(8, 2), 9:(8, 1), 10:(9, 1)},
'value2': {0:(1, 6), 1:(2, 6), 2:(4, 4), 3:(4, 6),
4:(4, 5), 5:(4, 4), 6:(4, 3), 7:(5, 3),
8:(6, 3), 9:(8, 1), 10:(8, 3), 11:(9,3)}}
My question is: What is the best and most efficient way to extract the equal values in the nested dics for every loop (considering that I can have more than two)? I'm struggling a lot to find a solution for that.
For example, the repeated values in the 'value2' dic is 2:(4, 4) and 9:(8, 1) (in relation to the 'value1' dic), and I would like to return a new list updated as (4,4) inserted in the index 2, and (8,1) at index 9, for example:
#The Path List of Tuples inputed at the second loop
path = [(2, 6), (4, 4), (4, 6),(4, 5), (4, 4),
(4, 3), (5, 3), (6, 3), (8, 1), (8, 3), (9, 3)]
#The New Updated Path List that I want to return since
#the method finded equals values compared to the 'value1' dic:
new_path = [(2, 6), (4, 4), (4, 4) (4, 6),(4, 5), (4, 4),
(4, 3), (5, 3), (6, 3), (8, 1), (8, 1), (8, 3),
(9, 3)]

Strange interferences bewteen Heapq module and dictionary

On one hand, I have a grid defaultdict that stores the neighboring nodes of each node on a grid and its weight (all 1 in the example below).
node (w nbr_node)
grid = { 0: [(1, -5), (1, -4), (1, -3), (1, -1), (1, 1), (1, 3), (1, 4), (1, 5)],
1: [(1, -4), (1, -3), (1, -2), (1, 0), (1, 2), (1, 4), (1, 5), (1, 6)],
2: [(1, -3), (1, -2), (1, -1), (1, 1), (1, 3), (1, 5), (1, 6), (1, 7)],
3: [(1, -2), (1, -1), (1, 0), (1, 2), (1, 4), (1, 6), (1, 7), (1, 8)],
...
}
On the other, I have a Djisktra function that computes the shortest path between 2 nodes on this grid. The algorithm uses the heapq module and works perfectly fine.
import heapq
def Dijkstra(s, e, grid): #startpoint, endpoint, grid
visited = set()
distances = {s: 0}
p = {}
queue = [(0, s)]
while queue != []:
weight, node = heappop(queue)
if node in visited:
continue
visited.add(node)
for n_weight, n_node in grid[node]:
if n_node in visited:
continue
total = weight + n_weight
if n_node not in distances or distances[n_node] > total:
distances[n_node] = total
heappush(queue, (total, n_node))
p[n_node] = node
Problem: when calling the Djikstra function multiple times, heappush is... adding new keys in the grid dictionary for no reason !
Here is a MCVE:
from collections import defaultdict
# Creating the dictionnary
grid = defaultdict(list)
N = 4
kernel = (-N-1, -N, -N+1, -1, 1, N-1, N, N+1)
for i in range(N*N):
for n in kernel:
if i > N and i < (N*N) - 1 - N and (i%N) > 0 and (i%N) < N - 1:
grid[i].append((1, i+n))
# Calling Djikstra multiple times
keys = [*range(N*N)]
while keys:
k1, k2 = random.sample(keys, 2)
Dijkstra(k1, k2, grid)
keys.remove(k1)
keys.remove(k2)
The original grid defaultdict:
dict_keys([5, 6, 9, 10])
...and after calling the Djikstra function multiple times:
dict_keys([5, 6, 9, 10, 4, 0, 1, 2, 8, 3, 7, 11, 12, 13, 14, 15])
When calling the Djikstra function multiple times without heappush (just commenting heappush at the end):
dict_keys([5, 6, 9, 10])
Question:
How can I avoid this strange behavior ?
Please note that I'm using Python 2.7 and can't use numpy.
I could reproduce and fix. The problem is in the way you are building grid: it contains values that are not in keys from -4 to 0 and from 16 to 20 in the example. So you push those inexistant nodes on the head, and later pop them.
And you end in executing for n_weight, n_node in grid[node]: where node does not (still) exists in grid. As grid is a defaultdict, a new node is automatically inserted with an empty list as value.
The fix is trivial (at least for the example data): it is enough to ensure that all nodes added as value is grid exist as key with a modulo:
for i in range(N*N):
for n in kernel:
grid[i].append((1, (i+n + N + 1)%(N*N)))
But even for real data it should not be very hard to ensure that all nodes existing in grid values also exist in keys...
BTW, if grid had been a simple dict the error would have been immediate with a KeyError on grid[node].

Categories