Remove triplets of adjacent numbers from the list [duplicate] - python

This question already has answers here:
Write a function lines(a) that determines how many balls will be destroyed
(6 answers)
Closed 2 years ago.
Here is the problem. The input is a list of integers. If three adjacent numbers appear next to each other they should be dropped and the operation goes again. Iphone app with the balls of the same colors. The output should be the count of the balls that will be destroyed.
Example:
input = [3,3,4,4,4,3,4]
1st iteration
output: [3,3,3,4]
Final output:
6
4,4,4 on the first iteration, so three balls. and 3,3,3 on the second. Overall six.
My code is below. It will remove the 4,4,4 but will fail after, because list index will quickly go out of range.
def balls(a):
curr_index = 0
removed_count = 0
while len(a) > 2:
if (a[curr_index] == a[curr_index+1]) and (a[curr_index] == a[curr_index+2]):
a.remove(a[curr_index])
a.remove(a[curr_index+1])
a.remove(a[curr_index+2])
curr_index += 1
removed_count += 3
return removed_count
a = [3, 3, 4, 4, 4, 3, 4]
print(balls(a)) # should print 6
Any ideas?

input_values = [3,3,4,4,4,3,4]
values = input_values
while len(values) >= 3:
for x in range(0,len(values)-2):
# if we find 3 values in ar row
if values[x] == values[x+1] and values[x] == values[x+2]:
# update values by cutting out the series
values = values[:x] + values[x+3:]
# break this for loop
break
else:
# for loops can have an else statement
# this means that we came at the end of the for loop
# this if we didn't break the loop (and didn't found a valid triple)
# then we brea
break
#result - amount of removed balls
values, len(input_values) - len(values)

A more generic approach where you can define N
(it will find exact N values)
input_values = [3,3,4,4,4,4,3,3,4]
# amount of neighbours
N = 4
values = input_values
# keep looping as long as we've N values
while len(values) >= N:
# we need this if we break the inner loop
stop = False
# loop over values from left to right
# 3 -> 3 -> 4 -> 4 -> ... until -N + 1
# because that is the last spot that we've N values left
for x in range(0, len(values) - N + 1):
# scout for the next numbers (x+1 x+2 x+3 ....)
# this goes from, x+1 until the end of the serie
# also start counting from 2 - because
# if we've a single match, 2 numbers are equal
for e, y in enumerate(range(x+1, len(values)), start=2):
# if x and y are different, the stop this scouting
# remember y = x+1 x+2 x+3 ....
if values[x] != values[y]:
break
# if we reached this stage, we know that
# x and x+1 x+2 ... are equal
# but also check if we've enough matches
if e == N:
# update values by cutting out the series
values = values[:x] + values[y+1:]
stop = True
break
if stop:
break
else:
# for loops can have an else statement
# this means that we came at the end of the for loop
# this if we didn't break the loop (and didn't found a valid triple)
# then we brea
break
values, len(input_values) - len(values)

Here is one way to do it:
your_list=[3, 3, 4, 4, 4, 3, 4]
def remove_one(l):
if len(l)>2:
for i in range(len(l)-2):
if l[i]==l[i+1]==l[i+2]:
res.extend(l[i:i+3])
l=l[:i]+l[i+3:]
return l
return []
res=[]
while len(your_list)>2:
your_list=remove_one(your_list)
print(len(res))
Output:
6

An approach with a minimal threshold
input_values = [3,3,4,4,4,4,4,3,3,4]
# threshold
N = 3
values = input_values
while len(values) >= N:
# we need this if we break the inner loop
stop = False
# loop over values
for x in range(0, len(values) - N + 1):
# scout for the next numbers (x+1 x+2 x+3 ....)
for e, y in enumerate(range(x+1, len(values)), start=2):
# check if the values of x and x+1 (x+2 x+3 ...) are different
if values[x] != values[y]:
# if so, check if we've reached our threshold of N
if e >= N:
# if this is the case update values by cutting out the series
values = values[:x] + values[y:]
stop = True
break
# if x+n != x then we break this scouting loop
break
# stop is True, this means, that we found N neibouring numbers
# thus we can break this loop, and start over with a new* value
if stop:
break
else:
# for loops can have an else statement
# this means that we came at the end of the for loop
# thus if we didn't break the loop (and didn't found a valid triple)
# then we break
break
values, len(input_values) - len(values)

