Currently I found this code while learning from a video. However, the code has a flaw which was pointed out but I am unable to understand part of the algorithm and why that flaw is a 'flaw'.
i = len(numList) - 1
while i > 1:
j = 0
while j < i:
# If the value on the left is bigger switch values
if numList[j] > numList[j+1]:
temp = numList[j]
numList[j] = numList[j + 1]
numList[j + 1] = temp
else:
print()
j += 1
i -= 1
for k in numList:
print(k, end=", ")
print()
The code is supposed to order numbers from a list of numbers, however I am unable to understand two things out of it:
One is "Why is 1 subtracted from "i"?"
i = len(numList) - 1
And two, when the last number of the algorithm is "1" the algorithm won't order the numbers properly. For example, a list of "4, 2, 6, 3, 1" will be ordered as "2, 1, 3, 4, 6" instead of the correct "1, 2, 3, 4, 6,". People from the comments pointed out the reason for this is that it should be "while i > 0" or "while i >= 1" instead of "while i > 1".
while i > 1:
However I am unable to understand why it is like that.
"Why is 1 substracted from "i"?"
Because collections are zero-indexed. You have N elements, but the last indexable value is always N-1
When j < i and you access numList[j+1], then for when j is at its max value, j == i-1, and i is at its max value of len(numList) - 1, then you access numList[(i-1)+1] == numList[i] == numList[len(numList) - 1], which is the last available element.
two and the one that makes my head hurt the most
while i > 0 is correct because in the first iteration, i == 1 and j == 0, and you swap j+1 == 1 and j == 0 index when numList[j] > numList[j+1], so therefore 2 (index 1) and 1 (index 0) would be flipped, and you get 1,2,3,4,6 from 2,1,3,4,6
The algorithm carries the maximum of the first i+1 numbers to the front (so i+1-th position), by switching the neighbours until the maximum 'bubbles up'.
In the first iteration, i = len(numList) - 1, so the maximum of the whole numList
(index starts at 0 and ends at len(numList) - 1) is carried to the front.
This is the maximum value, which should be last. Now you only have to worry about
the first i - 1 values, therefore i is reduced by one.
Because i > 1 forgets about carrying the first element to the front (being the second position), 2 and 1 in your example are not ordered correctly, as they would need to switch.
Therefore you need the i = 1 iteration step.
There is a great site, which helps with visualisation of the sorting algorithms.
https://visualgo.net/en/sorting?slide=1. Your algorithm is called bubble-sort.
Related
You are given a 0-indexed array of integers nums of length n. You are initially positioned at nums[0].
Each element nums[i] represents the maximum length of a forward jump from index i. In other words, if you are at nums[i], you can jump to any nums[i + j] where:
0 <= j <= nums[i] and
i + j < n
Return the minimum number of jumps to reach nums[n - 1]. The test cases are generated such that you can reach nums[n - 1].
Example 1:
Input: nums = [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index.
Example 2:
Input: nums = [2,3,0,1,4]
Output: 2
My solution:
class Solution:
def jump(self, nums: List[int]) -> int:
j = len(nums)-1
res = []
while j >= 0:
cursor = j
valid_j = j # this variable will hold the best value of j to be set
while cursor >= 0:
if nums[cursor] + cursor >= j:
valid_j = cursor
cursor-=1
res.append(valid_j)
j = valid_j
return len(res)
Either of the while loop doesn't terminate and I am unable to figure out why. Time limit exceeded error.
Please help by explaining the error.
Thanks!
Your while condition is wrong. It will always be true since j is never going to become negative.
The meaning of j in your algorithm is the index that must be jumped to from some earlier index. But index 0 must never be jumped to, since we already achieved that index by starting there.
To fix this, the while condition should be changed to j > 0.
It should be noted that this is not a very efficient solution, as cursor is repeatedly reading the same values of the list, giving this algorithm a worst time complexity of O(𝑛²).
It is possible to solve this problem without nested loop, using a forward loop.
I'm trying to solve the FibFrog Codility problem and I came up with the following approach:
If len(A) is 0 we know we can reach the other side in one jump.
If len(A) + 1 is a fibonacci number, we can also reach it in one jump.
Else, we loop through A, and for the positions we can reach, we check if we can either reach them directly from -1 using a fibonacci number (idx + 1 in fibonaccis) or if we can reach them by first jumping to another position (reachables) and then jumping to the current position. In either case, we also check if we can go from the current position to the end of the river - if we can, then we can return because we found the minimum number of steps required.
Finally, if unreachable is True once this loop completes, this means we can't reach any position using a Fibonacci number, so we return -1.
I'm getting 83% correctness and 0% performance with this approach.
I understand the solution is O(n^2), assuming the array consists of only 1, the nested loop for v in reachables: would run n times - however I'm not sure how else I can compute this, since for each of the positions I need to check whether we can reach it from the start of the array, or from any previous positions using a fibonacci number.
def solution(A):
if len(A) == 0: return 1
fibonaccis = fibonacci(len(A) + 3)
if len(A) + 1 in fibonaccis: return 1
leaves = [0] * len(A)
unreachable = True
reachables = []
for idx, val in enumerate(A):
if val == 1:
if idx + 1 in fibonaccis:
unreachable = False
leaves[idx] = 1
if len(A) - idx in fibonaccis:
return 2
reachables.append(idx)
elif len(reachables) > 0:
for v in reachables:
if idx - v in fibonaccis:
leaves[idx] = leaves[v] + 1
if len(A) - idx in fibonaccis:
return leaves[v] + 2
reachables.append(idx)
break
if unreachable: return -1
if len(A) - reachables[-1] in fibonaccis:
return leaves[reachables[-1]] + 1
def fibonacci(N):
arr = [0] * N
arr[1] = 1
for i in range(2, N):
arr[i] = arr[i-1] + arr[i-2]
return arr
Some suggestions for improving performance of your algorithm -
If len(A) = 100000, you are calculating 100003 fibonacci numbers, while we only need fibonacci numbers which are less than 100k, which would be <30 of them.
Your solution is O(n^4), since each X in reachables or Y in fibonaccis operation is O(N) where N is len(A). (and length of fibonaccis being N because of above issue)
Since you are doing a lot of item in list operations on fibonaccis and reachables, consider making it a set or a dictionary for faster(O(1) instead of O(n)) lookup.
Even with the above changes, the algorithm would be O(N^2) because of nested looping across A and reachables, so you need to come up with a better approach.
With your existing implementation, you need to traverse through all the paths and then in the end you will get the smallest number of jumps.
Instead of this approach, if you start at 0, and then keep a count of the number of jumps you have taken so far, and maintain how far(and to which numbers) you can reach after each jump then you can easily find the minimum jumps required to reach the end. (this will also save on redundant work you would have to do in case you have all 1s in A.
e.g. for
A = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
fibonacci = set(1, 2, 3, 5)
At first jump, we can reach following 1-based indexes -
reachable = [1, 2, 3, 5]
jumps = 1
After second jump
reachables = [2, 3, 4, 5, 6, 7, 8]
jumps = 2
After third jump
reachables = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
jumps = 3
so you have reached the end(10) after 3 jumps.
Please check out #nialloc's answer here - https://stackoverflow.com/a/64558623/8677071 which seems to be doing something similar.
Check out also my solution, which scores 100% on Codility tests and is easy to comprehend.
The idea is to track all possible positions of the frog after k jumps. If possible position == n, return k.
def fib_up_to(n):
numbers = [1]
i = 1
while True:
new_num = (numbers[-2] + numbers[-1]) if i > 1 else 2
if new_num > n:
break
numbers.append(new_num)
i += 1
return numbers
def solution(A):
n = len(A)
if n == 0:
return 1
numbers = fib_up_to(n+1)
possible_positions = set([-1])
for k in range(1, n+1):
positions_after_k = set()
for pos in possible_positions:
for jump in numbers:
if pos + jump == n:
return k
if pos + jump < n and A[pos + jump]:
positions_after_k.add(pos + jump)
possible_positions = positions_after_k
return -1
I have a list ls with integer elements between 0 and 100. I want to build a function that counts a number of elements until it encounters an element that has a larger value, and appends the count to the solution list.
In other words, if ls = [5, 10, 1, 1, 20, 1], the solution should be [1, 3, 2]:
1 comes from the first element, 5
3 comes from the second to fourth elements, 10, 1, 1
2 comes from the last two elements, 20, 1
(If ls = [7, 3, 9], the return should be [2,1].)
I used a for loop to perform the task:
def compare_and_count(ls):
answer = []
num = 1
ref = 0
for j in range(1, len(ls)):
try:
if ls[ref + j] <= ls[ref]:
num += 1
else:
answer.append(num)
num = 1
ref = ref + j
except IndexError:
break
answer.append(num)
return answer
I tried to do a value comparison with two movable references, but this raises IndexError and also sometimes neglects the final counting and hence returns an incorrect list. (I added try...except to be free of the IndexError, but the latter problem is still unsolved)
I assume either loop or stack/queue is the most concise way to solve this, but I couldn't land an optimal way yet. Any insight is appreciated.
Edited the answer to correct it .
def my(a):
count = 1
maxval = a[0]
answer = []
for i in range(1, len(a)):
if a[i] > maxval:
maxval = a[i]
answer.append(count)
count = 1
else:
count += 1
answer.append(count)
return answer
I tried to do a value comparison with two movable references
You dont need to, just one moving index i and one count counter with a maxval to check the condition.
I have a list that moves around 100 by a small bit (see the list in the code below). For the days that I have 0, I would like to spread out the difference of the previous cells.
(I'm counting zero as the first cell in the text below, because python)
To give an example, the original_list below has the value 100.41191500000001 in cell 2, and 0 in cells 3,4,5. I would like to take the change abs(original_list[1]-original_list[2])=1,26108000000001, and spread it out over the three days. So basically I would like cell 2 to be occupied by original_list[1]+(1.26108000000001/4), cell 3 to be new_list[2] + (1.26108000000001/4), cell 4 to be new_list[3] + (1.26108000000001/4), and finally cell 5 to also be new_list[4] + (1.26108000000001/4).
I divide by four because I want the change that happened between cell 1 and 2, to be spread out evenly between cells 1 to 5.
The code below presents what I've tried, but I'm not sure if this is the best approach. Any help would be greatly appreciated.
original_list = [98.87464, 99.150835, 100.41191500000001, 0, 0, 0, 101.650165, 0, 0, 0, 0, 0, 101.850421, 0, 99.970131, 100.244205, 98.550495, 0, 0, 97.496535, 97.971645]
new_list = [0 for i in range(len(original_list))]
for i in range(len(original_list)):
j = i
if original_list[i] == 0:
while original_list[j] == 0:
j += 1
elif original_list[i] != 0:
new_list[i] = original_list[i]
if j != i:
for k in range(0,j-i+1):
new_list[i+k] = new_list[i-1] + abs(new_list[i]-new_list[i-1])/(j-i+1)
print(new_list)
It's not the best approach as it will not give your expected result.
The for loops iterates over all the elements of the range. You have an inner for loop in the if j != i: block which calculate the replacement values for the following 0's. When the control is returned to the main for loop, it is going to recalculate those values, giving unexpected numbers.
I think your math to calculate new_list[i+k] is wrong. If I understood correctly, it does not reproduce what you describe in your question.
In your case a while loop is recommended. In the case where original_list[i] != 0: you increment the index by 1, in the case where original_list[i] == 0: you need to set the index of the proper value so that it corresponds to the next non null element in your original list, which should be i = j.
And of course you need to fix the math.
I would wrote the code in this way:
new_list = []
i = 0
while i < len(original_list):
if original_list[i] == 0:
j = i+1
while original_list[j] == 0 and j < len(original_list):
j += 1 #searching the index of the next non null value
#please check if the following math is what you really need, fix it in case.
diff = original_list[j] - new_list[-1]
inslist = [new_list[-1] + (k*diff)/(j-i+1) for k in range(1, (j-i+1))]
new_list.extend(inslist)
i = j
elif original_list[i] != 0:
new_list.append(original_list[i])
i += 1
You can compare the two list with: print([(o, n) for o, n in zip(original_list, new_list)]) and see if each original - new pair of values is correct.
Here is the explanation of what I'm trying to say:-
Input:- 5 1 3 2 7
Output:- 3
Explanation:
In first move, we move 3 to the end. Our list becomes 5,1,2,7,3
In second move, we move 5 to the end. Our list becomes 1,2,7,3,5
In third move, we move 7 to the end. Our final list = 1,2,3,5,7
So, total moves are:- 3.
Here is what I tried to do, but failed.
a = [int(i) for i in input().split()]
count = 0
n = 0
while (n < len(a) - 1):
for i in range(0,n+1):
while (a[i] > a[i + 1]):
temp = a[i]
a.pop(i)
a.append(temp)
count += 1
n += 1
print(count, end='')
I'd like to request your assistance in helping in solving this question.
jdehesa's answer is basically right, but not optimal for cases, when there is more element of same value. Maybe more complex solution?
def min_moves(a):
c = 0
while(1):
tmp = None
for i in range(0, len(a)):
if a[i] != min(a[i:]) and (tmp is None or a[i] < a[tmp]):
tmp = i
if tmp is None:
return c
else:
a.append(a.pop(tmp))
c += 1
Edit:
Or if you don't need ordered list, there's much more easier solution just to count items that are out of order for the reason from jdehesa's solution :-D
def min_moves(a):
c = 0
for i in range(0, len(a)):
if a[i] != min(a[i:]):
c += 1
return c
Edit 2:
Or if you like jdehesa's answer more, small fix is to reduce lst to set, so it will get smallest index
sorted_index = {elem: i for i, elem in enumerate(sorted(set(lst)))}
I cannot comment yet.
I don't know if it can be done better, but I think the following algorithm gives the right answer:
def num_move_end_sort(lst):
# dict that maps each list element to its index in the sorted list
sorted_index = {elem: i for i, elem in enumerate(sorted(lst))}
moves = 0
for idx, elem in enumerate(lst):
if idx != sorted_index[elem] + moves:
moves += 1
return moves
print(num_move_end_sort([5, 1, 3, 2, 7]))
# 3
The idea is as follows. Each element of the list would have to be moved to the end at most once (it should be easy to see that a solution that moves the same element to the end more than once can be simplified). So each element in the list may or may not need to be moved once to the end. If an element does not need to be moved is because it ended up in the right position after all the moves. So, if an element is currently at position i and should end up in position j, then the element will not need to be moved if the number of previous elements that need to be moved, n, satisfies j == i + n (because, after those n moves, the element will indeed be at position j).
So in order to compute that, I sorted the list and took the indices of each element in the sorted list. Then you just count the number of elements that are not in the right position.
Note this algorithm does not tell you the actual sequence of steps you would need to take (the order in which the elements would have to be moved), only the count. The complexity is O(n·log(n)) (due to the sorting).
I think you can simplify your problem,
Counting elements that need to be pushed at the end is equivalent to counting the length of the elements that are not in sorted order.
l = [5, 1, 3, 2, 7]
sorted_l = sorted(l)
current_element = sorted_l[0]
current_index = 0
ans = 0
for element in l:
if current_element == element:
current_index += 1
if current_index < len(l):
current_element = sorted_l[current_index]
else:
ans += 1
print(ans)
Here the answer is 3