This question already has answers here:
python order of elements in set
(2 answers)
Does Python have an ordered set?
(15 answers)
Closed 3 months ago.
I face a strange problem where set comprehension won't work as intended.
b=[[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]]
print({(R, 0, 0) for _, R, _ in b})
We have output
{(12, 0, 0), (9, 0, 0), (7, 0, 0), (20, 0, 0), (24, 0, 0)}
Not the desired answer
{(9, 0, 0), (7, 0, 0), (12, 0, 0), (20, 0, 0), (24, 0, 0)}
What went wrong? For list comprehension, it works fine.
b=[[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]]
print([(R, 0, 0) for _, R, _ in b])
We have output
[(9, 0, 0), (7, 0, 0), (12, 0, 0), (20, 0, 0), (24, 0, 0)]
It is natural to have sets comprehension looks like this. Based on Iain and juanpa's comment, sets are not ordered. The order is not preserved. So that's why we have "unintended" print value.
On the other hand, if you want to preserve the order, you should use lists.
Related
I'm watching a tutorial on how to make Tetris and I'm completely lost trying to understand how to represent the different rotations of pieces.
The tutorial references the 2009 Tetris Design Guide, particularly the slide on Tetrimino Facings:
Relevant Code in the video starts at 1:10:27
The below code represent tuples indicating coordinates at which every piece rotates around.
Kind::O => &[(0, 0), (0, 1), (1, 0), (1, 1)],
Kind::I => &[(-1, 0), (0, 0), (1, 0), (2, 0)],
Kind::T => &[(-1, 0), (0, 0), (1, 0), (0, 1)],
Kind::L => &[(-1, 0), (0, 0), (1, 0), (1, 1)],
Kind::J => &[(-1, 1), (-1, 0), (0, 0), (1, 0)],
Kind::S => &[(-1, 0), (0, 0), (0, 1), (1, 1)],
Kind::Z => &[(-1, 1), (0, 1), (0, 0), (1, 0)],
Can someone explain to me this representation system please?
Example with J [(-1, 1), (-1, 0), (0, 0), (1, 0)]:
(y)
3
2
1 A
0 B C D
-1
-1 0 1 2 (x)
where
A = (-1, 1)
B = (-1, 0)
C = ( 0, 0)
D = ( 1, 0)
(0,0) (here C) is the center of rotation.
To rotate, do this transform for each block :
clockwise: (x, y) => (-y, x)
anticlockwise: (x, y) => (y, -x)
I have this input P (0, 2,+1) (2, 0, -1) (0, 4,+1) (4, 0, -1) and I would like to have it printed out this way [(0, 2, 1), (2, 0, -1), (0, 4, 1), (4, 0, -1)]
. However, due to the extra space in the input I ran into this error. Without making any change to the input, I wonder if anyone could advise? Thanks
algorithm_type_2 = list(eval(user_input_2))
File "<string>", line 1
(0,,2,+1),(2,,0,,-1),(0,,4,+1),(4,,0,,-1)
^
SyntaxError: invalid syntax
user_input = input().split()
# input_list = user_input.split()
# algorithm_type = 'X'
algorithm_type = user_input.pop(0)
user_input_2 = ','.join(user_input)
algorithm_type_2 = list(eval(user_input_2))
print(user_input_2)
print(algorithm_type_2)
I'm not sure the context etc.. I know this is ugly, but in situations like this you can add a split character just to have a nice easy character to split on. Did I say split?
Here is what I did. Replace the closing bracket with a closing bracket and a "$" then split on the "$".
user_input = input().replace(")",")$").split("$")
# input_list = user_input.split()
# algorithm_type = 'X'
algorithm_type = user_input.pop(0)
user_input_2 = ','.join(user_input)
algorithm_type_2 = list(eval(user_input_2))
print(user_input_2)
print(algorithm_type_2)
This gave me the output you wanted.
= RESTART: C:/Users/Jeremy Wright/Desktop/Desktop Mess/Not Work/StackOverflow Stuff/example.py
(0, 2,+1) (2, 0, -1) (0, 4,+1) (4, 0, -1)
(2, 0, -1), (0, 4,+1), (4, 0, -1),
[(2, 0, -1), (0, 4, 1), (4, 0, -1)]
>>>
I want to call a function that accepts tuples as part of its arguments, i.e. the function is called as follows:
foo('Hello', (x1, x2), (y1, y2), (z1, z2))
I have a list of tuples as follows:
arr = [(1, 1), (2, 2), (3, 3)]
Calling the function with foo("Hello", *arr) (as suggested by many answers online) doesn't work. It gives the error: TypeError: foo() missing 2 required positional arguments: 'y' and 'z'
The function definition cannot be modified, i.e. I cannot change it to foo("Hello", *arg).
Edit: The specific function I'm using is cv2.rectangle, which draws a rectangle on an image. There are 3 tuples in the argument list: (x1, y1), (x2, y2) and a tuple of the RGB values e.g. (255, 0, 0). Because I need to call this cv2.rectangle function multiple times with different values, I store those values in a dictionary like this:
params = {
key1: [(10, 10), (60, 30), (255, 0, 0)],
key2: [(15, 15), (65, 35), (255, 100, 255)],
key3: [(20, 20), (70, 40), (255, 0, 0)],
key4: [(25, 25), (75, 45), (255, 100, 0)],
key5: [(30, 30), (80, 50), (0, 100, 0)],
key6: [(35, 35), (85, 55), (255, 0, 0)],
key7: [(40, 40), (90, 60), (0, 100, 0)],
key8: [(45, 45), (95, 65), (0, 100, 255)],
key9: [(50, 50), (100, 70), (255, 0, 0)],
key10: [(55, 55), (105, 75), (255, 100, 0)]
}
To call the cv2.rectangle function, of course I can just pass in params[key1][0], params[key1][1], params[key1][2], but I want to know if there is generally a more elegant solution for it, where I can do such thing as
cv2.rectangle(..., *params[key1], ...)
Any help would be appreciated. Thanks!
When you say you need to call it multiple times with different values, the natural way to do this is looping over the sets of values you need to provide.
image = ... # The first argument to cv2's `rectangle` function
for key, values in params.items():
assert isinstance(values, list) # Just checking!
cv2.rectangle(image, *values)
Without access to your specific data (a minimal reproducible example), I created my own to show that this works.
In [1]: def foo(a, b, c, d, e=True, f="hi"):
...: print(a, b, c, d, e, f)
...:
In [2]: params = {"one": ['b', 'bee', 'beta'], "two": ['g', 'gee', 'gamma']}
In [3]: for key, values in params.items():
...: foo("hello!", *values, False)
...:
hello! b bee beta False hi
hello! g gee gamma False hi
I´m trying to iterate trough elements where list N return an iterator, which goes through all possible tuples A, where Ai changes from 0 to Ni. the elements are always and int. A solution or a path to a solution with for loops and while loops is preferred.
def f(*args):
lst = []
for i in range(args[0]):
for x in range(args[1]):
for a in range(args[2]):
for v in range(args[3]):
lst.append((i,x,a,v))
return lst
print(f(5, 3, 1, 5))
This code works, but I don't want it hard coded: let's say if I want to input another int, lets say: print(f(5, 3, 1, 5, 6)) <- it should work for that as well.
So the question is how to make a loop like the one above without the hard coding?
I have tried to use a while loop and a for loop inside the while:
def f(*args):
cnt = 0
lst = []
while len(args) > cnt:
print(cnt)
for i in range(args[cnt]):
lst.append(i)
print(lst)
cnt += 1
return lst
print(f(5, 3, 1, 5))
this is the correct output of the first code snippet and this is what I want without the hard coding:
Another way you could do it is:
def f(*args):
res = [[]]
for z in map(range, args):
res = [tuple(x)+(y,) for x in res for y in z]
return res
f(5,3,1,5)
You can use recursion for this. It replaces the unknown number of layered for loops.
def fr(tt, idx = 0):
lst = []
if idx == len(tt)-1: # last index, just loop
for x in range(tt[idx]):
lst.append(tt[:-1] + (x,))
return lst
for x in range(tt[idx]): # loop and call child loops
l2 = tt[:idx] + (x,) + tt[idx+1:] # update this index
lst.extend(fr(tuple(l2), idx+1)) # iterate next level
return lst
print(fr((5, 3, 1, 5)))
print(fr((5, 3, 1, 5, 6)))
Output
[(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3), (0, 0, 0, 4), ......., (4, 2, 0, 3), (4, 2, 0, 4)]
[(0, 0, 0, 0, 0), (0, 0, 0, 0, 1), (0, 0, 0, 0, 2), (0, 0, 0, 0, 3), ......., (4, 2, 0, 4, 4), (4, 2, 0, 4, 5)]
A file (included with two examples) is a list of banned number intervals. A line that contains, for example, 12-18, indicates that all numbers 12 to (inclusive) 18 are prohibited. The intervals may overlap.
We want to know what the minimum number is.
Use variables to analyze run-time (not necessarily need all them):
• N: Maximum (not maximum permissible) number; So the numbers are between 0 and N
• K: number of intervals in a file
• M: width of maximum interval.
A. There is an obvious way to solve this problem: we're checking all numbers until we run into the smallest allowed.
• How fast is such an algorithm?
B. You can probably imagine another simple algorithm that uses N bytes (or bits) of memory.
(Hint: strikethrough.)
• Describe it with words. For example, you can make your own assignment (say a few intervals with numbers between 0 and 20), and show the algorithm on them. However, it also draws up a general description.
• How fast is this algorithm? When thinking, use N, K, and M (if you need it).
C. Make an algorithm that does not consume additional memory (more accurately: the memory consumption should be independent of N, K and M), but it is faster than the algorithm under point A.
• Describe it.
• How fast is it? Is it faster than the B algorithm?
D. Now we are interested in how many numbers are allowed (between 0 and N). How would you adjust the above algorithms for this question? What happens to their rates?
file = "0-19.txt"
intervals = [tuple(map(int, v.split("-"))) for v in open(file)]
#example# intervals = [(12, 18), (2, 5), (3, 8), (0, 4), (15, 19), (6, 9), (13, 17), (4, 8)]#
my current code just executes the program but better algorithms for the code i am yet to figure, still need a lot of work to understand, i would need a quick solution code/algorithm for examples A, B, and C and maybe D. Then i can study the time analysis myself. Appreciate help!
def generator_intervala(start, stop, step):
forbidden_numbers = set()
while start <= stop:
forbidden_numbers.add(start)
start += step
return (forbidden_numbers)
mnozica = set()
for interval in intervals:
a, b = interval
values = (generator_intervala(a, b, 1))
for i in values:
mnozica.add(i)
allowed_numbers = set()
N = max(mnozica)
for i in range(N):
if i not in mnozica:
allowed_numbers.add(i)
print(intervals)
print(mnozica)
print(min(allowed_numbers))
print(max(mnozica))
Output:
[(12, 18), (2, 5), (3, 8), (0, 4), (15, 19), (6, 9), (13, 17), (4, 8)]
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19}
10
19
Your set approach is needlessly complex:
N = 100
ranges = [(12, 18), (2, 5), (3, 8), (0, 4), (15, 19), (6, 9), (13, 17), (4, 8)]
do_not_use = set()
for (a,b) in ranges:
do_not_use.update(range(a,b+1))
print(do_not_use)
print( min(a for a in range(N+1) if a not in do_not_use))
Is about all that is needed. Output:
set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19])
10
This is independend of N it just depends on how many numbers are in the ranges.
Storing only forbidden numbers in a set takes O(1) for checking, using the min() buildin over a range to get the minimum.
You can make it faster if you sort your tuples first and then iterate them until you find the first gap making it Θ(N log N) for the sort, followed by Θ(N) for the search:
def findme():
ranges = [(12, 18), (2, 5), (3, 8), (0, 4), (15, 19), (6, 9), (13, 17), (4, 8)]
ranges.sort() # inplace sort, no additional space requirements
if ranges[0][0]>0:
return 0
for ((a_min,a_max),(b_min,b_max)) in zip(ranges,ranges[1:]):
if a_max < b_min-1:
return a_max+1
return ranges[-1][1]+1 # might give you N+1 if no solution in 0-N exists
timeit of yours vs mine:
Your code uses 2 sets, as well as multiple loops, incremental addition to your set and function calls that makes it slower:
N = 100
def findme():
ranges = [(12, 18), (2, 5), (3, 8), (0, 4), (15, 19), (6, 9), (13, 17), (4, 8)]
ranges.sort()
if ranges[0][0]>0:
return 0
for ((a_min,a_max),(b_min,b_max)) in zip(ranges,ranges[1:]):
if a_max < b_min-1:
return a_max+1
return ranges[-1][1]+1
def mine():
ranges = [(12, 18), (2, 5), (3, 8), (0, 4), (15, 19), (6, 9), (13, 17), (4, 8)]
N = 100
do_not_use = set()
for (a,b) in ranges:
do_not_use.update(range(a,b+1))
return min(a for a in range(N+1) if a not in do_not_use)
def yours():
ranges = [(12, 18), (2, 5), (3, 8), (0, 4), (15, 19), (6, 9), (13, 17), (4, 8)]
def generator_intervala(start, stop, step):
forbidden_numbers = set()
while start <= stop:
forbidden_numbers.add(start)
start += step
return (forbidden_numbers)
mnozica = set()
for interval in ranges:
a, b = interval
values = (generator_intervala(a, b, 1))
for i in values:
mnozica.add(i)
allowed_numbers = set()
N = max(mnozica)
for i in range(N):
if i not in mnozica:
allowed_numbers.add(i)
return min(allowed_numbers)
import timeit
print("yours", timeit.timeit(yours,number=100000))
print("mine", timeit.timeit(mine,number=100000))
print("findme", timeit.timeit(findme,number=100000))
Output:
yours 1.3931225209998956
mine 1.263602267999886
findme 0.1711935210005322