There are a couple of issues with your code.
The first is you're trying to iterate over the list a single time, but that won't work for your example.
The second is you're assuming the list will always reduce to less than 3, which won't be the case if you have more numbers.
There are also some places you can simplify the syntax, for example:
if (a[curr_index] == a[curr_index+1]) and (a[curr_index] == a[curr_index+2]):
is equivalent to:
if a[curr_index] == a[curr_index + 1] == a[curr_index + 2]:
And
a.remove(a[curr_index])
a.remove(a[curr_index+1])
a.remove(a[curr_index+2])
is equivalent to:
del a[curr_index: curr_index + 3]
So here is one way to accomplish this, thought there are more efficient ways.
def balls(a):
removed = 0
complete = False
# Run until complete is set or the list is too short for a match
while not complete and len(a) >= 3:
for idx, val in enumerate(a):
if val == a[idx + 1] == a[idx + 2]:
del a[idx: idx + 3]
removed += 3
break # Restart every time we find a match
# If it's the last possible match, set complete
if idx == len(a) - 3:
complete = True
break
return removed

I'm little bit late, but my idea is to do the processing in one pass. The idea is that when you drop a tripple, go back two positions to re-check if a new tripple has been created by that change.
Example:
3 3 4 4 4 3
^--- tripple found here, remove it
3 3 3
^--- move the cursor 2 positions left and continue checking
The code:
def drop3(lst):
# drop3 destroys the lst, make a copy if you want to keep it
dropped = 0
cursor = 0
limit = len(lst) - 2
while cursor < limit:
if lst[cursor] == lst[cursor+1] == lst[cursor+2]:
del lst[cursor:cursor+3]
cursor = max(0, cursor-2)
limit -= 3
dropped += 3
else:
cursor += 1
return dropped

A linear-tme solution:
def balls(a):
b = []
for x in a:
b.append(x)
if b[-3:] == [x] * 3:
del b[-3:]
return len(a) - len(b)

Related

traversing through a list using recursion

