merge values in list - python

I have a list in which I have to merge values like this:
list: [1, 1, 2, 1, 1, 0]
result: [2, 2, 2, 0, 0, 0]
It merges from left to right, and only merges 2 of the same numbers per time.
the first two values are added up (1 + 1)
the 2 is shifted to the left
the other two values are added up as well (1 + 1) and shifted
the list is be padded from the right with zero's so the length of the list stays the same
(edit) any zero's in the list will be at the end, not in the middle
I'm quite new to Python, and don't have a clear idea on how to approach this. Any help is appreciated.

How about something simple and straight-forward:
def merge_pairs(a):
out = []
i = 0
while i < len(a):
if i < len(a) - 1 and a[i] == a[i + 1]:
out.append(a[i] + a[i + 1])
i += 2
else:
out.append(a[i])
i += 1
out += [0] * (len(a) - len(out))
return out
Not 100% sure this is what you require, but perhaps it can be a start.
The single test you provided seems to work:
>>> merge_pairs([1, 1, 2, 1, 1, 0])
[2, 2, 2, 0, 0, 0]

Related

How to reduce higher values to 1s and sort the ones from right to left

I want to make a python program that quickly reduces a number greater than 1 in an array/list and places it in an empty spot before it. Say we have:
li = [4,1,0,0,0,1,3,0]
we'd get:
rtr = [1,1,0,1,1,1,1,0]
Note how the 4 turns into a 1 because it's already to the left and then the 3 gets divided into 2 positions before the 1 that has already been taken. Can anyone help me with this problem?
You can iterate the list from end to start and keep track of the sum you collect from the values. When you have a non zero sum, take 1 from it to populate the result list, and otherwise put a 0 in the result list.
Here is how that could work:
def spread(lst):
carry = 0
res = []
for i in reversed(lst):
carry += i
res.append(int(carry > 0))
if carry:
carry -= 1
return list(reversed(res))
lst = [4, 1, 0, 0, 0, 1, 3, 0]
print(spread(lst)) # [1, 1, 0, 1, 1, 1, 1, 0]
Using numpy
def fun(l):
s = np.array(l[::-1])
for i in range(len(s)):
if s[i] != 1 and s[i] != 0:
x = s[i+1:]
x[(x == 0).nonzero()[0][:s[i]-1]] = 1
s[i] = 1
return s[::-1].tolist()
print (fun([4,1,0,0,0,1,3,0]))
print (fun([0, 10]))
Output:
[1, 1, 0, 1, 1, 1, 1, 0]
[1, 1]

Algorithm Lists with python

I am trying to find an algorithm to solve the following problem with python.
Given a list of integers, check if after dropping two elements of the list, is it possible to divide the list in 3 where the sum of the consecutive numbers are equals.
For example:
A = [1, 3, 4, 2, 2, 2, 1, 1, 2] should return TRUE because we would drop the elements in bold the list can be slit in [1, 3], [2, 2], [2, 1, 1] where all sum 4
B = [1, 1, 1, 1, 1, 1] should return FALSE
C = [1, 1, 3, 1, 1, 1034, 5, 9900, 1, 2] should also return FALSE because eventhough after droping the numbers in bold you can sum have the numbers to sum 5 (1,1,3), (5), (1, 1, 1, 2) the list should be sorted first and that's not allowed.
I have come with a solution that seems to work but it is very, very bad and not sure if always work, and the complexity is too high when should be O(n)
I don't know how to iterate removing 2 numbers from a list without having a complexity of O(n^2)
Thanks
It is indeed possible to solve this problem in O(n) (given that the integers are positive) where n is the size of your array A.
It can be surprising since the problem reminds of the bin packing, or subset sum problems, but the fact that we look for consecutive sum in the array makes the task much easier.
Here is a python code doing it:
def find_couple_list_sum(int_list):
n = len(int_list)
left_sum = [0 for _ in range(n)]
for i in range(1, n):
left_sum[i] = left_sum[i-1] + int_list[i-1]
right_sum = [0 for _ in range(n)]
for j in range(n-2, -1, -1):
right_sum[j] = right_sum[j+1] + int_list[j+1]
total_sum = sum(int_list)
print(left_sum)
print(right_sum)
print(total_sum)
i, j = 0, n-1
while True:
print(i, j)
mid_sum = total_sum - left_sum[i] - right_sum[j] - int_list[i] - int_list[j]
if left_sum[i] == right_sum[j] and left_sum[i] == mid_sum:
return i, j
elif i == j:
return None, None
elif left_sum[i] < right_sum[j]:
i += 1
else:
j -= 1
int_list = [1, 3, 4, 2, 2, 2, 1, 1, 2]
print(find_couple_list_sum(int_list))
The whole thing runs indeed in O(n).

