Python remove N consecutive duplicates from the list - python

Here's the problem. The input is a list of integers. If more than three adjacent numbers appear next to each other they should be dropped and the operation goes again. Kind of similar to the Iphone game, where player needs to pop lines of three or more balls of the same colors. The output should be the count of the balls that will be removed.
The algorithm is as follows. Starting with a sample list of say [3,3,4,4,4,4,3,2].
First iteration should remove the 4,4,4,4 - so the list would become [3,3,3,2], and the intermediary output of removed numbers will be 4.
Second iteration should remove 3,3,3 - so the final list would be [2] and final count of removed numbers - 7.
The first implementation for three consecutive items came from another stackoverflow thread - Remove triplets of adjacent numbers from the list
Here's the working function implementation for exactly 3 consecutive numbers:
def balls(l):
values = l
while len(values) >= 3:
print(values) #for demonstrative purposes of this question
for x in range(0,len(values)-2):
if values[x] == values[x+1] and values[x] == values[x+2]:
values = values[:x] + values[x+3:]
break
else:
break
print(values) #for demonstrative purposes of this question
return len(l) - len(values)
balls([3, 3, 4, 4, 4, 3, 4])
Output:
[3, 3, 4, 4, 4, 3, 4]
[3, 3, 3, 4]
[4]
6
How could I update the implementation to include the more general solution of removing 3+ consecutive numbers. I am thinking about tracking the start and end index of the consecutive duplicates, then subsetting the list. However, not sure how to implement that. Here are the tests that should work.
if __name__ == "__main__":
test1 = [3, 3, 4, 4, 4, 3, 4]
print(balls(test1))
#Output should be 6
test2 = [5, 5, 5, 5, 5, 5, 5, 5]
print(balls(test2))
#Output should be 8
test3 = [5, 7, 8, 3]
print(balls(test3))
#Output should be 0

def remove_consecutive(l, length):
amount = len(l)
count = 1
start = 0
current = l[0]
i = 1
while i < len(l):
if l[i] == current:
count += 1
else:
if count >= length:
for i in range(count):
l.pop(start)
start = 0
i = 0
current = l[0]
else:
start = i
current = l[i]
count = 1
i+=1
if count >= length:
for i in range(count):
l.pop(start)
return amount - len(l)
Wuff, i got it. My brain is kinda stinky lately so it took so long.

Here is my code, it works well. But I think there may be better ways to achieve higher efficiency.
def remove_consecutive(lst):
len_init = len(lst)
contain_tuplets = True
while contain_tuplets:
for i in range(len(lst)-2):
indices_to_pop = []
if lst[i]==lst[i+1]==lst[i+2]:
indices_to_pop.extend([i, i+1, i+2])
for j in range(i+3,len(lst)):
if lst[j] == lst[i]:
indices_to_pop.append(j)
else:
break
[lst.pop(i) for _ in indices_to_pop]
contain_tuplets = True
break
else:
contain_tuplets = False
count_removed_numbers = len_init - len(lst)
return count_removed_numbers, lst
test case1:
lst = [3,3,4,4,4,4,3,2]
remove_consecutive(lst)
output
(7, [2])
test case 2:
lst = [2, 2, 1, 1, 1, 2, 1]
remove_consecutive(lst)
output:
(6, [1])

def remove_consecutive(l, length):
amount = 0
count = 1
current = l[0]
for i in range(1, len(l)):
if l[i] == current:
count += 1
if count > length:
amount += 1
elif count == length:
amount += length
else:
current = l[i]
count = 1
return amount

Related

deleting every nth element from a list until 1 element remains