So I am new to recursion and I am trying to make a program where you can enter a list and python tests each integer (lets say 9 for example) and sees if the integer following it is doubled. So if I entered a list of 2 4 8 16 32, would return 4, and -5 -10 0 6 12 9 36, would return 2 because -5 followed by -10 is one and 6 followed by 12 is the second. This is the code I have so far. I feel like I am very close. but just a few thing stand in my way. Any help would be great!
L = []
def countDouble(L):
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
print(y[1])
print(y[0])
count = 0
y[0] += y[0]
# unsure of how to multiple y[0] by 2
if y[0]*2 == y[1]:
count += 1
else:
count += 0
#how would I traverse through the rest of the entered list using recursion?
print(count)
countDouble(L)
If you want/need to solve it using recursion, the following will do the trick:
def count_sequential_doubles(li, count=0):
return count_sequential_doubles(li[1:], count + int(li[0] * 2 == li[1])) if len(li) > 1 else count
I would suggest this recursive way:
def countDouble(L):
count = 0
if len(L) == 1:
return count
else:
if int(L[0])*2 == int(L[1]):
count += 1
return count + countDouble(L[1:])
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = countDouble(y)
print(count)
I urge you to read the entire answer, but in case you are not interested in tips, notes and the process of finding the solution, here are two solutions:
solution using recursion (not recommended):
x = input()
y = x.split(' ')
count = 0
def countDouble(i):
if(i+1 == len(y)):
return 'recursion ends here when'
if(int(y[i])*2==int(y[i+1])):
count += 1
countDouble(i+1)
countDouble(0)
print(count)
this solution just imitates a while loop:
solution using a while loop (recommended):
x = input()
y = x.split(' ')
count = 0
i = 0
while(i < len(y) - 1):
if(int(y[i]) * 2 == int(y[i+1])):
count += 1
i += 1
print(count)
Before I continue, here are a few tips and notes: (some of them will only make sense after)
I assume the 14 in your example is a typo
I didn't put the code in a function because it's not needed, but you can change it easily.
In your code, you are passing L as a parameter to the countDouble() function, but you don't use it. if you don't need a parameter don't pass it.
when splitting the input, the values of the list are still strings. so you have to invert them to integers (for instance, you can do that with the int() 'function') before comparing their values - otherwise multiplying by 2 will just repeat the string. for example: '13'*2 is the string '1313'
I don't know why you why you added y[0] to itself in line 9, but based on the code that comes after this would yield incorrect results, you don't need to change the elements in order to get their value multiplied by 2.
notice that in the else block, nothing has changed. adding 0 to the count doesn't change it. so you can remove the else block entirely
While it's possible to solve the problem in recursion, there's something else designed for these kind of problems: loops.
The problem is essentially repeating a simple check for every element of a list.
This is how I would arrive to a solution
so we want to run the following 'code':
if(y[0]*2 == y[1]):
count += 1
if(y[1]*2 == y[2]):
count += 1
if(y[2]*2 == y[3]):
count += 1
...
of course the computer doesn't understand what "..." means, but it gives us an idea to the pattern in the code. now we can do the following:
divide the extended 'code' into similar sections.
identify the variables in the pattern - the values that change between sections
find the starting values of all variables
find a pattern in the changes of each variable
find a breaking point, a condition on one of the variables that tells us we have reached the last repeating section.
here are the steps in this specific problem:
the sections are the if statements
the variables are the indexes of the elements in y we compare
the first index starts at 0 and the second at 1
both indexes increase by one after each if-statement
when the second index is bigger then the last index of y then we already checked all the elements and we can stop
so all is left is to set the needed variables, have a while loop with the breaking condition we found, and in the while loop have the general case of the repeating sections and then the changing of the variables.
so:
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = 0
# setting the starting values of the variables
index1 = 0
index2 = 1
# creating a loop with the breaking condition
while(index2 < len(y)):
# the general case of the repeated code:
if(int(y[index1]) * 2 == int(y[index2])):
count += 1
# changing the variables for the next loop
index1 += 1
index2 += 1
print(count)
We see that the index2 is just index1 + 1 at all time. so we can replace it like that:
x = input(f'Enter a list of numbers separated by a space: ')
y = (x.split(' '))
count = 0
index1 = 0
while(index1 + 1 < len(y)):
if(int(y[index1]) * 2 == int(y[index1 + 1])):
count += 1
index1 += 1
print(count)
Note: You can use a for loop similarly to the while loop
So in summary, you can use recursion to solve the problem, but the recursion would just be imitating the process of a loop:
in each call, the breaking condition will be checked, the repeated code would run and the variables/parameters would change.
Hope you find this answer useful :)
Final edit: OP edited his example so my other code didnt apply
Some good questions people are asking, but in the spirit of helping, here's a recursive function that returns the count of all doubles.
def get_doubles_count_with_recursion(a_list, count, previous=None):
while a_list:
try:
first = previous if previous else a_list.pop(0)
next_item = a_list.pop(0)
except IndexError:
return count
if next_item / 2 == first:
count += 1
return get_doubles_count_with_recursion(a_list, count, next_item)
return count
a_list = [1, 3, 5, 10, 11, 14, 28, 56, 88, 116, 232, 464, 500]
doubles = get_doubles_count_with_recursion(a_list, 0)
print(doubles == 5)
Probably could clean it up a bit, but it's a lot easier to read than the other guy's ;)
If I'm reading your question right, you want a count of all pairs where the 2nd item is double the first. (and the 14 in the first list is a typo). In which case a simple function like this should do the job:
#a = [2,4,8,16,32]
a = [-5, -10, 0, 16, 32]
count = 0
for i, x in enumerate(a):
# Stop before the list overflows
if i < len(a) - 1:
# If the next element is double the current one, increment the counter
if a[i+1] == x * 2:
count = count + 1
else:
break
print(count)

I need loop to do until all elements of array are equal to 0