Generate unique binary permutations in python

Please, how can I get all these binary permutations, but without repetition in Python?
a = list(itertools.permutations([1, 1, 0, 0]))
for i in range(len(a)):
print a[i]
(1, 1, 0, 0)
(1, 1, 0, 0)
(1, 0, 1, 0)
...
It would be great if it would be roughly efficient since I'll have to do that with a list of even 30 elements like this.
As #Antti said in a comment, this is equivalent to looking for combinations of positions of the input list which determine which bits in the output are 1.
from itertools import combinations
def binary_permutations(lst):
for comb in combinations(range(len(lst)), lst.count(1)):
result = [0] * len(lst)
for i in comb:
result[i] = 1
yield result
for perm in binary_permutations([1, 1, 0, 0]):
print(perm)
Output:
[1, 1, 0, 0]
[1, 0, 1, 0]
[1, 0, 0, 1]
[0, 1, 1, 0]
[0, 1, 0, 1]
[0, 0, 1, 1]
Here's the algorithm from the accepted answer to the generic algorithm question, adapted into Python 3 (should work in Python 2.7+). The function generate(start, n_bits) will generate all n-bit integers starting from start lexicographically.
def generate(start, n_bits):
# no ones to permute...
if start == 0:
yield 0
return
# fastest count of 1s in the input value!!
n_ones = bin(start).count('1')
# the minimum value to wrap to when maxv is reached;
# all ones in LSB positions
minv = 2 ** n_ones - 1
# this one is just min value shifted left by number of zeroes
maxv = minv << (n_bits - n_ones)
# initialize the iteration value
v = start
while True:
yield v
# the bit permutation doesn't wrap after maxv by itself, so,
if v == maxv:
v = minv
else:
t = ((v | ((v - 1))) + 1)
v = t | (((((t & -t)) // ((v & -v))) >> 1) - 1)
# full circle yet?
if v == start:
break
for i in generate(12, 4):
print('{:04b}'.format(i))
Prints
1100
0011
0101
0110
1001
1010
If list output is generated, this can then be decorated:
def generate_list(start):
n_bits = len(start)
start_int = int(''.join(map(str, start)), 2)
# old and new-style formatting in one
binarifier = ('{:0%db}' % n_bits).format
for i in generate(start_int, n_bits):
yield [int(j) for j in binarifier(i)]
for i in generate_list([1, 1, 0, 0]):
print(i)
prints
[1, 1, 0, 0]
[0, 0, 1, 1]
[0, 1, 0, 1]
[0, 1, 1, 0]
[1, 0, 0, 1]
[1, 0, 1, 0]
What is nice about this algorithm is that you can resume it at any point. If you find a way to calculate good starting points, it is possible to parallelize too. And the numbers should be more compact than lists, so you could use them if possible.
What you are trying to do is choose two positions at which the element will be 1.
Code
from itertools import combinations
def bit_patterns(size, ones):
for pos in map(set, combinations(range(size), ones)):
yield [int(i in pos) for i in range(size)]
Output
>>> print(*bit_patterns(4, 2), sep='\n')
[1, 1, 0, 0]
[1, 0, 1, 0]
[1, 0, 0, 1]
[0, 1, 1, 0]
[0, 1, 0, 1]
[0, 0, 1, 1]
Alternative
A fun alternative is to see the desired output as the binary representations which have only two ones. We can use this definition to get the output you want.
from itertools import combinations
def bit_patterns(size, ones):
for t in combinations([1 << i for i in range(size)], ones):
yield [int(n) for n in f'{sum(t):0{size}b}']
Here is a recursive solution:
def bin_combs_iter(ones, zeros):
if not zeros:
yield [1] * ones
elif not ones:
yield [0] * zeros
else:
for x in bin_combs_iter(ones - 1, zeros):
x.append(1)
yield x
for x in bin_combs_iter(ones, zeros - 1):
x.append(0)
yield x
def bin_combs(ones, zeros):
return list(bin_combs_iter(ones, zeros))

find the number of separated pairs in array

Good Morning,
I have a numpy array like:
[0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0]
and I would like to find the number of separated pairs of 1.
Three (or more) consecutive 1s count also as pair, i.e.: in this example the returned number should be 3.
What is the best technique to achieve this?
Thanks a lot!
using itertools.groupby,
k hold the unique key 0/1 based list lst below, g hold the correspond group iterator for the unique key k
import itertools
target = 1
lst = [0,1,1,0,1,1,1,0,0,1,0,1,1,0]
pair_count = 0
for k,g in itertools.groupby(lst):
if k==target and len(list(g))>1: # match target and more than 1 count as pair
pair_count += 1
# pair_count = 3
You can easily implement it yourself, see my code below:
l = [0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0]
# Create flag to skip all elements that have >2 1 in sequence
added = False
pairs_counter = 0
for i in range(1, len(l)):
rem = l[i - 1]
if l[i] == 1 and rem == 1 and not added:
added = True
pairs_counter +=1
if l[i] == 0:
added = False
print (pairs_counter)
Complexity of this method is O(n)
I would go for something like this:
a = [0 1 1 0 1 1 1 0 0 1 0 1 1 0]
sum([
a[i-2] == a[i-1] == 1 and a[i] == 0
for i in xrange(2,len(a))
]) + (len(a) > 2 and a[-1] == a[-2] == 1)
It just keeps adding Trues and Falses together. I guess some people will find it ugly, I think it is OK.
It should be noted though, that if the list is really large, this is not a good option, since it creates the list of bools in memory. It would be easy to avoid that.
Use itertools.groupby and sum
sum(1 for target, group_count in itertools.groupby(lst)
if target == 1 and len(list(group_count)) >= 2)

Bubble sort in Python, what are these arguments for?

def bbsort(list1):
for passnum in range(len(list1)-1,0,-1):
for i in range (passnum):
if list1[i]>list1[i+1]:
temp =list1[i]
list1[i] = list1[i+1]
list1[i+1] = temp
This is code for a bubble sort, but what is the use of -1,0,-1 in the while condition?
You are looking at arguments to the range() function; the numbers set up the range to start at the list length, minus one, then stepping down to 1 (the end point is not included in the range).
So there are 3 arguments, the first is len(list1) - 1, the second is 0 and the third is -1.
Say the list is length 5, then range(5 - 1, 0, -1) will produce a list with 4, 3, 2, and 1:
>>> list(range(4, 0, -1))
[4, 3, 2, 1]
The for loop steps over these values:
>>> for i in range(4, 0, -1):
... print(i)
...
4
3
2
1
The for loop assigns these numbers to passnum, and the next nested loop uses that to create a new range(). The first time, that inner range will go from 0 to len(list1) - 1 (exclusive), the next time from 0 to len(list1) - 2, etc, until the last time, when it'll run from 0 to 1, always excluding the stop index. For a list of length 5, that means the inner loop first assigns 0, 1, 2, 3 to i, then 0, 1, 2, then 0, 1, then 0.
The code is missing an opportunity to use Python sequence assignment to swap to elements:
def bbsort(list1):
for passnum in range(len(list1) - 1, 0, -1):
for i in range (passnum):
if list1[i] > list1[i + 1]:
list1[i], list1[i + 1] = list1[i + 1], list[i]
The first -1 subtracts 1 from the length of the input list because the indexes start from 0, not 1. The 0 is the start number(only neccesary to get to the next parameter). The 2nd -1 is the step so that tells it to move backwards through the range.
Example:
range(5, 2, -1) is [5, 4, 3]
equal to range(start, stop, step)

Categories