I have a numlist 1 to x and I should delete every nth element until 1 remains similar to the Josephus problem but instead of going in a circle, you start iterating from the beginning. If you had a list [1,2,3,4,5,6,7,8,9,10] and n = 3, first iteration removes 3,6,9. Second iteration removes 4 (would be 2 if it were in a circle) and then 8. Third iteration removes 5 and so on until n > len(players) then it could go in a circle.
players = list(range(1, int(input()) + 1))
n = (int(input()) - 1)
innocent = (n) % len(players)
while len(players) > 1:
loser = players.pop(innocent)
innocent = (innocent + n) % len(players)
print(*players)
This code deletes every nth element until 1 is left but it goes in a circle and doesn't iterate from the beginning when it reaches the end of the list each time. How would I change it to do as I explained above?
This would solve your problem
use a while loop if len(A) >= n delete nth elements
```if len(A) < n and len(A) >1`` delete the last element.
A = [h for h in range(1,11)]
n = 3
potentials = [i-1 for i in range(len(A)) if i%n == 0][1:]
B = {}
k = 0
while len(A) > 1:
if len(A) >= n:
A = [A[j] for j in range(len(A)) if j not in potentials]
B[k] = A
k +=1
else :
A = A[:-1]
B[k] = A
k +=1
print(B)
Out:
{0: [1, 2, 4, 5, 7, 8, 10],
1: [1, 2, 5, 7, 10],
2: [1, 2, 7, 10],
3: [1, 2, 10],
4: [1, 2],
5: [1]}
To adjust to your problem
players = list(range(1, int(input()) + 1))
n = (int(input()) - 1)
potentials = [i-1 for i in range(len(players)) if i%n == 0][1:] # position of elements that will be deleted in each iteration
while len(players ) > 1:
if len(players ) >= n:
losers = [players[j] for j in range(len(players)) if j in potentials] #I assume losers are the ones being deleted
players = [players[j] for j in range(len(players)) if j not in potentials]
else:
losers = players[-1]
players = players[:-1]

How to compare each element in the list and check if it's bigger then the element on the right

hello I am struggling with this problem for school and can't get my code to do what it needs to solve this. The question is: Define an element of a list of items to be a dominator if every element to its right (not just the one
element that is immediately to its right) is strictly smaller than that element. It wants me to count how many denominators are in the list.
def extract_increasing(digits):
countDem = 0
#check and see if there is anything in the list
if not digits:
return 0
#compare the first element to the one on the right of it
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
countDem += 1
return countDem
The code below should check if a number in the list is a dominator.
def is_dominator(lst, idx):
for i in range(idx + 1, len(lst)):
if lst[i] >= lst[idx]:
return False
return True
digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in digits:
print(is_dominator(digits, i))
The error in your code is that you're adding one for the counter every time the next value meets the condition.
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
countDem += 1
Every time digits[x] > digits[y] is met you add one to your counter. You should only add one once you checked that all values to the right meet the condition.
isDem = False
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
isDem = True
else:
isDem = False
#Once you went through all the values to the right you can add one to the counter
if isDem ==True:
countDem += 1
Hope that helps!
You start in the last element, and save always the max_element in every iteration, then you know always if exist some number grater than the current number. This is a little more efficient because it runs through the array only once.
def dominator(li: list):
sol = 0
max_number = -math.inf
for i in range(len(li)-1, -1,-1):
if li[i] > max_number:
sol+=1
max_number = li[i]
return sol
Try list comprehension
lst = [0, 10, 2, 6, 7]
new_lst = [v for k,v in enumerate(lst) if all(v > x for x in lst[k+1:])]
# [10, 7]
Update
def extract_increasing(digits: list) -> int:
countDem = 0
for x, y in enumerate(digits):
if all(y > a for a in digits[x+1:]):
countDem += 1
return countDem
lst = [0, 10, 2, 6, 7]
extract_increasing(lst) # -> 2

Breeding new child list from two same length lists, in order, without duplicates and splitting at random index

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

Finding Maximum non-negative Subarray in python

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

Find consecutive integers in a list

I am trying to find consecutive values from an unsorted list. Experimental code below:
num = [8, 9, 4, 1, 2, 3]
#(num[0+1]) next value
for i in range(len(num)-1): # not using -1 will cause index error
if num[i]+1==num[i+1]:
print('Con',num[i])
Problem: I am unable to get the last value with this current code. My output excludes the last value. Here is what I get (no 9 or no 3):
Con 8
Con 1
Con 2
I have seen a few complex solutions which were a little difficult for me to understand. Is it possible to tweak the for loop part a little and get the entire sequence? Thanks a lot.
You can use the function groupby:
from itertools import groupby
num = [8, 9, 4, 1, 2, 3]
# Enumerate and get differences between counter—integer pairs
# Group by differences (consecutive integers have equal differences)
gb = groupby(enumerate(num), key=lambda x: x[0] - x[1])
# Repack elements from each group into list
all_groups = ([i[1] for i in g] for _, g in gb)
# Filter out one element lists
list(filter(lambda x: len(x) > 1, all_groups))
# [[8, 9], [1, 2, 3]]
This is because you only check the next number. When you want the second number (like 9 or 3), you have to include a check for the previous number too. This will make the if a bit longer, but it'll work.
num=[8,9,4,1,2,3]
for i in range(len(num)):
if (
( # check for the next number
i + 1 != len (num) and # don't check the end of the list
num[i]+1==num[i+1]
) or ( # check for the previous number
i != 0 and # don't check before the list
num [i-1] == num [i] - 1
)
): print('Con',num[i])
Also, I had to remove the -1 in your range, because I already do a manual check, and as pointed out, this prvented 3 from being shown.
Your code only tests in one direction (being followed by a consecutive number).
For the full sequence you have to test in both direction.
num=[8,9,4,1,2,3]
assert(len(num) > 1)
for i, n in enumerate(num):
if i != 0:
if n == num[i-1] + 1:
print("Con", n)
continue
if i != len(num) - 1:
if n == num[i+1] - 1:
print("Con", n)
One way would be to print both numbers when you found them to be consecutive, but also check that the one at index i-1 was not in the consecutive list as well so that the number at index i is not printed twice:
num = [8, 9, 4, 1, 2, 3]
for i in range(len(num)-1): # not using -1 will cause index error
if num[i] + 1 == num[i + 1]:
if i == 0 or (i - 1 >= 0 and num[i - 1] != num[i] - 1):
print('Con', num[i])
print('Con', num[i + 1])
Could try with a more complex list as well:
num = [8, 9, 4, 1, 2, 3, 4, 4, 8, 9, 1, 2, 3, 0, 1, 5, 6, 1]
for i in range(len(num)-1): # not using -1 will cause index error
if num[i] + 1 == num[i + 1]:
if i == 0 or (i - 1 >= 0 and num[i - 1] != num[i] - 1):
print('Con', num[i])
print('Con', num[i + 1])
num = [8, 9, 4, 1, 2, 3]
def con(rng, pos=0):
if pos < len(rng):
if (pos > 0 and rng[pos]-1 == rng[pos-1]) or (pos < len(rng) -1 and rng[pos]+1 == rng[pos+1]):
print("con", rng[pos])
con(rng, pos+1)
con(num)
edit:
this is solution is based on concurrent function, and needs only the list as argument. As long as they are within lower-/upperbound of list, the function will check if (previous number)-1 or (next number)+1 are equal (this number)
output will be:
con 8
con 9
con 1
con 2
con 3

Categories

Resources