Related
I am using Python for a few month now, but I got a task, which I cannot accomplish in an efficient, pythonic way.
I have a dictionary like this:
{ Apple: [{x: [0, 1, 2, 3], y: [4, 5, 6, 7], z: [8, 9, 0, 1]},
{x: [2, 3, 4, 5], y: [6, 7, 8, 9], z: [0, 1, 2, 3]}],
Orange: [{x: [4, 5, 6, 7], y: [8, 9, 1, 0], z: [2, 3, 4, 5]},
{x: [6, 7, 8, 9], y: [0, 1, 2, 3], z: [4, 5, 6, 7]}]}
This is only an example. The dictionary itself has more than two key, and one key has more list elements (coordinates), than 2.
My task is to compare all the key values to all to other key values and found all of those coordinates which are within a given range limit.
For example:
The given range limit is 2:
Apple:[{x:[0,3,6,9], y:[7,0,3,6], z:[9,2,5,8]}]
Orange:[{x:[3,6,9,1], y:[4,5,6,9], z:[9,1,10,11]}]
Here the z keys first two value are within the range, hence it is a match, which I am looking for.
I could come up with a way, but it is not too efficient and too slow. Can you recommend another way for this?
Here is my code so far:
def CompareCoordinates(coordinate_dict):
key_pairs = []
error_count = 0
error_actors = {}
key_list = sorted(coordinate_dict.keys())
for ind, val in enumerate(key_list):
first = key_list[ind]
for si in range(len(key_list)):
if si == ind or si < ind:
continue
second = key_list[si]
tmp_list = [first, second]
key_pairs.append(tmp_list)
for kp in key_pairs:
error_actors[f'{kp[0]} - {kp[1]} - OVERLAP'] = []
error_actors[f'{kp[0]} - {kp[1]} - X'] = []
error_actors[f'{kp[0]} - {kp[1]} - Y'] = []
error_actors[f'{kp[0]} - {kp[1]} - Z'] = []
for fk in coordinate_dict[kp[0]]:
first_x = fk['x']
first_y = fk['y']
first_z = fk['z']
for sk in coordinate_dict[kp[1]]:
second_x = sk['x']
second_y = sk['y']
second_z = sk['z']
if first_x == second_x and first_y == second_y and first_z == second_z:
error_count += 1
error_actors[f'{kp[0]} - {kp[1]} - OVERLAP'].append([coordinate_dict[kp[0]].index(fk)+1, coordinate_dict[kp[1]].index(sk)+1])
else:
diff_x = list(set(first_x).symmetric_difference(set(second_x)))
diff_y = list(set(first_y).symmetric_difference(set(second_y)))
diff_z = list(set(first_z).symmetric_difference(set(second_z)))
if diff_x:
if float(abs(diff_x[0] - diff_x[1])) <= difference:
error_count += 1
error_actors[f'{kp[0]} - {kp[1]} - X'].append([f'{coordinate_dict[kp[0]].index(fk) + 1}. coordinate', f'{coordinate_dict[kp[1]].index(sk) + 1}. coordinate'])
if diff_y:
if float(abs(diff_y[0] - diff_y[1])) <= difference:
error_count += 1
error_actors[f'{kp[0]} - {kp[1]} - Y'].append([f'{coordinate_dict[kp[0]].index(fk) + 1}. coordinate', f'{coordinate_dict[kp[1]].index(sk) + 1}. coordinate'])
if diff_z:
if float(abs(diff_z[0] - diff_z[1])) <= difference:
error_count += 1
error_actors[f'{kp[0]} - {kp[1]} - Z'].append([f'{coordinate_dict[kp[0]].index(fk) + 1}. coordinate', f'{coordinate_dict[kp[1]].index(sk) + 1}. coordinate'])
error_actors = {k: v for k, v in error_actors.items() if v}
return error_count, error_actors
if I understood your task correctly you want to find all the values of each key such that the absolute value of all the differences have the property that the distance from one value to the other is no more than the range limit.
Start with a simpler problem: how can you do it if you have just a list ?
You could use a comparison with min and max values in the list, but here we take the "loops route" because it is better in the case of your problem :
values = [1,2,5,2,1,7,3,4,1,8]
n = 3
min_v = max_v = values[0]
for x in values:
if abs(max_v - min_v) > n :
print("Range limit exceeded")
break
else :
if x < min_v : min_v = x
elif x > max_v : max_v = x
else :
print("All values are within the range")
You can apply the same method to your problem but you need to make it coordinate by coordinate and if you want to do it efficiently for every coordinate at the same time while you loop through coordinate_dict.
This is really just a simple logic question, but I provide a concrete example.
By 'corresponding' variable, I mean a variable that is understood to be related to the underlying data, although the value of the variable is not important for my question, just the fact the variable changes is important.
The code below generates the lists...
A = [2, 2, 2, 3, 2, 3]
B = [3, 3, 3, 2, 3, 2]
...by responding to a change in the values in the 'slope' list. But, actually this should be...
A = [2, 2, 2, 3, 2, 2]
B = [3, 3, 3, 2, 3, 3]
What is the logic required to correct this?
def funcOne(val):
return val + 1
def funcTwo(val):
return val + 2
values = [1, 1, 1, 1, 1, 1]
slope = [1, 1, 1, -1, -1, 1]
A = []
B = []
l = len(values)
for i in range(l-1):
val = values[i]
sign = slope[i]
pre = slope[i-1]
nex = slope[i+1]
if pre == sign:
va = funcOne(val)
vb = funcTwo(val)
A.append(va)
B.append(vb)
if nex != sign:
va = funcOne(val)
vb = funcTwo(val)
A.append(vb)
B.append(va)
print(A)
# target list:
# [2, 2, 2, 3, 3, 2]
print(B)
# target...
# [3, 3, 3, 2, 2, 3]
try this
#!/usr/bin/python3
def funcOne(val):
return val + 1
def funcTwo(val):
return val + 2
values = [1, 1, 1, 1, 1, 1]
slope = [1, 1, 1, -1, -1, 1]
A = []
B = []
l = len(values)
for i in range(l-1):
val = values[i]
sign = slope[i]
pre = slope[i-1]
nex = slope[i+1]
if sign == pre:
va = funcOne(val)
vb = funcTwo(val)
A.append(va)
B.append(vb)
if pre > nex:
va = funcOne(val)
vb = funcTwo(val)
A.append(vb)
B.append(va)
print(A)
# target list:
# [2, 2, 2, 3, 3, 2]
print(B)
# target...
# [3, 3, 3, 2, 2, 3]
Now I have a working solution I see in fact the correct behaviour would result in the lists
A = [2, 2, 2, 3, 2, 2], and
B = [3, 3, 3, 2, 3, 3].
This has been corrected in the original post.
Overall, irrespective of the exact behaviour required, I have found that a solution to this problem is to create and update a variable that represents which function was applied in the previous step (in this case simply a boolean called 'check'), to evaluate against whether the sign of the corresponding variable ('slope') is positive, negative or zero, and to evaluate against whether or not the corresponding variable is the same as in the previous step.
I feel like there must still be a more concise way of expressing this solution so additional comments are welcomed.
#!/usr/bin/env python3
def funcOne(val):
return val + 1
def funcTwo(val):
return val + 2
values = [1, 1, 1, 1, 1, 1]
slope = [1, 1, 1, -1, -1, 1]
A = []
B = []
l = len(values)
check = True
#for i in range(l-1):
for i in range(l):
val = values[i]
sign = slope[i]
pre = slope[i-1]
va = funcOne(val)
vb = funcTwo(val)
if sign == pre and check == True:
A.append(va)
B.append(vb)
check = True
if sign == pre and check == False:
A.append(vb)
B.append(va)
check = False
if sign != pre and sign > 0 and check == True:
A.append(va)
B.append(vb)
check = True
if sign != pre and sign > 0 and check == False:
A.append(vb)
B.append(va)
check = False
if sign != pre and sign < 0 and check == True:
A.append(vb)
B.append(va)
check = True
if sign != pre and sign < 0 and check == False:
A.append(va)
B.append(vb)
check = False
if sign != pre and sign == 0 and check == True:
A.append(va)
B.append(vb)
check = True
if sign != pre and sign == 0 and check == False:
A.append(vb)
B.append(va)
check = False
print(A)
# [2, 2, 2, 3, 2, 2]
print(B)
# [3, 3, 3, 2, 3, 3]
So, How to if given a list of integers and a number called x, return recursively the sum of every x'th number in the list.
In this task "indexing" starts from 1, so if x = 2 and nums = [2, 3, 4, -9], the output should be -6 (3 + -9).
X can also be negative, in that case indexing starts from the end of the list, see examples below.
If x = 0, the sum should be 0 as well.
For example:
print(x_sum_recursion([], 3)) # 0
print(x_sum_recursion([2, 5, 6, 0, 15, 5], 3)) # 11
print(x_sum_recursion([0, 5, 6, -5, -9, 3], 1)) # 0
print(x_sum_recursion([43, 90, 115, 500], -2)) # 158
print(x_sum_recursion([1, 2], -9)) # 0
print(x_sum_recursion([2, 3, 6], 5)) # 0
print(x_sum_recursion([6, 5, 3, 2, 9, 8, 6, 5, 4], 3)) # 15
I have been trying to do this function for 5 hours straight!!!
Would like do see how someone else would solve this.
This is the best i have come up with.
def x_sum_rec_Four(nums: list, x: int) -> int:
if len(nums) == 0:
return 0
elif len(nums) < x:
return 0
elif x > 0:
i = x - 1
return nums[i] + x_sum_rec_Four(nums[i + x:], x)
elif x < 0:
return x_sum_rec_Four(nums[::-1], abs(x))
My problem with this recursion is that the finish return should be:
if len(nums) < x:
return nums[0]
but that would pass things like ([2, 3, 6], 5)) -->> 2 when it should be 0.
If you really need to do it recursively, you could pop x-1 elements from the list before each call, follow the comments below:
def x_sum_recursion(nums, x):
# if x is negative, call the function with positive x and reversed list
if x < 0:
return x_sum_recursion(nums[::-1], abs(x))
# base case for when x is greater than the length of the list
if x > len(nums):
return 0
# otherwise remove the first x-1 items
nums = nums[x-1:]
# sum the first element and remove it from the next call
return nums[0] + x_sum_recursion(nums[1:], x)
print(x_sum_recursion([], 3)) # 0
print(x_sum_recursion([2, 5, 6, 0, 15, 5], 3)) # 11
print(x_sum_recursion([0, 5, 6, -5, -9, 3], 1)) # 0
print(x_sum_recursion([43, 90, 115, 500], -2)) # 158
print(x_sum_recursion([1, 2], -9)) # 0
print(x_sum_recursion([2, 3, 6], 5)) # 0
print(x_sum_recursion([6, 5, 3, 2, 9, 8, 6, 5, 4], 3)) # 15
However, you can do it in a one liner simple and pythonic way:
print(sum(nums[x-1::x] if x > 0 else nums[x::x]))
Explanation:
you slice the list using nums[start:end:increment] when you leave the end empty it slices from the starting position till the end of the list and increments by the increment specified
How do you determine if a list contains either 3 even or 3 odd values all next to each other?
Example lists (True, False, True):
[2, 1, 3, 5]
[2, 1, 2, 5]
[2, 4, 2, 5]
Closest code:
evenOdd = []
while True:
try:
n = int(input())
evenOdd.append(n)
except:
break
for x in evenOdd:
if x % 2 == 0:
print("True")
Here is some code. This is considered more "pythonic" than iterating over indices--this iterates over consective-triples, using the zip function. This will give an error if the list has fewer than three items--you can add that error check. The zip function stops when one of the iterables runs out of values, which is exactly what we want here.
def three_evens_or_odds(alist):
for a, b, c in zip(alist, alist[1:], alist[2:]):
if (((a & 1) and (b & 1) and (c & 1)) or
((a & 1 == 0) and (b & 1 == 0) and (c & 1 == 0))):
return True
return False
print(three_evens_or_odds([2, 1, 3, 5]))
print(three_evens_or_odds([2, 1, 2, 5]))
print(three_evens_or_odds([2, 4, 2, 5]))
Or, even shorter (borrowing an idea from #jdehesa which I should have thought of on my own, so upvote his answer as I did),
def three_evens_or_odds(alist):
for a, b, c in zip(alist, alist[1:], alist[2:]):
if a & 1 == b & 1 == c & 1:
return True
return False
print(three_evens_or_odds([2, 1, 3, 5]))
print(three_evens_or_odds([2, 1, 2, 5]))
print(three_evens_or_odds([2, 4, 2, 5]))
The printout from that is
True
False
True
You can use itertools.groupby():
from itertools import groupby
def check_list(lst):
for k, g in groupby(lst, key=lambda x: x % 2):
if len(list(g)) == 3:
return True
return False
print(check_list([2, 1, 3, 5])) # True
print(check_list([2, 1, 2, 5])) # False
print(check_list([2, 4, 2, 5])) # True
This can be easily adjusted for any group size.
You can iterate through the list of numbers in blocks of three:
def hasThreeContiguousEvenOrOdd(numbers):
for i in range(len(numbers) - 2):
a, b, c = numbers[i:i + 3]
if a % 2 == 0 and b % 2 == 0 and c % 2 == 0:
return True
elif a % 2 == 1 and b % 2 == 1 and c % 2 == 1:
return True
return False
numbers1 = [2, 1, 3, 5]
numbers2 = [2, 1, 2, 5]
numbers3 = [2, 4, 2, 5]
print(numbers1, hasThreeContiguousEvenOrOdd(numbers1))
print(numbers2, hasThreeContiguousEvenOrOdd(numbers2))
print(numbers3, hasThreeContiguousEvenOrOdd(numbers3))
Output:
[2, 1, 3, 5] True
[2, 1, 2, 5] False
[2, 4, 2, 5] True
If you want to be more idiomatic or "pythonic" you can improve the code and do for example something like this:
def hasThreeContiguousEvenOrOdd(numbers):
for a, b, c in zip(numbers, numbers[1:], numbers[2:]):
if a % 2 == b % 2 == c % 2:
return True
return False
One more short and generic solution:
def consecutive_evenodd(lst, n=3):
n_uplets = ( lst[i:i+n] for i in range(len(lst)-n+1) )
return any( sum(el % 2 for el in n_uplet) % n == 0 for n_uplet in n_uplets )
# test
inputs = [[2, 1, 3, 5],
[2, 1, 2, 5],
[2, 4, 2, 5]]
for lst in inputs:
print(lst, consecutive_evenodd(lst))
output:
[2, 1, 3, 5] True
[2, 1, 2, 5] False
[2, 4, 2, 5] True
Some explanation: The sum(...) part gives the number of odd number in each n-uplet. When all numbers are odd or even, this sum is equal to either n or zero. For the other cases, the result of the sum falls in-between. So, sum(...) % n is equal to 0 only when all the n-uplet numbers are either odd or even.
If you are looking for exact 3 of odd or even in a list of 4 element:
def ex(l):
return sum([1 if item%2 ==0 else -1 for item in l]) in [2, -2]
ex([2, 1, 3, 5]) # True
ex([2, 1, 2, 5]) # False
ex([2, 4, 2, 5]) # True
And if list length and number of odd or even elements are parameters you can change 2 and -2 to a parameter.
one way with two int counters:
Time: O(n)
space: O(1)
function:
def chk(alist):
odd=even=0
for n in alist:
if n % 2:
odd += 1
even = 0
else:
even += 1
odd = 0
if odd>2 or even>2:
return True
return False
3 lists from your example output True False True. (tested with python2)
Tried a simple way to do it. Maintain two flags for even and odd. If you find an even, increment the even flag and reset the odd flag, and vice versa. As soon as one flag's value becomes 3, you know you have found three in succession.
evenodd = []
while True:
try:
n = int(input())
evenodd.append(n)
except:
break
evenflag = 0
oddflag = 0
print(evenodd)
for number in evenodd:
if number % 2 == 0:
evenflag += 1
oddflag = 0
else:
oddflag += 1
evenflag = 0
if evenflag == 3:
print("Found 3 consecutive even numbers")
break
elif oddflag == 3:
print ("Found three consecutive odd numbers")
break
Use zip to create n element tuples (3 in our case), the use all(map(lambda x: x%2, sl) to find if all the 3 elements in the tuples are odd and then use any to check if there is atleast one match.
Using any and all would guarantee minimal number of odd or even checks needed
>>> n =3
>>> three_odds_or_even = lambda lst: any(all(map(lambda x: x%2, sl)) or all(map(lambda x: not x%2, sl)) for sl in zip(*(lst[i:] for i in range(n))))
>>> three_odds_or_even([2, 1, 3, 5])
True
>>> three_odds_or_even([2, 1, 2, 5])
False
>>> three_odds_or_even([2, 4, 2, 5])
True
>>>
Here is a solution using a simple for loop.
The idea is to maintain a list, whose values depend on whether elements in the input list are even or odd. Clear the list when you don't have a consecutive odd/even.
If at any point you have 3 items in your tracker list, the function returns True.
L1 = [2, 1, 3, 5]
L2 = [2, 1, 2, 5]
L3 = [2, 4, 2, 5]
def even_odd_consec(x, k=3):
lst = []
for item in x:
is_odd = item % 2
if not (lst and (lst[-1] == is_odd)):
lst.clear()
lst.append(is_odd)
if len(lst) == k:
return True
return False
even_odd_consec(L1, 3) # True
even_odd_consec(L2, 3) # False
even_odd_consec(L3, 3) # True
Just checks that odd/even holds as long as the minimum sequence length requested, 3 in this case.
def determine(list_, in_a_row=3):
prv, cntr = None, 1
for v in list_:
cur = bool(v % 2)
if prv is cur:
cntr += 1
else:
cntr = 1
prv = cur
if cntr >= in_a_row:
return True
return False
dataexp = [
([2, 1, 3, 5],True),
([2, 1, 2, 5],False),
([2, 4, 2, 5],True),
#hmmm, you're able to handle lists smaller than 3, right?
([],False),
]
for li, exp in dataexp:
got = determine(li)
msg = "exp:%s:%s:got for %s" % (exp, got, li)
if exp == got:
print("good! %s" % msg)
else:
print("bad ! %s" % msg)
output
good! exp:True:True:got for [2, 1, 3, 5]
good! exp:False:False:got for [2, 1, 2, 5]
good! exp:True:True:got for [2, 4, 2, 5]
good! exp:False:False:got for []
I am trying to print the path for the LowCost script I found here the code
Find LowCost I change some small stuff only.
The script works but it gives me only the final cost; I want it also to give me the path I have taken.
Here is an example:
[ [1, 2, 3],
[4, 8, 2],
[1, 5, 3] ]
so here how look like the path its get the 1,2,2,3:
1-2 3
.....\
4 8 2
......|
1 5 3
i want to print correc the path like Correct
path : [1,2,2,3]
low cost : 8
Now I get a very big path result and it's no correct Wrong !
path : [1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1, 5, 5, 5]
low cost : 8
Here is the code:
import sys
def mymin(a,b,c):
return min(min(a,b),c)
def minCostMain(cost, m, n):
result = []
def minCost(cost, m, n):
if (n < 0 or m < 0):
return sys.maxsize
elif (m == 0 and n == 0):
return cost[m][n]
else:
t1= minCost(cost, m-1, n-1)
t2= minCost(cost, m-1, n)
t3= minCost(cost, m, n-1)
v=mymin(t1,t2,t3)
#this dosen't work get more items
result.append(v)
return cost[m][n] + v
return minCost(cost, m, n),result
cost= [ [1, 2, 3],
[4, 8, 2],
[1, 5, 3] ]
lowcost,path= minCostMain(cost, 2, 2)
print "path : " + str(path)
print "low cost : " + str(lowcost)
ok now understand how its working i need to keep list in function and the self function return list no value and after i add the new item :) like this now i can get the path and the map where it's in list the number 1,2,2,3
import sys
def MyPrint(mlist):
mlist[0]=str(mlist[0]) + " = (0,0)"
sum=0
for item in mlist:sum+=int(item.split(" = (")[0])
print "*"*20
print "Sum = "+str(sum)
print "*"*20
print "map"
print "*"*20
for item in mlist:print item
def Min(x,y, z):
if (x[-1] < y[-1]):
return x if (x[-1] < z[-1]) else z
else:
return y if (y[-1] < z[-1]) else z
def minCost(cost, m, n):
if (n < 0 or m < 0):
return [sys.maxsize]
elif (m == 0 and n == 0):
return [cost[m][n]]
else:
arr=Min(minCost(cost, m-1, n-1),
minCost(cost, m-1, n),
minCost(cost, m, n-1))
arr.append(str(cost[m][n])+" = ("+str(m)+","+str(n)+")")
return arr
cost= [ [1, 2, 3],
[4, 8, 2],
[1, 5, 3] ]
x=minCost(cost, 2, 2)
MyPrint(x)
the result it's
********************
Sum = 8
********************
map
********************
1 = (0,0)
2 = (0,1)
2 = (1,2)
3 = (2,2)