Related
The problem statement is:
Design and implement an algorithm that displays the elements of a list
by interleaving an element from the beginning and an element from the
end.
For example, input:
1 2 3 4 5 6 7 8
Output :
1 8 2 7 3 6 4 5
This is what I tried, but I don't know what happen with elements 7 and 8:
lista = [1, 2, 3, 4, 5, 6, 7, 8]
for i in range(len(lista)):
lista.insert(2*i-1,lista.pop())
print("The list after shift is : " + str(lista))
# output:
# The list after shift is : [1, 7, 2, 8, 3, 6, 4, 5]
The only error in you code, is that range(len(lista)) starts from 0, not from 1. By starting from zero, in the first iteration 2*i-1 will be 2*0-1 = -1, and hence lista.insert(-1,lista.pop()), which means inserting at the very end of the list (that is what index -1 means in python).
To fix your code, you just need to start the range from 1. Actually, you are iterating too much, you can have your range just from 1 to the half of your list, like this:
lista = [1, 2, 3, 4, 5, 6, 7, 8]
for i in range(1, len(lista)//2):
lista.insert(2*i-1,lista.pop())
print("The list after shift is : " + str(lista))
# output:
# The list after shift is : [1, 8, 2, 7, 3, 6, 4, 5]
When you become more familiarized with the language, you will see that this can be accomplished much more easily.
For example, you can use the python slice syntax to achieve your goal. You slice from beginning to half , and from end to half (step of -1), then zip then together and flat.
[i for z in zip(lista[:4],lista[:-5:-1]) for i in z]
# [1, 8, 2, 7, 3, 6, 4, 5]
Another option:
import math
lista = [1, 2, 3, 4, 5, 6, 7, 8]
ans = []
for i in range(math.floor(len(lista)/2)):
ans.append(lista[i])
ans.append(lista[-i-1])
if (len(lista) % 2) != 0:
ans.append(lista(math.ceil(len(lista)/2)))
print(ans)
Technically speaking, I'd say it's two off-by-one errors (or one off-by-one error, but from -1 to +1, you'll see what I mean in the second paragraph). The first one is that you're subtracting 1 when you shouldn't. In the case when i = 0 (remember that range(n) goes from 0 to n-1), the insert position is being evaluated as 2*0-1 = (2*0)-1 = 0-1= -1 (for insert() method, that's the last position of the original list, pushing what was there forward, so it'll be the penultimate position of the NEW list).
But, when you remove the -1, the output becomes 8 1 7 2 6 3 5 4, which is close to what you want, but not quite right. What's missing is that the elements inserted should be at positions 1, 3, 5, 7, and not 0, 2, 4, 6. So, you'll actually need to add 1.
So, the shortest change to fix your code is to change lista.insert(2*i-1,lista.pop()) to lista.insert(2*i+1,lista.pop()).
Notice: if you put a print inside for, you'll realize that, after changing half the elements, the output is already right. That's because when len(lista) is 8, and you do lista.insert(x, lista.pop()) where x is bigger than 8, basically you're removing the last element (pop) and adding it at the end, so, nothing changes. Hence, you could also change range(len(lista)) to range(len(lista)//2). Test if it'll work when len(lista) is odd
Is there a way to loop through a list from a specific index that wraps back to the front?
Let's imagine a list
arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Is there a way to loop from 4 onwards, wrapping back to the front and continuing from there?
Ideally iterating through the original list as I need to modify the values.
Expected output:
4 5 6 7 8 9 0 1 2 3
Visualized example
If you want to use the iterator directly, then you can use
for x in arr[4:] + arr[:4]:
# operations on x
I used the + for concatenation assuming it is a native Python List
Otherwise if you use indices:
for i in range(len(arr)):
x = arr[(4 + i)%len(arr)]
# operations on x
There is. You can slice the list in two and iterate over them in the same loop like this:
arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
idx = 4
for i in arr[idx:] + arr[:idx]:
print(i)
I'm new to both algorithms AND programming.
As an intro to the MERGE algorithms the chapter introduces first the MERGE algorithm by itself. It merges and sorts an array consisting of 2 sorted sub-arrays.
I did the pseudocode on paper according to the book:
Source: "Introduction to Algorithms
Third Edition" Thomas H. Cormen Charles E. Leiserson Ronald L. Rivest Clifford Stein
Since I am implementing it in python3 I had to change some lines given that indexing in python starts at 0 unlike in the pseudocode example of the book.
Keep in mind that the input is one array that contains 2 SORTED sub-arrays which are then merged and sorted, and returned. I kept the prints in my code, so you can see my checks...
#!/anaconda3/bin/python3
import math
import argparse
# For now only MERGE slides ch 2 -- Im defining p q and r WITHIN the function
# But for MERGE_SORT p,q and r are defined as parameters!
def merge(ar):
'''
Takes as input an array. This array consists of 2 subarrays that ARE ALLREADY sorted
(small to large). When splitting the array into half, the left
part will be longer by one if not divisible by 2. These subarrays will be
called left and right. Each of the subarrays must already be sorted. Merge() then
merges these sorted arrays into one big sorted array. The sorted array is returned.
'''
print(ar)
p=0 # for now defining always as 0
if len(ar)%2==0:
q=len(ar)//2-1 # because indexing starts from ZERO in py
else:
q=len(ar)//2 # left sub array will be 1 item longer
r=len(ar)-1 # again -1 because indexing starts from ZERO in py
print('p', p, 'q', q, 'r', r)
# lets see if n1 and n2 check out
n_1 = q-p+1 # lenght of left subarray
n_2 = r-q # lenght of right subarray
print('n1 is: ', n_1)
print('n2 is: ', n_2)
left = [0]*(n_1+1) # initiating zero list of lenght n1
right=[0]*(n_2+1)
print(left, len(left))
print(right, len(right))
# filling left and right
for i in range(n_1):# because last value will always be infinity
left[i] = ar[p+i]
for j in range(n_2):
right[j] = ar[q+j+1]
#print(ar[q+j+1])
#print(right[j])
# inserting infinity at last index for each subarray
left[n_1]=math.inf
right[n_2]=math.inf
print(left)
print(right)
# merging: initiating indexes at 0
i=0
j=0
print('p', p)
print('r', r)
for k in range(p,r):
if left[i] <= right[j]:
ar[k]=left[i]
# increase i
i += 1
else:
ar[k]=right[j]
#increase j
j += 1
print(ar)
#############################################################################################################################
# Adding parser
#############################################################################################################################
parser = argparse.ArgumentParser(description='MERGE algorithm from ch 2')
parser.add_argument('-a', '--array', type=str, metavar='', required=True, help='One List of integers composed of 2 sorted halves. Sorting must start from smallest to largest for each of the halves.')
args = parser.parse_args()
args_list_st=args.array.split(',') # list of strings
args_list_int=[]
for i in args_list_st:
args_list_int.append(int(i))
if __name__ == "__main__":
merge(args_list_int)
The problem:
When I try to sort the array as shown in the book the merged array that is returned contains two 6es and the 7 is lost.
$ ./2.merge.py -a=2,4,5,7,1,2,3,6
[2, 4, 5, 7, 1, 2, 3, 6]
p 0 q 3 r 7
n1 is: 4
n2 is: 4
[0, 0, 0, 0, 0] 5
[0, 0, 0, 0, 0] 5
[2, 4, 5, 7, inf]
[1, 2, 3, 6, inf]
p 0
r 7
[1, 2, 2, 3, 4, 5, 6, 6]
This does how ever not happen with arrays of any number higher than 6.
$ ./2.merge.py -a=2,4,5,7,1,2,3,8
[2, 4, 5, 7, 1, 2, 3, 8]
p 0 q 3 r 7
n1 is: 4
n2 is: 4
[0, 0, 0, 0, 0] 5
[0, 0, 0, 0, 0] 5
[2, 4, 5, 7, inf]
[1, 2, 3, 8, inf]
p 0
r 7
[1, 2, 2, 3, 4, 5, 7, 8]
I showed it to a colleague in my class without success. And I've walked it through manually with numbers on paper snippets but withouth success. I hope someone can find my silly mistake because I'm completely stuck.
Thanks
As r is the index of the last value in arr, you need to add one to it to make a range that also includes that final index:
for k in range(p, r + 1):
# ^^^^^
Note that your code could be greatly reduced if you would use list slicing.
Brother you made a very small mistake in this line
for k in range(p,r):
Here you loop is running from p to r-1 and your last index i.e r, will not get iterated.
So you have to use
for k in range(p,r+1):
And in the second testcase a=[2,4,5,7,1,2,3,8]
You are getting the correct output even with your wrong code because you are overwriting the values in array ar and your current code was able to sort the array till index r-1 and the number present at index r will be the same which was present before the execution of your merge function i.e 8
Try using this testcase: [2, 4, 5, 8, 1, 2, 3, 7]
And your output will be [1, 2, 2, 3, 4, 5, 7, 7]
Hope this helped
I want to add every nth number from list recursively, but NOT the first number, so lets say I have a list [1, 2, 3, 4, 5, 6] and I want to add every 2nd number, so that would mean I need to add 2 + 4 + 6 but if I want to add every 3rd number, then it should add 3 + 6.
So right now I have this much, I want to add every 2nd number so that means I want to add 2, 4 and 6, but I can't seem to figure out why it doesn't work, what should I do differently?
def getsum(numbers):
if len(piece)==0:
return 0
else:
return getsum(numbers[2:]) + numbers[0]
print getSum([1, 2, 3, 4, 5, 6])
You can pick out the nth number, then recursively slice off everything after that when you call the function again
def get_sum(numbers, n):
if len(numbers) < n:
return 0
return numbers[n-1] + get_sum(numbers[n:], n)
For example with n = 2 and n = 3 respectively
>>> get_sum([1, 2, 3, 4, 5, 6], 2) # 2 + 4 + 6
12
>>> get_sum([1, 2, 3, 4, 5, 6], 3) # 3 + 6
9
I need to create a random array of 6 integers between 1 and 5 in Python but I also have another data say a=[2 2 3 1 2] which can be considered as the capacity. It means 1 can occur no more than 2 times or 3 can occur no more than 3 times.
I need to set up a counter for each integer from 1 to 5 to make sure each integer is not generated by the random function more than a[i].
Here is the initial array I created in python but I need to find out how I can make sure about the condition I described above. For example, I don't need a solution like [2 1 5 4 5 4] where 4 is shown twice or [2 2 2 2 1 2].
solution = np.array([np.random.randint(1,6) for i in range(6)])
Even if I can add probability, that should work. Any help is appreciated on this.
You can create an pool of data that have the most counts and then pick from there:
import numpy as np
a = [2, 2, 3, 1, 2]
data = [i + 1 for i, e in enumerate(a) for _ in range(e)]
print(data)
result = np.random.choice(data, 6, replace=False)
print(result)
Output
[1, 1, 2, 2, 3, 3, 3, 4, 5, 5]
[1 3 2 2 3 1]
Note that data is array that has for each element the specified count, then we pick randomly from data this way we ensure that you won't have more elements that the specify count.
UPDATE
If you need that each number appears at least one time, you can start with a list of each of the numbers, sample from the rest and then shuffle:
import numpy as np
result = [1, 2, 3, 4, 5]
a = [1, 1, 2, 0, 1]
data = [i + 1 for i, e in enumerate(a) for _ in range(e)]
print(data)
result = result + np.random.choice(data, 1, replace=False).tolist()
np.random.shuffle(result)
print(result)
Output
[1, 2, 3, 3, 5]
[3, 4, 2, 5, 1, 2]
Notice that I subtract 1 from each of the original values of a, also the original 6 was change to 1 because you already have 5 numbers in the variable result.
You could test your count against a dictionary
import random
a = [2, 2, 3, 1, 2]
d = {idx: item for idx,item in enumerate(a, start = 1)}
l = []
while len(set(l) ^ set([*range(1, 6)])) > 0:
l = []
while len(l) != 6:
x = random.randint(1,5)
while l.count(x) == d[x]:
x = random.randint(1,5)
l.append(x)
print(l)