Here is my code, it's randomly generated one of four numbers (1,2,3,4) and turns right left or up and down if consider array as a board, starting point is room [0][0]. It stops after changing one element of array to 0 but I need it to do until all elements are equal to 0, Thanks!
import numpy
import random
N = 2
M = 2
time = 0
room = numpy.ones((N, M), dtype=int)
print(room)
i = 0
j = 0
while room.all() != 0:
turn = random.choice([1, 2, 3, 4])
if turn == 1 and i > 0:
print("up")
i = i - 1
room[i][j] = 0
time += 1
elif turn == 2 and i < N - 1:
print("down")
i = i + 1
room[i][j] = 0
time += 1
elif turn == 3 and j > M - 1:
print("right")
j = j + 1
room[i][j] = 0
time += 1
elif turn == 4 and j < 0:
print("left")
j = j - 1
room[i][j] = 0
time += 1
else:
turn = random.choice([1, 2, 3, 4])
print("hit the wall")
print(room)
The all method checks whether all elements of your array are truthy (in this case, unequal to zero), so the condition in the while loop becomes False once there is a single zero in rooms.
What you actually want to check is whether any element of rooms is truthy (here, unequal to zero).
You can use while rooms.any(): ... instead (and omit the comparison to != 0).
There are two ways you can approach the problem:
First: As suggested by #timgeb:
while room.any():
Second: To use np.count_nonzero to check if there is any non-zero value in the array. Then use this condition in the while loop
while np.count_nonzero(room) > 0:
HOWEVER, when I use either of these conditions, two elements become 0 and two remain 1 and I end up in a never ending loop. That means, the problem is somewhere in your implementation of your indices.
That being said, now knowing two ways to check for non-zero elements, you need to inspect your code for implementation error.

Given a list of tree heights find the least attempts possible to cut them down

The question I'm trying to solve is that, imagine you have a list of the number of trees in each column, e.g. [10,20,10,0], each element in this list is the number of the trees exist in that column, means that in the first column you have ten trees, in the second column you have 20 trees and so on. The number of columns is fixed, and by cutting out a column, its number will become zero. You can cut down a row or a column completely if possible, but you can only cut down in continuous rows or columns, means that if you have [4,0,4] you cannot cut down rows in order to cut all trees down.
For [4,0,4] it just takes two attempts, cutting down first and third columns down. I'll give you another example, imagine you have a list of [4,3,4,3,4] you can cut down three rows and make it [1,0,1,0,1] but then you have to remove three columns which takes you a total of 6 steps, but also you can cut down all columns which take 5 steps, any other solution's result would be greater than 5 steps.
Edit: This image will clarify the question:
First, you cut the column which has 4 elements, then you cut the first row, then you cut the column which has 2 elements now, and then the last row or column. As I've stated before, you cannot cut rows which have a discontinuity. (All elements present in a row should be adjacent in order to be able to cut the row down) (in this picture, the row #3 has a discontinuity)
enter image description here
I'm trying to solve this question for two days, I'm getting nowhere. My latest code is here, which goes into an almost infinite loop in non-square like [for example a list of 3 with the most element being 200] tree list.
def cut_tree_row(tree):
res = []
discontinoue_flag = 0 # A flag to findout if cutting down the row is possible or not
non_zero_flag_once = 0 # if the program encounters a non_zero element for the first time this would change to 1
non_zero_flag_twice = 0# if the program encounters a non_zero element for the second time while the discontinue flag is 1, it would change to 1
for i in xrange(len(tree)):
if tree[i] > 0:
non_zero_flag_once = 1
if non_zero_flag_once == 1 and discontinoue_flag == 1:
non_zero_flag_twice = 1
else:
res.append(tree[i]-1)
else:
if discontinoue_flag == 0 and non_zero_flag_once == 1:
discontinoue_flag = 1
if discontinoue_flag == 1 and non_zero_flag_twice == 1:
return [], 10000000
res.append(0)
if discontinoue_flag == 1 and non_zero_flag_twice == 1:
return [], 10000000
return res , 1
def cut_tree_column(tree):
res = []
max_index = 0
m = max(tree)
flag = 1
if len(tree) == 0:
return tree , 0
for i in xrange(len(tree)):
if tree[i] == m and flag == 1:
flag = 0
res.append(0)
continue
res.append(tree[i])
return res , 1
def find_min_attempts(tree, total_sum = 0):
if len(tree) == 0:
return total_sum
s = sum(tree)
if s == 0:
return total_sum
res1 , num1 = cut_tree_column(tree)
res2 , num2 = cut_tree_row(tree)
return min(find_min_attempts(res1, total_sum + num1),find_min_attempts(res2, total_sum + num2))
def main():
tree = [187, 264, 298, 924, 319] #This input gives me an infinite loop
print find_min_attempts(tree)
main()
This recursive solution calculates the number of steps after cutting the largest column and alternately, the largest row. It returns the minimum of the two.
If a zero is present, it solves each discontinuous section as a subproblem.
def get_max_col(t):
""" Get index of tallest column """
return max(enumerate(t),key=lambda x: x[1])[0]
def remove_max_col(t):
""" Remove tallest column """
idx = get_max_col(t)
new_t = t[:]
new_t[idx] = 0
return new_t
def remove_bottom_row(t):
""" Remove bottom row """
return [x - 1 for x in t]
def splitz(iterable, val):
""" Split a list using val as the delimiter value """
result = []
group = []
for e in iterable:
if e == val:
if group: # ignore empty groups
result.append(group)
group = [] # start new
else:
group.append(e)
if group: # last group
result.append(group)
return result
def min_cuts(t):
# All zeroes, finished
if set(t) == set([0]):
return 0
# Single column
if len(t) == 1:
return 1
# All ones, single row
if set(t) == set([1]):
return 1
# If discontinued, add cost of subproblems
if 0 in t:
sub_ts = splitz(t, 0)
return sum(map(min_cuts, sub_ts))
# try removing the largest column and largest row(bottom)
# Pick the cheapest one
else:
t1 = remove_max_col(t)
x = 1 + min_cuts(t1)
t2 = remove_bottom_row(t)
y = 1 + min_cuts(t2)
return min(x, y)
print(min_cuts([4,3,4,3,4]))
print(min_cuts([187, 264, 298, 924, 319]))

