Related
import numpy as np
m1 = np.arange(1,10).reshape(3,3)
diagonal = np.diag(m1)
antdiagonal =[]
for j in range(0,3):
x = m1[j][3-1-j]
antdiagonal.append(x)
def common_data(list1, list2):
result = False
for x in list1:
for y in list2:
if x == y:
result = True
return result
if(common_data(list(diagonal), list(antdiagonal))):
print("hitter")
else:
print("Non-hitter")
In the above code snippet , the Matrix (m1) will be considered as “hitter” if any integer is repeating in both the principal diagonal and the anti-diagonal of m1. Otherwise should print “non hitter”. The principal diagonal of the above matrix(m1) is {1,5,9} and the principle antidiagonal will be {3,5,7}. and For the given matrix(m1) the output will be “non hitter”.
Please modify the above code to get the result.
i have tried with above code snippet but missing the logic for displaying "hitter" or "non-hitter"
pdiagonal = []
pantidiagonal = []
# for Principal Diagonal
def getPrincipalDiagonal(mat, n):
for i in range(n):
for j in range(n):
if (i == j):
pdiagonal.append(mat[i][j])
# for Anti-Diagonal
def getSecondaryDiagonal(mat, n):
for i in range(n):
for j in range(n):
if ((i + j) == (n - 1)):
pantidiagonal.append(mat[i][j])
#for "non-hitter" matrix
n = 3
a = [[1,2,3],[4,5,6],[7,8,9]]
#for "hitter" matrix
# n = 4
# a = [[2, 2, 3, 4 ],[5, 6, 7, 8 ],[1, 2, 3, 4 ],[6, 6, 7, 8 ]]
getPrincipalDiagonal(a, n)
getSecondaryDiagonal(a, n)
print("Principal Digonal : ", pdiagonal,"Principal Anti-Diagonal", pantidiagonal)
if len(set(pdiagonal).intersection(pantidiagonal)) > 1:
print("Result: hitter")
else:
print("Result: non-hitter")
NOTE: Based on your description, here i have termed a matrix as "hitter" if more than one element will be common, and "non-hitter" otherwise.
I have a problem that asks me to write a program that has a defined list (as the argument) and will then find all runs of Y consecutive numbers that increase or decrease by 1. It will then return the list of indices of the first element of each of these runs.
For example, if Y = 3, then l1 = [1,2,3,5,10,9,8,9,10,11,7,8,7] returns [0,4,6,7]. I got the code working somewhat, but it only works if I 'hard code' how many numbers I am checking. For example, if Y = 3, then my IF statement is checking l1[i], l1[i] + 1 and l1[i] + 2). I need it to work dynamically no matter what I set Y to.
Below is my code with my 'base logic':
A = [1,2,3,5,10,9,8,9,10,11,7,8,7]
new_indices = []
for index, number in enumerate(A[:-2]):
urgent_number = abs(A[index + 1])
next_number = abs(A[index + 2])
if (number + 1 == urgent_number and number + 2 == next_number) or (number - 1 == urgent_number and number - 2 == next_number):
new_indices.append(index)
print(new_indices)
Below is my attempt to try and incorporate a while loop to do this dynamically. I'm close, but I just can't seem to figure it out:
A = [1,2,3,5,10,9,8,9,10,11,7,8,7]
Y = 3 #length of list
new_indices = []
for index, number in enumerate(A[:-2]):
urgent_number = abs(A[index + 1])
next_number = abs(A[index + 2])
x = 0
while x < Y:
if (number + 1 == urgent_number and number + 2 == next_number) or (number - 1 == urgent_number and number - 2 == next_number):
new_indices.append(index)
x += 1
print(new_indices)
prints out [0,0,0,4,4,4,6,6,6,7,7,7]. However, I'm looking for [0,4,6,7].
Since you've gotten all the start indices and just need to remove the duplicates, you can do:
from itertools import groupby
original_result = [0,0,0,4,4,4,6,6,6,7,7,7]
final_result = [key for key, _ in groupby(original_result)]
[key for key, _ in groupby(original_result)]
to get the desired output.
This outputs:
[0, 4, 6, 7]
If you need to deal with variable run length specifically, you can find all the valid list slices that could make up a run, and then validate whether or not that slices makes up a run:
A = [1,2,3,5,10,9,8,9,10,11,7,8,7]
RUN_LENGTH = 3
indices = []
for s in range(len(A) - RUN_LENGTH):
is_incrementally_increasing = \
all(i2 - i1 == 1 for i1, i2 in zip(A[s:s+RUN_LENGTH], A[s+1:s+RUN_LENGTH]))
is_incrementally_decreasing = \
all(i1 - i2 == 1 for i1, i2 in zip(A[s:s+RUN_LENGTH], A[s+1:s+RUN_LENGTH]))
if is_incrementally_increasing or is_incrementally_decreasing:
indices.append(s)
print(indices)
This also outputs:
[0, 4, 6, 7]
This also works:
def dynamical(A):
new_indices = []
for index, number in enumerate(A[:-2]):
urgent_number = abs(A[index + 1])
next_number = abs(A[index + 2])
if (number + 1 == urgent_number and number + 2 == next_number) or (number - 1 == urgent_number and number - 2 == next_number):
new_indices.append(index)
return new_indices
Let A, B and C be three arrays, each containing N numbers:
A = a[0], a[1], a[2], ..., a[N-1]
B = b[0], b[1], b[2], ..., b[N-1]
C = c[0], c[1], c[3], ..., c[N-1]
I want to select the best k < N elements from A and the best k < N elements from B so that the total sum of their values is maximized. The interesting twist is: If element i is chosen from both A and B (where i in {0, ..., N-1} is the index), then instead of these elements contributing a[i] + b[i], they will contribute c[i] where c[i] >= a[i] + b[i].
At first glance this looked deceptively straightforward to me, but the more I think about the more involved it gets.
I am ultimately looking for an implementation in Python, but at this stage I am just trying to get a sense of what would be an efficient algorithm here.
Example
To clarify, inputs to the algorithm are the 3 N x 1 arrays A, B and C and an integer value for k. The expected output are two k x 1 lists of indices, defining the value-maximizing combination of elements from A and B (and C).
For example, suppose k = 2, N = 4 and let
A = a[0], a[1], a[2], a[3] = 3, 1, 1, 0
B = b[0], b[1], b[2], b[3] = 1, 3, 0, 1
C = c[0], c[1], c[2], c[3] = 4, 4, 3, 2
Even in this simple example, there are many possible combinations. For instance, if elements i = 0, 2 are chosen from A and elements j = 1, 3 are chosen from B, then the total value would be a[0] + a[2] + b[1] + b[3] = 8.
If on the other hand elements i = 0, 1 and j = 0, 1 would be chosen from both A and B, then the special twist applies: Instead of yielding a[0] + a[1] + b[0] + b[1], the total value is given by c[0] + c[1] = 8.
In this example, the combination of elements that maximizes the total value is given by i = 0, 2 from A and elements j = 1, 2 from B. This yields a total value of a[0] + b[1] + c[2] = 9, which can be verified is more than any other combination.
Comparison of answers
Here's a quick comparison of the 3 submitted solutions. First, I checked all of them, and they all give the intended results. As a side comment, none of them requires the elements of C to be weakly larger than the sum of the corresponding elements in A and B, so I dropped this assumption in my performance review.
Here's what I run:
import numpy as np
from utils import tic, toc # simple wrapper to time.perf_counter()
k, N = 10, 1000
A = list(np.random.random_sample([N]))
B = list(np.random.random_sample([N]))
C = list(np.random.random_sample([N]))
tic()
print(optimal_choices(k, A, B, C)) # solution by btilly
toc()
tic()
print(maxPicks(A.copy(), B.copy(), C.copy(), k)) # solution by Eric T-M
toc()
tic()
print(maxSum(A, B, C, k)) # solution by Alain T.
toc()
I tested for various combinations of k and N. It seems that #btilly's algorithm scales well in N as long as k is small. #Alain-T.'s algorithm does the opposite, doing well when k is large relative to N. Across the board, #Eric-T-M's algorithm does best, scaling well in both k and N.
Small problem: k = 10 and N = 500
btilly's algorithm: 0.49s
Eric T-M's algorithm: 0.00s
Alain T.'s algorithm: 0.52s
Small-k, large-N: k = 10 and N = 1000
btilly's algorithm: 0.89s
Eric T-M's algorithm: 0.00s
Alain T.'s algorithm: 1.99s
Large-k, small-N: k = 80 and N = 100
btilly's algorithm: 1.54s
Eric T-M's algorithm: 0.00s
Alain T.'s algorithm: 0.09s
Medium problem: k = 50 and N = 1000
btilly's algorithm: 13.01ss
Eric T-M's algorithm: 0.00s
Alain T.'s algorithm: 8.55s
Large problem 1: k = 10 and N = 1_000_000
Eric T-M's algorithm: 1.03s
Large problem 2: k = 1_000 and N = 100_000
Eric T-M's algorithm: 10.22s
(For the benchmarks, I removed the sorting in Alain T.'s code, to make it comparable.)
Try this. It takes O(N^2) time and it is fairly simple.
def maxPicks(A,B,C,k):
# returns the tuple (list of entries picked in A, list of entries picked in B, total value)
# BASE CASE
if k == 0:
return ([], [], 0)
aMax = max(A)
bMax = max(B)
cMax = max(C)
if (aMax + bMax) > cMax:
aIdx = A.index(aMax)
bIdx = B.index(bMax)
B[aIdx] = C[aIdx] - A[aIdx]
A[aIdx] = -2
C[aIdx] = -1
A[bIdx] = C[bIdx] - B[bIdx]
B[bIdx] = -2
C[bIdx] = -1
nextPicks = maxPicks(A,B,C,k-1)
return (nextPicks[0] + [aIdx], nextPicks[1] + [bIdx], nextPicks[2] + aMax + bMax)
else:
cIdx = C.index(cMax)
A[cIdx] = -1
B[cIdx] = -1
C[cIdx] = -1
nextPicks = maxPicks(A,B,C,k-1)
return (nextPicks[0] + [cIdx], nextPicks[1] + [cIdx], nextPicks[2] + cMax)
Here's how it works:
The base case should be self explanatory. Otherwise we will compare the sum of the maximum of all entries in A and the maximum of all entries in B to the maximum of all entries in C. If this sum is larger than it is safe to pick these entries from A and B, but before making more picks we will need to set the entries we picked as well as their corresponding entries in C to a negative value. As a side note I do assume that all values in A, B and C are originally nonnegative so by setting them negative we forbid our algorithm from picking them again. If this assumption is wrong you might want to set these values to something extremely negative to prohibit double picks. We also see that if we picked A[i] the value of B[i] is now whatever C[i]-A[i] was, because picking B[i] will lose us the value in A[i] and give us the value in C[i] same for the entry A[j] if we pick B[j].
If on the other hand, the greatest entry in C was greater than or equal to aMax+bMax we want to pick it (by picking the corresponding entries in both A and B, because no other picks of entries in A and B or just C alone would be more valuable. At this point we know we don't want to re-pick A[i],B[i], or C[i] again, so we set them all negative.
This can be solved with dynamic programming.
# Helper function to get out of the data structure.
def get_nested_array (data, path):
for x in path:
if data is None or len(data) <= x:
return None
else:
data = data[x]
return data
# Helper function to set data in the data structure.
def set_nested_array (data, path, value):
# Navigate there
for x in path[0:len(path)-1]:
while len(data) <= x:
data.append([])
if data[x] is None:
data[x] = []
data = data[x]
while len(data) <= path[-1]:
data.append(None)
data[path[-1]] = value
# Is this option better than what is there? If so, then add it.
def possibly_add_choice (best_choice, pos, i, j, current_sum, last_i, last_j):
best_prev = get_nested_array(best_choice, [pos, i, j])
if best_prev is None or best_prev[0] < current_sum:
set_nested_array(best_choice, [pos, i, j], (current_sum, last_i, last_j))
# Our program.
def optimal_choices (k, A, B, C):
# best_choice[pos][i][j] = (max_sum, last_a, last_b)
# where:
# We have made all choices in the range 0..pos-1
# We chose i out of A
# We chose j out of B
# and
# max_sum is the best possible sum
# last_a is the last index chosen from a
# last_b is the last index chosen from b
# then we can find the answer working backwards from
# best_choice[len(A)][k][k]
#
best_choice = []
# Enter the empty set answer
set_nested_array(best_choice, [0, 0, 0], (0, None, None))
for pos in range(len(A)):
best_choice.append([])
best_choice_for_pos = best_choice[pos]
for i in range(k+1):
if len(best_choice_for_pos) <= i:
break
best_choice_for_i = best_choice_for_pos[i]
for j in range(k+1):
if len(best_choice_for_i) <= j:
break
last_sum, last_i, last_j = best_choice_for_i[j]
# Try all 4 things we can choose here. Nothing, or A or B or both.
possibly_add_choice(best_choice, pos+1, i, j, last_sum, last_i, last_j)
possibly_add_choice(best_choice, pos+1, i+1, j, last_sum + A[pos], pos, last_j)
possibly_add_choice(best_choice, pos+1, i, j+1, last_sum + B[pos], last_i, pos)
possibly_add_choice(best_choice, pos+1, i+1, j+1, last_sum + C[pos], pos, pos)
# Now we have the answer, it is just a question of decoding it.
if get_nested_array(best_choice, [len(A), k, k]) is None:
return (None, None)
else:
choose_a = []
choose_b = []
best_spot = [len(A), k, k]
max_sum, last_i, last_j = get_nested_array(best_choice, best_spot)
while last_i is not None or last_j is not None:
# Figure out where we last had a choice and what was chosen.
if last_i is None:
last_pos = last_j
i_dec = 0
j_dec = 1
elif last_j is None:
last_pos = last_i
i_dec = 1
j_dec = 0
else:
last_pos = max(last_i, last_j)
i_dec = 0
j_dec = 0
if last_pos == last_i:
i_dec = 1
if last_pos == last_j:
j_dec = 1
# record the choice.
if 1 == i_dec:
choose_a.append(last_pos)
if 1 == j_dec:
choose_b.append(last_pos)
# Go back to that spot
max_sum, last_i, last_j = get_nested_array(best_choice, [last_pos, k-len(choose_a), k-len(choose_b)])
# We walked backwards to generate these lists. So they are currently reversed.
return (list(reversed(choose_a)), list(reversed(choose_b)))
print(optimal_choices(2, [3, 1, 1, 0 ], [1, 3, 0, 1], [4, 4, 3, 2]))
If you expand A and B into a list of index pairs with their respective sums (applying exceptions from C), you can iteratively take the maximum sum and exclude the corresponding pairs at each step. This will select the highest possible totals from the remaining pairs at each iteration:
def maxSum(A,B,C,K):
S = [ ([a+b,C[i]][i==j],i,j) for i,a in enumerate(A)
for j,b in enumerate(B)]
usedA,usedB = set(),set()
for _ in range(K):
_,i,j = max(s for s in S if not(s[1] in usedA or s[2] in usedB))
usedA.add(i)
usedB.add(j)
return sorted(usedA),sorted(usedB)
output:
A = [3, 1, 1, 0]
B = [1, 3, 0, 1]
C = [4, 4, 3, 2]
print(maxSum(A,B,C,2)) # ([0, 2], [1, 2]) total is 9
A = [1,2,3,4]
B = [4,5,6,2]
C = [5,9,9,6]
print(maxSum(A,B,C,2)) # ([1, 3], [1, 2]) total is 19
print(maxSum(A,B,C,3)) # ([1, 2, 3], [0, 1, 2]) total is 26
Wrote a simple code for left array rotation, getting the same array without any Rotation done to it as the wrong output.
def leftRotate(arr, d, n):
while (d-1) > 0:
leftRotatebyOne(arr, n)
def leftRotatebyOne(arr, n):
temp = arr[0]
for i in range(n-1):
arr[i] = arr[i + 1]
arr[n - 1] = temp
def PrintArray(arr, size):
for i in range(size):
print("%d" % arr[i], end=" ")
arr = []
l = int(input("Enter the number of elements: "))
for i in range(0, l):
ele = int(input())
arr.append(ele)
d = int(input("Enter the number of rotations: "))
n = len(arr)
leftRotate(arr, d, n)
PrintArray(arr, n)
and here's an example of the output i've got,
Enter the number of elements: 3
1
2
3
Enter the number of rotations: 1
1 2 3
I expected an output of 2 3 1 after one rotation.
I would suggest using array slicing, then adding the slices together, to perform rotation.
def left_rotate(data, num):
return data[num:] + data[:num]
def right_rotate(data, num):
return data[-num:] + data[:-num]
For example
>>> a = [1,2,3,4,5,6,7]
>>> left_rotate(a, 2)
[3, 4, 5, 6, 7, 1, 2]
>>> right_rotate(a, 2)
[6, 7, 1, 2, 3, 4, 5]
Also note that collections.deque has this behavior available already
>>> from collections import deque
>>> d = deque([1,2,3,4,5,6,7])
>>> d.rotate(2)
>>> d
deque([6, 7, 1, 2, 3, 4, 5])
>>> d.rotate(-2)
>>> d
deque([1, 2, 3, 4, 5, 6, 7])
In the function leftRotate,
there is an error in while loop.
Replace
while (d-1) > 0:
leftRotatebyOne(arr, n)
with
while d > 0:
leftRotatebyOne(arr, n)
d -= 1
When d == 1, while (d-1) > 0: will not be executed any time. Also, you never decrement d. The easiest way to solve is by using a for _ in range(d) loop:
def leftRotate(arr, d, n):
for _ in range(d):
leftRotatebyOne(arr, n)
NOTE: Python has way better ways to do rotations than this. This code seems to be C more than Python. Passing the array length makes no sense in Python for example. And the rotation can be done all in one assignation.
def leftRotate(arr, d):
d %= len(arr)
for _ in range(d):
arr[-1], arr[:-1] = arr[0], arr[1:]
Cory Kramer's answer is even more pythonic. But it has a bug and a difference with your question's methods. The bug is that it doesn't work when the number of rotations requested are higher than the length of the list. The difference is that they are returning a new list instead of modifying it. These two issues could be addresed like this:
def left_rotate(data, num):
num %= len(data)
data[:] = data[num:] + data[:num]
def right_rotate(data, num):
num %= len(data)
data[:] = data[-num:] + data[:-num]
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