How many steps to the nearest zero value - python

Looking for some help with solving a seemingly easy algorithm.
Brief overview:
We have an unsorted list of whole numbers. The goal is to find out how far each element of this list is from the nearest '0' value.
So if we have a list similar to this: [0, 1, 2, 0, 4, 5, 6, 7, 0, 5, 6, 9]
The expected result will be: [0, 1, 1, 0, 1, 2, 2, 1, 0, 1, 2, 3]
I've tried to simplify the problem in order to come up with some naive algorithm, but I can't figure out how to keep track of previous and next zero values.
My initial thoughts were to figure out all indexes for zeros in the list and fill the gaps between those zeros with values, but this obviously didn't quite work out for me.
The poorly implemented code (so far I'm just counting down the steps to the next zero):
def get_empty_lot_index(arr: list) -> list:
''' Gets all indices of empty lots '''
lots = []
for i in range(len(arr)):
if arr[i] == 0:
lots.append(i)
return lots
def space_to_empty_lots(arr: list) -> list:
empty_lots = get_empty_lot_index(arr)
new_arr = []
start = 0
for i in empty_lots:
steps = i - start
while steps >= 0:
new_arr.append(steps)
steps -= 1
start = i + 1
return new_arr

One possible algorithm is to make two sweeps through the input list: once forward, once backward. Each time retain the index of the last encountered 0 and store the difference. In the second sweep take the minimum of what was stored in the first sweep and the new result:
def space_to_empty_lots(arr: list) -> list:
result = []
# first sweep
lastZero = -len(arr)
for i, value in enumerate(arr):
if value == 0:
lastZero = i
result.append(i - lastZero)
# second sweep
lastZero = len(arr)*2
for i, value in reversed(list(enumerate(arr))):
if value == 0:
lastZero = i
result[i] = min(result[i], lastZero - i)
return result
NB: this function assumes that there is at least one 0 in the input list. It is not clear what the function should do when there is no 0. In that case this implementation will return a list with values greater than the length of the input list.

Related

Returning value from recursion

I was following a tutorial to All unique combinations whose sum equals to K from here. I need to tinker with the program to return the first unique combination found.
So I modified the code to return a final array which I pass in the combination_rec as well. Debugging the code in I see that the state of final is not maintained across the recursive stack, I get [] as the returned value.
import copy
def combination(arr, sum):
arr.sort(reverse=False)
local = []
res = combination_rec(0, 0, sum, local, arr, [])
if res:
return res
return []
def combination_rec(length, total, sum, local, arr, final):
if total == sum:
final = copy.copy(local)
return final
for i in range(length, len(arr), 1):
if total + arr[i] > sum:
continue
if (i > length and arr[i] == arr[i - 1]):
continue
local.append(arr[i])
final = combination_rec(i + 1, total + arr[i], sum, local, arr, final)
local.remove(local[len(local) - 1])
return final
if __name__ == "__main__":
combination([6, 5, 4, 3], 8)
Expected Output
--------
[3,5]
Actual Output
--------
[]
Summary of the program
Sorts the array - [3, 4, 4, 5, 6]
Loops over its elements to check each possible combination of elements which can sum up to K (8)
Begins with 3. Checks if subarray [3, 4] totals 8. It does not.
So it modifies the sub-array to include [3, 4, 5]. As this total (12) > 8
It goes back to line local.remove(local[len(local) - 1]) to remove 5 from sub-array and proceed to consider subsequent elements in the sorted array
Checks for [3,4,6].
Removes 6, followed by removal of 4.
Next check is for [3, 5]. This matches the total and this is when I wish to return final as [3, 5]
What is it I am doing wrong?
PS: I know that step 4 - the subsequent for loop iterations - is redundant. It does not make sense to check for [3,4,6] when [3,4,5] yielded a sum greater than it. But that's not my concern right. I am trying to understand returning values from recursion

Divide and conquer strategy python

I am trying to write a code which to compare each element of a list and give the index of the closet larger number in two direction: left or right. Using the divide and conquer method
For example:
Input: arr=[5,2,6,8,1,4,3,9]
Output:
Left=[None, 0, None, None, 3, 3, 5, None]
Right=[2, 2, 3, 7, 5, 7, 7, None]
Input: arr=[4,2,3,1,8,5,6,9]
Output:
L=[None, 0, 0, 2, None, 4, 4, None]
R=[4, 2, 4, 4, 7, 6, 7, None]
This is what I have now:
arr = [5,2,6,8,1,4,3,9]
def Left(arr):
L = []
for i in range(len(arr)):
flag = True
for j in range(i,-1,-1):
if (arr[i] < arr[j]):
L.append(j)
flag = False
break
if flag:
L.append(None)
return L
def Right(arr):
R = []
for i in range(len(arr)):
flag = True
for j in range(i, len(arr), 1):
if (arr[i] < arr[j]):
R.append(j)
flag = False
break
if flag:
R.append(None)
return R
print(*Left(arr), sep = ",")
print(*Right(arr), sep =",")
Am I doing it in a right way? Thank you.
This is my python version code for the algorithm in its "closest larger right" version.
Obviously, as you can see it is recursive. Recursion is really elegant but a bit tricky because few lines of code condense lots of concepts regarding to algorithms design and the own language they are coded. In my opinion 4 relevant moments are happening:
1) Recursive calls. Function is call to itself. During this step the list progresible slice into halves. Once the atomic unit is reached the base algorithm will be executed over them firstly (step 3). if not solution is reached greater list sizes will be involved in the calculation in further recursions.
2) Termination condition. Previous step is not run forever, it allows stop recursion and going to the next step (base algorithm). Now the code has len(arr) > 1: that means that the atomic unit will be pairs of numbers (or one of three in case of odd list). You can increase the number so that the recursive function will stay less time slicing the list and summarizing the results, but the counterpart is that in a parallelized environment "workers" will have to digest a bigger list.
3) Base algorithm. It makes the essential calculation. Whatever the size of the list, it returns the indexes of its elements to the right closest larger number
4) "Calculation saving". The base algorithm no need to calculated indexes on those numbers resolved in previous recursions. There is also a break to stop calculations once the number gets the index in the current recursion list.
Other algorithms models could be design, more efficient for sure. It occurs to me ones based on dictionaries or on different slicing strategies.
def closest_larger_right(arr):
len_total = len(arr)
result = [None] * len_total
def recursive(arr, len_total, position=0):
# 2) Termination condition
if len(arr) > 1:
mid = len(arr) // 2
left = arr[:mid]
right = arr[mid:]
position_left = 0 + position
position_right = len(left) + position
# 1) Recursive calls
recursive(left, len_total, position_left)
recursive(right, len_total, position_right)
# 3) Base algorithm
for i in range(len(arr)-1):
# 4) Calculation saving
if result[i + position] is None:
for j in range(i+1, len(arr), 1):
if (arr[i] < arr[j]):
result[i + position] = j + position
break
return result
return recursive(arr, len_total)
# output: [2, 2, 3, 7, 5, 7, 7, None]
print(closest_larger_right([5, 2, 6, 8, 1, 4, 3, 9]))
I am not sure how a divide-and-conquer algorithm can be applied here, but here's an improvement to your current algorithm that also already has optimal running time of O(n) for n elements in the array:
stack = []
left = []
for i in range(len(arr)):
while stack and arr[stack[-1]] < arr[i]:
stack.pop()
left.append(stack[-1] if stack else None)
stack.append(i)
This uses a stack to keep track of the indices of the larger elements to the left, popping indices from the stack as long as their element are smaller than the current element, and then adding the current index itself. Since each element is added to and popped from the stack at most once, running time is O(n). The same can be used for the right-side elements simply by iterating the array in reverse order.