Competition practise task (Python)

Not sure if it's the best title. The explanation of what the program is suposed to do is below, my version only works with the first example but it doesn't work in the second when you get for example 1 1 3 1 1 2 because i can't figure out a good way to handle so much variations especially if K is bigger than 3 and the limit is 50. My version:
N, K, M = map(int, input().split())
niz = list(map(int, input().split()))
nizk = list(range(1, K+1))
izlazi = []
for r in range(0, M):
operacija = list(map(int, input().split()))
index = 0
if operacija[0] == 2:
nizkk = []
for z in range(0, len(nizk)):
if nizk[z] in niz:
continue
else:
izlazi.append(-1)
break
for p in range(0, N):
if niz[p] not in nizkk:
nizkk.append(niz[p])
nizkk.sort()
if nizkk == nizk:
index = p
izlazi.append(index+1)
break
else:
continue
else:
index, repl = map(int, operacija[1:])
niz[index - 1] = repl
print(izlazi)
In the first line of the input there should be N, K, M (1 <= N, M <= 100k, 1 <= K <= 50, you don't need to actually check this the numbers that are tested will always be in those ranges). In the second line of input you put a list of numbers which are the lenght of N you entered earlier. M is the number of operations you will do in the following lines of input. There can be 2 operations. If you enter 1 p v(p = index of number you want to replace, v the number you replace it with) or if you enter 2 it needs to find the shortest array of numbers defined by range(1, K+1) in the list of numbers you entered in line 2 and possibly changed with operation 1. If it doesn't exist it should output -1 if it does it should output lenght of numbers in the array you look in(numbers can be like 2, 1, 3 if you're looking for 1, 2, 3, also if you're looking for 1, 2, 3 etc and you have 2, 1, 1, 3 as the shortest one that is the solution and it's lenght is 4). Also the replacement operation doesnt count from 0 but from 1. So watch out when managing lists.
These are the examples you can input in the program ulaz = input, izlaz = ouput:
I have the following idea:
Min length sequence either starts from first element or does not contain first element and hence equals to min length of the same sequence without first element.
So we have recursion here.
For sequence [1,1,3,2,1,1] and [1,2,3] we will have:
Min length from start element [1,1,3,2,1,1] is 4
Min length from start element __[1,3,2,1,1] is 3
Min length from start element ____[3,2,1,1] is 3
Min length from start element ______[2,1,1] is -1
Can stop here.
Result is minimum for [4,3,3] = 3
You have already implemented the part for min length, if it starts from the first element. Need now extract it as a function and create a recursive function.
Some metacode:
function GetMinLength(seq)
{
minLengthFromFirstElement = GetMinLenthFromFirstElement(seq)
minLengthFromRest = GetMinLength(seq[1:]) //recusive call
return Min(minLengthFromFirstElement, minLengthFromRest )//-1 results should not count, add extra code to handle it
}
Unfortunately I don't know python, but I can provide code on F# in case you need it.
EDIT:
Try this:
N, K, M = map(int, input().split())
niz = list(map(int, input().split()))
nizk = list(range(1, K+1))
izlazi = []
def GetMinLengthStartingFromFirstElement(seq):
nizkk = []
for z in range(0, len(seq)):
if seq[z] in nizk:
continue
else:
return -1
for p in range(0, len(seq)):
if seq[p] not in nizkk:
nizkk.append(seq[p])
nizkk.sort()
if nizkk == nizk:
index = p
return index+1
else:
continue
return -1
def GetMinLength(seq):
if len(seq) == 0:
return -1
else:
curMinLength = GetMinLengthStartingFromFirstElement(seq)
if curMinLength == -1:
return -1
minLengthFromRest = GetMinLength(seq[1:])
if minLengthFromRest > -1:
return min(curMinLength,minLengthFromRest)
else:
return curMinLength;
for r in range(0, M):
operacija = list(map(int, input().split()))
index = 0
if operacija[0] == 2:
minLength = GetMinLength(niz)
izlazi.append(minLength)
else:
index, repl = map(int, operacija[1:])
niz[index - 1] = repl
print(izlazi)

Python: while loop inside else

def is_prime(x):
count = 1
my_list = []
while count > 0 and count < x:
if x % count == 0:
my_list.append(x/count)
count += 1
return my_list
my_list = is_prime(18)
def prime(x):
my_list2 = []
for number in my_list:
if number <= 2:
my_list2.append(number)
else:
count = 2
while count < number:
if number % count == 0:
break
else:
my_list2.append(number)
count += 1
return my_list2
print prime(18)
Just started out with Python. I have a very simple question.
This prints: [9, 3, 2].
Can someone please tell me why the loop inside my else stops at count = 2? In other words, the loop inside my loop doesn't seem to loop. If I can get my loop to work, hopefully this should print [2, 3]. Any insight is appreciated!
Assuming that my_list2 (not a very nice name for a list) is supposed to contain only the primes from my_list, you need to change your logic a little bit. At the moment, 9 is being added to the list because 9 % 2 != 0. Then 9 % 3 is tested and the loop breaks but 9 has already been added to the list.
You need to ensure that each number has no factors before adding it to the list.
There are much neater ways to do this but they involve things that you may potentially find confusing if you're new to python. This way is pretty close to your original attempt. Note that I've changed your variable names! I have also made use of the x that you are passing to get_prime_factors (in your question you were passing it to the function but not using it). Instead of using the global my_list I have called the function get_factors from within get_prime_factors. Alternatively you could pass in a list - I have shown the changes this would require in comments.
def get_factors(x):
count = 1
my_list = []
while count > 0 and count < x:
if x % count == 0:
my_list.append(x/count)
count += 1
return my_list
# Passing in the number # Passing in a list instead
def get_prime_factors(x): # get_prime_factors(factors):
prime_factors = []
for number in get_factors(x): # for number in factors:
if number <= 2:
prime_factors.append(number)
else:
count = 2
prime = True
while count < number:
if number % count == 0:
prime = False
count += 1
if prime:
prime_factors.append(number)
return prime_factors
print get_prime_factors(18)
output:
[3, 2]
Just to give you a taste of some of the more advanced ways you could go about doing this, get_prime_factors could be reduced to something like this:
def get_prime_factors(x):
prime_factors = []
for n in get_factors(x):
if n <= 2 or all(n % count != 0 for count in xrange(2, n)):
prime_factors.append(n)
return prime_factors
all is a built-in function which would be very useful here. It returns true if everything it iterates through is true. xrange (range on python 3) allows you to iterate through a list of values without manually specifying a counter. You could go further than this too:
def get_prime_factors(x):
return [n for n in get_factors(x) if n <= 2 or all(n % c != 0 for c in xrange(2, n))]

Categories