Related
I am given test cases but my codes comes up with solutions that aren't part of the solutions given. Also every-time I run the code I get a different solution. Any help debugging this will be greatly appreciated.
Here are the constraints:
Red - No constraints
Yellow - equals the rightmost digit of of the product of all its neighbors
Green - equals the rightmost digit of the sum of all its neighbors
Blue - equals the leftmost digit of the sum of all its neighbors
Violet - equals the leftmost digit of the product of all of its neighbors
Each node has a domain of {1,2,...,9}
My Code:
import random
def get_neighbors(node, arcs):
# Returns the neighbors of the given node
neighbors = []
for arc in arcs:
if arc[0] == node:
neighbors.append(arc[1])
elif arc[1] == node:
neighbors.append(arc[0])
return neighbors
def is_valid_coloring(node, value, node_values, arcs):
# Checks if the current node coloring satisfies the constraints
neighbors = get_neighbors(node, arcs)
color = node_values[node]
if color == 'Y':
product = 1
for neighbor in neighbors:
product *= node_values[neighbor]
return value == product % 10
elif color == 'G':
s = sum(node_values[neighbor] for neighbor in neighbors)
return value == s % 10
elif color == 'B':
sum = 0
for neighbor in neighbors:
sum += node_values[neighbor]
return value == sum % 10
elif color == 'V':
product = 1
for neighbor in neighbors:
product *= node_values[neighbor]
return value == product % 10
else:
return True
def select_unassigned_variable(node_values, nodes, arcs):
"""
Returns an unassigned node that has the most conflicts with its neighbors.
"""
unassigned_nodes = [i for i, val in enumerate(node_values) if val == 0]
max_conflicts = -1
max_conflict_nodes = []
for node in unassigned_nodes:
neighbors = get_neighbors(node, arcs)
node_conflicts = 0
for neighbor in neighbors:
if node_values[neighbor] != 0 and not is_valid_coloring(neighbor, node_values[neighbor], node_values, arcs):
node_conflicts += 1
if node_conflicts > max_conflicts:
max_conflicts = node_conflicts
max_conflict_nodes = [node]
elif node_conflicts == max_conflicts:
max_conflict_nodes.append(node)
if len(max_conflict_nodes) == 0:
return None
return random.choice(max_conflict_nodes)
def get_conflicts(node_values, node, arcs, nodes):
conflicts = 0
node_idx = node
for arc in arcs:
if node_idx == arc[1]:
if node_values[node_idx] == node_values[arc[0]]:
conflicts += 1
if node_idx == arc[0]:
if node_values[node_idx] == node_values[arc[1]]:
conflicts += 1
return conflicts
def min_conflicts(node_values, nodes, arcs, max_steps):
# Solves the csp using the mini conflicts algorithm
for step in range(max_steps):
unassigned_node = select_unassigned_variable(node_values, nodes, arcs)
if unassigned_node is None:
return node_values
domain = [i for i in range(1, 10)]
conflicts = [get_conflicts(node_values, unassigned_node, arcs, nodes)]
min_conflicts = float('inf')
min_conflict_values = []
for value in domain:
new_node_values = node_values.copy()
new_node_values[unassigned_node] = value
if is_valid_coloring(unassigned_node, value, new_node_values, arcs):
num_conflicts = get_conflicts(new_node_values, unassigned_node, arcs, nodes)
if num_conflicts < min_conflicts:
min_conflicts = num_conflicts
min_conflict_values = [value]
elif num_conflicts == min_conflicts:
min_conflict_values.append(value)
if min_conflict_values:
new_value = random.choice(min_conflict_values)
node_values[unassigned_node] = new_value
else:
# If there are no values that result in a minimum number of conflicts,
# choose a random value from the domain
new_value = random.choice(domain)
node_values[unassigned_node] = new_value
# If the new node values lead to an invalid coloring, try again with a different value
if not is_valid_coloring(unassigned_node, new_value, node_values, arcs):
node_values[unassigned_node] = random.choice([x for x in domain if x != new_value])
return None
def solve_csp(nodes, arcs, max_steps):
# Convert nodes to strings
nodes = [str(node) for node in nodes]
node_values = [0] * len(nodes)
return min_conflicts(node_values, nodes, arcs, max_steps)
def main():
# test Case 1
nodes = 'YGVRB'
arcs = [(0,1), (0,2), (1,2), (1,3), (1,4), (2,3), (2,4)]
max_steps = 1000
for _ in range(max_steps):
sol = solve_csp(nodes, arcs, max_steps)
if sol != []:
break
all_solutions = [[1, 1, 1, 7, 2],[2, 1, 2, 4, 3],[2, 6, 7, 6, 1],[2, 8, 9, 6, 1],
[3, 3, 1, 5, 4],[6, 2, 8, 7, 1],[6, 7, 8, 2, 1],[6, 9, 4, 8, 1]]
if sol == []:
print('No solution')
else:
if sol in all_solutions:
print('Solution found:', sol)
else:
print('ERROR: False solution found:', sol)
if __name__ == '__main__':
main()
Output:
ERROR: False solution found: [7, 4, 2, 1, 8]
I have also tried changing to a bigger step size and have not had any luck. I have double checked to make sure my constraints were accurate. Please let me know if there is something I have missed
Here is something to get you started on your bug-hunt.
The code below adds tests to your get_neighbors function using the doctest library. That function looks to be working correctly so the bug is most likely elsewhere. If you continue in this fashion adding simple tests to all of your functions and dividing the larger functions in more pieces you will eventually find the bug(s) in your code:
import random
import doctest
def get_neighbors(node, arcs):
"""
Returns the neighbors of the given node
>>> get_neighbors(0, [(0, 1), (0, 2), (1, 2), (2, 3), (4, 0)])
[1, 2, 4]
>>> get_neighbors(1, [(0, 1), (0, 2), (1, 2), (2, 3), (4, 0)])
[0, 2]
"""
neighbors = []
for arc in arcs:
if arc[0] == node:
neighbors.append(arc[1])
elif arc[1] == node:
neighbors.append(arc[0])
return neighbors
def is_valid_coloring(node, value, node_values, arcs):
# Checks if the current node coloring satisfies the constraints
neighbors = get_neighbors(node, arcs)
color = node_values[node]
if color == 'Y':
product = 1
for neighbor in neighbors:
product *= node_values[neighbor]
return value == product % 10
elif color == 'G':
s = sum(node_values[neighbor] for neighbor in neighbors)
return value == s % 10
elif color == 'B':
sum = 0
for neighbor in neighbors:
sum += node_values[neighbor]
return value == sum % 10
elif color == 'V':
product = 1
for neighbor in neighbors:
product *= node_values[neighbor]
return value == product % 10
else:
return True
def select_unassigned_variable(node_values, nodes, arcs):
"""
Returns an unassigned node that has the most conflicts with its neighbors.
"""
unassigned_nodes = [i for i, val in enumerate(node_values) if val == 0]
max_conflicts = -1
max_conflict_nodes = []
for node in unassigned_nodes:
neighbors = get_neighbors(node, arcs)
node_conflicts = 0
for neighbor in neighbors:
if node_values[neighbor] != 0 and not is_valid_coloring(neighbor, node_values[neighbor], node_values, arcs):
node_conflicts += 1
if node_conflicts > max_conflicts:
max_conflicts = node_conflicts
max_conflict_nodes = [node]
elif node_conflicts == max_conflicts:
max_conflict_nodes.append(node)
if len(max_conflict_nodes) == 0:
return None
return random.choice(max_conflict_nodes)
def get_conflicts(node_values, node, arcs, nodes):
conflicts = 0
node_idx = node
for arc in arcs:
if node_idx == arc[1]:
if node_values[node_idx] == node_values[arc[0]]:
conflicts += 1
if node_idx == arc[0]:
if node_values[node_idx] == node_values[arc[1]]:
conflicts += 1
return conflicts
def min_conflicts(node_values, nodes, arcs, max_steps):
# Solves the csp using the mini conflicts algorithm
for step in range(max_steps):
unassigned_node = select_unassigned_variable(node_values, nodes, arcs)
if unassigned_node is None:
return node_values
domain = [i for i in range(1, 10)]
conflicts = [get_conflicts(node_values, unassigned_node, arcs, nodes)]
min_conflicts = float('inf')
min_conflict_values = []
for value in domain:
new_node_values = node_values.copy()
new_node_values[unassigned_node] = value
if is_valid_coloring(unassigned_node, value, new_node_values, arcs):
num_conflicts = get_conflicts(new_node_values, unassigned_node, arcs, nodes)
if num_conflicts < min_conflicts:
min_conflicts = num_conflicts
min_conflict_values = [value]
elif num_conflicts == min_conflicts:
min_conflict_values.append(value)
if min_conflict_values:
new_value = random.choice(min_conflict_values)
node_values[unassigned_node] = new_value
else:
# If there are no values that result in a minimum number of conflicts,
# choose a random value from the domain
new_value = random.choice(domain)
node_values[unassigned_node] = new_value
# If the new node values lead to an invalid coloring, try again with a different value
if not is_valid_coloring(unassigned_node, new_value, node_values, arcs):
node_values[unassigned_node] = random.choice([x for x in domain if x != new_value])
return None
def solve_csp(nodes, arcs, max_steps):
# Convert nodes to strings
nodes = [str(node) for node in nodes]
node_values = [0] * len(nodes)
return min_conflicts(node_values, nodes, arcs, max_steps)
def main():
# test Case 1
nodes = 'YGVRB'
arcs = [(0,1), (0,2), (1,2), (1,3), (1,4), (2,3), (2,4)]
max_steps = 1000
for _ in range(max_steps):
sol = solve_csp(nodes, arcs, max_steps)
if sol != []:
break
all_solutions = [[1, 1, 1, 7, 2],[2, 1, 2, 4, 3],[2, 6, 7, 6, 1],[2, 8, 9, 6, 1],
[3, 3, 1, 5, 4],[6, 2, 8, 7, 1],[6, 7, 8, 2, 1],[6, 9, 4, 8, 1]]
if sol == []:
print('No solution')
else:
if sol in all_solutions:
print('Solution found:', sol)
else:
print('ERROR: False solution found:', sol)
if __name__ == '__main__':
doctest.testmod(verbose=True)
main()
Another possibility to debug graph based code is using the networkx package to visualize your graph visually to make the inconsistencies of your solution more intuitive.
You can choose different colors for different nodes, that will help you differentiate the different kinds of nodes in your project:
G.add_nodes_from([
(4, {"color": "red"}),
(5, {"color": "green"}),
])
I have list named li with values [1,2,4,5] and I want to return a new list with sum of odd numbers and even numbers like new_list = [6,6] where values add as 1+5 = 6 and 2+4 = 6. But, the output that I am receiving is [1]. Below is my code.
class Solution(object):
def calculate_odd_even(self, li):
even = 0
odd = 0
sum_num = []
for i in range(len(li)):
if li[i] % 2 == 0:
even += 1
sum_num.append(even)
else:
odd += 1
sum_num.append(odd)
return sum_num
if __name__ == "__main__":
p = Solution()
lit = [1, 2, 4, 5]
print(p.calculate_odd_even(lit))
There are several issues in your code.
you return too early (in the loop)
you add +1 instead of the value
you try to append to each loop (do it only in the end)
the order of the odd/even values depends on the input data (first one seen of odd/even will be first)
Other "minor" issue:
don't loop over the indices, loop over the values
class Solution(object):
def calculate_odd_even(self, li):
even = 0
odd = 0
sum_num = []
for x in li:
if x % 2 == 0:
even += x
else:
odd += x
sum_num.append(odd)
sum_num.append(even)
return sum_num
if __name__ == "__main__":
p = Solution()
lit = [1, 2, 4, 5]
print(p.calculate_odd_even(lit))
More simple variant:
class Solution(object):
def calculate_odd_even(self, li):
sum_num = [0, 0]
for x in li:
sum_num[1-x%2] += x # use sum_num[x%2] += x for even/odd order
return sum_num
if __name__ == "__main__":
p = Solution()
lit = [1, 2, 4, 5]
print(p.calculate_odd_even(lit))
In this problem I am trying to create a new list of length n from two lists of length n each. I randomly select a subset of the first parent list (using start and end variables) and add them to the new list in the same positions in which they appeared in their corresponding list. Then I populate the remainder of the new list with elements from the second parent list in the order in which they appear, without duplicating any element that was selected from the first parent. The image explains it.
Here is my python code: a plane here is weights for a Perceptron model.
def breed(plane1, plane2):
num_list = list(range(0, len(plane1)))
random.shuffle(num_list)
n1 = num_list[0]
n2 = num_list[1]
start = min(n1, n2)
end = max(n1, n2)
child = [None] * len(plane1)
for i in range(start, end):
child[i] = plane1[i]
idx = (end) % len(plane2)
for i in range(len(plane2)):
pos = (end + i) % len(plane2)
if plane2[pos] not in child:
child[idx] = plane2[pos]
idx = (idx + 1) % len(plane2)
return child
Can anyone recommend a different way that is efficient and concise?
Also, the end of random range is not included in the selection:
one = [1,2,3,4,5,6,7,8,9]
two = [9,8,7,6,5,4,3,2,1]
child = breed(one, two)
print(child)
start: 0
end: 7
Output:
[1, 2, 3, 4, 5, 6, 7, 9, 8]
Here's a solution. It could probably be better, not using a while loop would be more elegant. I don't know if it covers all edge cases. I've split out the logic from the randomly generated numbers to make it easier to test.
import random
def breed(plane1, plane2):
assert len(plane1) == len(plane2)
istart = random.randint(0, len(plane1) - 2)
iend = random.randint(istart + 1, len(plane1) - 1)
print(f"random indices: {istart} to {iend}")
return generate_breed(plane1, plane2, istart, iend)
def generate_breed(plane1, plane2, istart, iend):
child = [-1 for _ in plane1]
child[istart : iend + 1] = plane1[istart : iend + 1]
i = j = 0
while True:
if j == istart:
j = iend + 1
if j >= len(child):
break
if plane2[i] not in child:
child[j] = plane2[i]
j += 1
i += 1
return child
if __name__ == "__main__":
p1, p2 = [1, 2, 3, 4, 5, 6], [7, 3, 2, 1, 2, 6]
start, end = 2, 4
assert generate_breed(p1, p2, start, end) == [7, 2, 3, 4, 5, 1]
assert generate_breed([1, 2, 3], [4, 2, 1], 0, 2) == [1, 2, 3]
# call like this, but answer is unpredictable due to randint call
print(breed([1, 2, 3], [4, 2, 1]))
Edit
Original answer was super wrong, after iterating over this for a while, this works, assuming lists don't have duplicates in themselves.
Magic Numbers
With generating the start and end, the magic -2 and magic +1 can be tuned if you want to set a min length. I set the min length here to 1.
def breed(plane1, plane2):
i = randint(0, len(plane1) - 2)
j = randint(i + 1, len(plane1))
plane2 = [x for x in plane2 if x not in plane1[i:j]]
child = plane2[0:i] + plane1[i:j] + plane2[i:len(plane1) - (j - i)]
return child
So I have stock = [5,6,8,4,8,3,6,4]. I want to get the index of the greatest element adjacent to the 1st occurrence of the greatest element, 8. So what I want to get will be 6 with index 1. I have tried using this code.
closest = min(range(len(stock)), key=lambda i: abs(stock[i]-max(stock)))
but it just returns the max element.
If I understood your problem correctly, the most interesting input would look like [1,5,8,4,8,7,6,4]. I.e. we need to return the index of the 5 since it's a second maximum closest to the first occurrence of the maximum. If so, then the algorithm would look as follows:
find two leftmost and absolute maximums m1 and m2
if m1 == m2 then the target is in either of two subarrays:
[0, pos(m1))
[pos(m1) + 1, pos(m2))
otherwise, the target is in either of the following subarrays:
[0, pos(m1))
[pos(m1) + 1, len(arr))
We can find k max elements in an array in a close to linear time using the binary heap. So, I think I got a linear solution for you:
import heapq
def nlargest_with_pos(n, arr):
assert len(arr) >= n
largest = heapq.nlargest(n, ((it[1], -it[0]) for it in enumerate(arr)))
return [(it[0], -it[1]) for it in largest]
def find_x(arr):
assert len(arr) > 1
first_max, second_max = nlargest_with_pos(2, arr)
if len(arr) == 2:
return second_max[1]
left_range = (0, first_max[1])
if second_max[0] == first_max[0]:
right_range = (first_max[1] + 1, second_max[1])
else:
right_range = (first_max[1] + 1, len(arr))
left_hand = arr[left_range[0]:left_range[1]]
right_hand = arr[right_range[0]:right_range[1]]
if not left_hand:
return nlargest_with_pos(1, right_hand)[0][1]
if not right_hand:
return nlargest_with_pos(1, left_hand)[0][1]
left_second_max = nlargest_with_pos(1, left_hand)[0]
right_second_max = nlargest_with_pos(1, right_hand)[0]
if left_second_max[0] >= right_second_max[0]:
return left_second_max[1]
else:
return right_second_max[1]
print(find_x([1,5,8,4,8,7,6,4]))
Like this:
stock = [5,6,8,7,8,3,6,4]
if stock.index(max(stock)) == len(stock)-1:
print(len(stock)-2)
elif not stock.index(max(stock)):
print(1)
elif stock[stock.index(max(stock))-1] > stock[stock.index(max(stock))+1]:
print(stock.index(max(stock))-1)
else:
print(stock.index(max(stock))+1)
Output:
1
Not very elegant but this should work nonetheless:
stock.index(max(stock[stock.index(max(stock)) - 1], stock[(stock.index(max(stock)) + 1) % len(stock)]))
You'll have to add handling if there's a chance you see a list with less than three values
This prints index of highest element that's next to the first occurrence of maximum value in list stock:
stock = [1,5,8,4,8,7,6,4]
idx_max = stock.index(max(stock))
print(max([i for i in [idx_max-1, idx_max+1] if -1 < i < len(stock)], key=lambda k: stock[k]))
Prints:
1
Test cases:
stock = [8,3,8,4,8,3,6,4] # 1
stock = [1,3,1,3,8,5,6,4] # 5
stock = [1,3,1,4,1,3,6,8] # 6
stock = [1,5,8,4,8,7,6,4] # 1
Here's one way:
def get_max_neighbour(l):
_max = max(l)
excl = (-1, len(l))
neighbour = max(
(
(l[j], j)
for i in range(excl[-1])
for j in (i-1, i+1)
if l[i] == _max and j not in excl
),
key=lambda x: x[0]
)
return neighbour[1]
Result:
1
The nice thing about this is you can return both the value and index if you want.
Here's my solution to this puzzle. I'd say it is most similar to the solution of #DavidBuck, in that [8] -> -1 and [] -> None, but has four fewer exit points:
from math import inf
def index_of_max_neighbor_of_max(array):
if not array:
return None
index = array.index(max(array))
a = array[index - 1] if index - 1 >= 0 else -inf
b = array[index + 1] if index + 1 < len(array) else -inf
return index + (b > a) * 2 - 1
And my test code:
if __name__ == "__main__":
iomnom = index_of_max_neighbor_of_max
print(iomnom([5, 6, 8, 4, 8, 3, 6, 4])) # 1
print(iomnom([5, 6, 8, 4, 8, 3, 6, 9])) # 6
print(iomnom([5, 3])) # 1
print(iomnom([3, 5])) # 0
print(iomnom([8])) # -1
print(iomnom([])) # None
print(iomnom([5, 6, 8, 7, 8, 3, 6, 4])) # 3
print(iomnom([5, 6, 8, 4, 8, 3, 6, 9])) # 6
print(iomnom([5, 4, 8, 6, 8, 3, 6, 4])) # 3
def max_neighbor_index(l: list):
max_number = max(l)
max_number_indexes = [x for x in range(0, len(l)) if l[x] == max_number]
result = []
for number_index in max_number_indexes:
if number_index == 0:
result.append(1)
elif number_index == len(l) - 1:
result.append(len(l) - 2)
else:
result.append(l.index(max([l[number_index - 1], l[number_index + 1]])))
max_neighbor = max([l[x] for x in result])
return [x for x in result if l[x] == max_neighbor][0]
stock = [5, 6, 8, 4, 8, 3, 6, 4]
print(max_neighbor_index(stock))
1
stock = [5,6,8,4,8,3,6,4]
idx = 1
tmp,nxt = stock[0:2]
for i in range(1, len(stock)):
buf = stock[i-1] if i == len(stock)-1 else max(stock[i-1], stock[i+1])
if tmp < stock[i] or (tmp == stock[i] and nxt < buf):
idx = stock.index(buf, i-1)
nxt = stock[idx]
tmp = stock[i]
print('greatest next to greatest', nxt, 'at', idx)
Not very pretty, but it will cater for all possible scenarios, i.e. your list, max value a the start or the end, two or one value list.
Code is:
def highest_neighbour(stock):
if stock:
x = stock.index(max(stock))
if x - 1 >= 0:
if x + 1 < len(stock):
if stock[x + 1] > stock[x - 1]:
return x + 1
return x - 1
elif x + 1 < len(stock):
return x + 1
else:
return -1
I've set it to return -1 if the list only has one entry.
Output is:
highest_neighbour([5,6,8,4,8,3,6,4]) # -> 1
highest_neighbour([5,6,8,4,8,3,6,9]) # -> 6
highest_neighbour([9,6,8,4,8,3,6,4]) # -> 1
highest_neighbour([3,5]) # -> 0
highest_neighbour([8]) # -> -1
highest_neighbour([]) # -> None
I've tried to find the sub-array(s) from a given which contain elements of maximum sum than any other sub array.
Below function has parameter as input a and the output needs to be returned. There can be more than one subarray as their maximum sum can be equal. The code did not seem to be working as expected.
def max_sum_subarray(a):
N, sub_sum, max_sum, subArrays = len(a), 0, 0, {}
p,q=0,0 #starting and ending indices of a max sub arr
for i in range(N):
q=i
sub_sum+=a[i]
if(a[i]<0):
q-=1
if(sub_sum>=max_sum):
if(sub_sum>max_sum):
subArrays.clear()
subArrays[sub_sum]=[(p,q)]
else:
subArrays[sub_sum].append((p,q))
sub_sum=0
p=i+1
if(sub_sum>=max_sum):
if(sub_sum>max_sum):
subArrays.clear()
subArrays[sub_sum]=[(p,q)]
else:
subArrays[sub_sum].append((p,q))
return(subArrays[p:q+1])
When I tried to run for input
a=[ 1, 2, 5, -7, 2, 5 ]
Expected output is [1, 2, 5] but it gave [2, 5] instead. Can anyone please post the solution in python?
It seems like you making this harder than necessary. You can just keep track of max array seen to far and the current one you're pushing into -- you don't really need to care about anything else. When you hit a negative (or the end of the array) decide if the current should be the new max:
def maxSub(a):
max_so_far = []
max_sum = 0
cur = []
for n in a:
if n >= 0:
cur.append(n)
else:
cur_sum = sum(cur)
if cur_sum > max_sum:
max_sum = cur_sum
max_so_far = cur
cur = []
return max([max_so_far, cur], key = sum)
a=[ 1, 2, 5, -7, 2, 5 ]
maxSub(a)
# [1, 2, 5]
Of course itertools.groupby makes this a one-liner:
from itertools import groupby
a=[ 1, 2, 5, -7, 2, 5 ]
max([list(g) for k,g in groupby(a, key=lambda x: x>0) if k == True], key=sum)
For the following conditions:
NOTE 1: If there is a tie, then compare with segment’s length and
return segment which has maximum length
NOTE 2: If there is still a tie, then return the segment with minimum
starting index
Here is my working code in python:
def check(max_arr,curr):
if sum(curr) > sum(max_arr):
max_arr = curr
elif sum(curr) == sum(max_arr):
if len(curr) > len(max_arr):
max_arr = curr
elif len(curr) == len(max_arr):
if max_arr and (curr[0] > max_arr[0]):
max_arr = curr
return max_arr
def maxset(A):
curr = []
max_arr = []
for i in A:
if i >= 0:
curr.append(i)
else:
max_arr = check(max_arr,curr)
curr = []
max_arr = check(max_arr,curr)
return max_arr