How to choose specific minimum values in lists and do mathematical operations on them

After getting data from user input I put the input in lists like this:
x= [3, 2, 1, 0, 1, 2]
y= [1, 2, 0, 3, 4, 1]
I have manged to write this as:
rows = 3
weight = 0
high =0
low =0
while rows>=3 and rows<=200:
rows, weight = map(int, input().split())
break
count_input = 0
while count_input<rows:
while high>=0 and low<=100:
high, low = map(int, input().split())
i=i+1
if count_input==rows:
break
To choose the minimum number in a list i tried this:
smallest = None
for number in [1, 0, 3, 4, 5, 2]:
if smallest is None or number < smallest:
smallest = number
print('Smallest:', smallest)
my questions are:
How to determine minimum values in these two lists and add minimum values together BUT taking into account that selected minimum values of same positions like x[0] and y[0], or x[1] and y[1] can not be added together.
Elements in diagonal position to each other like x[0] and y[1], x[2] and y[3] can not be added together
Also How to put a limit for number of chosen values, like choosing the minimum 4 values found in lists together
This is how I would approach finding the minimum of the data set, with the logic for not using values between the lists if they have the same index or a diagonal index
x = [3, 2, 1, 0, 1, 2]
y = [1, 2, 0, 3, 4, 1]
final_min = max(x) + max(y)
for x_index in range(0, len(x)):
for y_index in range(0, len(y)):
if y_index == x_index - 1 or y_index == x_index or y_index == x_index + 1:
pass
else:
test_min = x[x_index] + y[y_index]
if test_min < final_min:
print(test_min) # prints 3, 2, 1
final_min = test_min
print(final_min) # prints 1
This makes sense by visually looking at the data, as there are 3 places that the sum would be 1, and the only place it could be smaller (0) would be 0 + 0 but that is a diagonal pair so it cannot be included. You should keep in mind that this is a computationally expensive approach though because it iterates through the y list for every index in the x list, and if your lists are large this will take a LONG time! Also, if the lists are different lengths then the program will likely hit an IndexError and I have not included safeguards for that. I cannot help you on your final point because I do not understand what is meant by:
How to put a limit for number of chosen values, like choosing the minimum 4 values found in lists together
You would need to clarify for anybody to understand what is meant here.
Use min(..) and index(..).
This solution may not be entirely correct, but you get the idea...
def add_min_with_constraints(a, b):
if len(a) == 0 or len(b) == 0:
return -math.inf
min_a_i = a.index(min(a))
min_b_i = b.index(min(b))
if min_a_i == min_b_i or abs(min_a_i - min_b_i) == 1: # same or diagonal indices
# exclude the current minimums
return add_min_with_constraints(a[:min_a_i] + a[min_a_i+1:],
b[:min_b_i] + b[min_b_i+1:])

How to find index of previous data that is higher than current value with pandas?

I'm looking for more simple and fast way to find difference between 2 time series values using pandas.
I have the following time series data:
a = [100, 20, 0, 10, 10, 50]
I'd like to know the way to get index of value that is the higher than the current value.
For example, a[3] is 10.
And we move one step backward in order to search for higher value than 10.
a[2] is 0, which is lower than 10( == a[3]). So let's move to a[1].
a[1] is 20, which is higher than 10.
So the answer what I want is 2, which is (index of a[3] - index of a[1]).
In case of a[5] ( == 50), the answer what I want is 5, which is (index of a[5] - index of a[0]).
All the results would be the following:
func(a[0]) = 0
func(a[1]) = 0
func(a[2]) = 0
func(a[3]) = 2
func(a[4]) = 3
func(a[5]) = 5
It would be very helpful if you suggest any hints about implementing this kind of function.
I believe your listed output is not consistent with what you ask. This function may be what you want.
def func(l, index):
for i in range( index):
if l[index - i - 1] > l[index]:
break
else:
return 0 # return 0 if it never finds a larger number (loop completes)
return i + 1 # this should be the distance
It searches backwards, and returns the distance to the next number which is larger (and zero if none exist)
[func(a, i) for i in range(len(a))] gives
[0, 1, 1, 2, 3, 5]

Array of positive integers , ideas for efficient implementation

I have a small problem within a bigger problem.
I have an array of positive integers. I need to find a position i in the array such that all the numbers which are smaller than the element at position i should appear after it.
Example:
(let's assume array is indexed at 1)
2, 3, 4, 1, 9,3, 2 => 3rd pos // 1,2,3 are less than 4 and are occurring after it.
5, 2, 1, 5 => 2nd pos
1,2,1 => 2nd pos
1, 4, 6, 7, 2, 3 => doesn't exist
I'm thinking of using a hashtable but I don't know exactly how. Or sorting would be better? Any ideas for an efficient idea?
We can start by creating a map (or hash table or whatever), which records the last occurence for each entry:
for i from 1 to n
lastOccurrence[arr[i]] = i
next
We know that if j is a valid answer, then every number smaller than j is also a valid answer. So we want to find the maximum j. The minimum j is obviously 1 because then the left sublist is empty.
We can then iterate all possible js and check their validity.
maxJ = n
for j from 1 to n
if j > maxJ
return maxJ
if lastOccurrence[arr[j]] == j
return j
maxJ = min(maxJ, lastOccurrence[arr[j]] - 1)
next
from sets import Set
def findMaxIndex(array):
lastSet = Set()
size = len(array)
maxIndex = size
for index in range(size-1,-1,-1):
if array[index] in lastSet:
continue
else:
lastSet.add(array[index])
maxIndex = index + 1
if maxIndex == 1:
return 0 # don't exist
else:
return maxIndex
from the last element to the first, use a set to keep elements having met, if iterate element(index i) is not in set, then the max index is i, and update the set

Categories