Permutations in python 2.5.2 - python

I have a list of numbers for input, e.g.
671.00
1,636.00
436.00
9,224.00
and I want to generate all possible sums with a way to id it for output, e.g.:
671.00 + 1,636.00 = 2,307.00
671.00 + 436.00 = 1,107.00
671.00 + 9,224.00 = 9,224.00
671.00 + 1,636.00 + 436.00 = 2,743.00
...
and I would like to do it in Python
My current constrains are:
a) I'm just learning python now (that's part of the idea)
b) I will have to use Python 2.5.2 (no intertools)
I think I have found a piece of code that may help:
def all_perms(str):
if len(str) <=1:
yield str
else:
for perm in all_perms(str[1:]):
for i in range(len(perm)+1):
#nb str[0:1] works in both string and list contexts
yield perm[:i] + str[0:1] + perm[i:]
( from these guys )
But I'm not sure how to use it in my propose.
Could someone trow some tips and pieces of code of help?
cheers,
f.

Permutations are about taking an ordered set of things and moving these things around (i.e. changing order). Your question is about combinations of things from your list.
Now, an easy way of enumerating combinations is by mapping entries from your list to bits in a number. For example, lets assume that if bit #0 is set (i.e. 1), then number lst[0] participates in the combination, if bit #1 is set, then lst[1] participates in the combination, etc. This way, numbers in range 0 <= n < 2**(len(lst)) identify all possible combinations of lst members, including an empty one (n = 0) and the whole lst (n = 2**(len(lst)) - 1).
You need only combinations of 2 items or more, i.e. only those combination IDs that have at least two nonzero bits in their binary representation. Here is how to identify these:
def HasAtLeastTwoBitsSet(x) :
return (x & (x-1)) != 0
# Testing:
>>> [x for x in range(33) if HasAtLeastTwoBitsSet(x)]
[3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
Next step is to extract a combination of list members identified by a combination id. This is easy, thanks to the power of list comprehensions:
def GetSublistByCombination(lst, combination_id) :
res = [x for (i,x) in enumerate(lst) if combination_id & (1 << i)]
return res
# Testing:
>>> GetSublistByCombination([0,1,2,3], 1)
[0]
>>> GetSublistByCombination([0,1,2,3], 3)
[0, 1]
>>> GetSublistByCombination([0,1,2,3], 12)
[2, 3]
>>> GetSublistByCombination([0,1,2,3], 15)
[0, 1, 2, 3]
Now let's make a generator that produces all sums, together with their string representations:
def IterAllSums(lst) :
combinations = [i for i in range(1 << len(lst)) if HasAtLeastTwoBitsSet(i)]
for comb in combinations :
sublist = GetSublistByCombination(lst, comb)
sum_str = '+'.join(map(str, sublist))
sum_val = sum(sublist)
yield (sum_str, sum_val)
And, finally, let's use it:
>>> for sum_str, sum_val in IterAllSums([1,2,3,4]) : print sum_str, sum_val
1+2 3
1+3 4
2+3 5
1+2+3 6
1+4 5
2+4 6
1+2+4 7
3+4 7
1+3+4 8
2+3+4 9
1+2+3+4 10

The code below generates all "subsets" of a given list (except the empty set), i.e. it returns a list of lists.
def all_sums(l): #assumes that l is non-empty
if len(l)==1:
return ([[l[0]]])
if len(l)==0:
return []
result = []
for i in range(0,len(l)):
result.append([l[i]])
for p in all_sums(l[i+1:]):
result.append([l[i]]+p)
return result
Now you could just write a short function doit for output also:
def doit(l):
mylist = all_sums(l)
print mylist
for i in mylist:
print str(i) + " = " + str(sum(i))
doit([1,2,3,4])

With itertools (Python >=2.6) would be:
from itertools import *
a=[1,2,3,4]
sumVal=[tuple(imap(sum,combinations(a,i))) for i in range(2,len(a)+1)]

Related

Compute pairs from two lists that when multiplied make a perfect square [duplicate]

This question already has answers here:
Find pairs in two arrays such that when multiplied becomes a perfect square
(4 answers)
Closed 3 years ago.
You are given two lists and you have to find out the pairs which make a perfect square.
For example in:
a = [2, 6, 10, 13, 17, 18]
b = [3, 7, 8, 9, 11, 15]
There are two pairs (2,8) and (8,18).
Is there any method efficient than the brute-force?
Here is my code which has a time complexity of O(n*m)
(where n is the length of a and m is the length of b).
pl = []
a = [ 2, 6, 10, 13, 17,18]
b = [ 3, 7, 8, 9, 11, 15 ]
i = 0
while(i < len(a)):
j = 0
while(j < len(b)):
p = a[i]*b[j]
n = p**0.5
u = int(n)
if n == u:
pl.append((a[i],b[j]))
j = j+1
i = i+1
print(pl)
This question has been asked before using C# here, but I don't get what they mean by "All we would need to store for each number is which of its prime factors have an odd count", so I can't implement this in my Python code.
Can someone explain to me how we might implement an efficient solution in Python?
The logic described in the linked question is as follows. A perfect square's prime factors will always be in pairs. For example, 36 = 2*2*3*3. We have two 3s and two 2s. Therefore, if we take any two numbers that multiply to form a perfect square, if we take the combined counts of each of their prime factors, we will also get even counts.
For example, 8 = 2*2*2, and 18 = 2*3*3. Combined, we get four 2s and two 3s.
Below is some Python code that implements this algorithm, using collections.Counter and sets to remove duplicates. First, we precompute all prime factorizations for each unique element in a and b to avoid redundancy, and store this in a dictionary. Then, we loop over pairs of elements from a and b, using itertools.product, and combine the prime factor counts. If every count is even, we add the sorted pair to a set.
Code:
from collections import Counter
import itertools
def prime_factors(n):
"""
https://stackoverflow.com/a/22808285/12366110
"""
i = 2
factors = []
while i * i <= n:
if n % i:
i += 1
else:
n //= i
factors.append(i)
if n > 1:
factors.append(n)
return Counter(factors)
a = [2, 6, 10, 13, 17,18]
b = [3, 7, 8, 9, 11, 15]
prime_factors = {i: prime_factors(i) for i in set(a) | set(b)}
rv = set()
for a, b in itertools.product(a, b):
combined_counts = prime_factors[a] + prime_factors[b]
if all(v%2 == 0 for v in combined_counts.values()):
rv.add(tuple(sorted([a, b])))
Output:
>>> rv
{(2, 8), (8, 18)}

Count all sequences in a list

My self-learning task is to find how many sequences are on the list. A sequence is a group of numbers, where each is one 1 bigger than the previous one. So, in the list:
[1,2,3,5,8,10,12,13,14,15,17,19,21,23,24,25,26]
there are 3 sequences:
1,2,3
12,13,14,15
23,24,25,26
I've spent few hours and got a solution, which I think is a workaround rather than the real solution.
My solution is to have a separate list for adding sequences and count the attempts to update this list. I count the very first appending, and every new appending except for the sequence, which already exists.
I believe there is a solution without additional list, which allows to count the sequences itself rather than the list manipulation attempts.
numbers = [1,2,3,5,8,10,12,13,14,15,17,19,21,23,24,25,26]
goods = []
count = 0
for i in range(len(numbers)-1):
if numbers[i] + 1 == numbers[i+1]:
if goods == []:
goods.append(numbers[i])
count = count + 1
elif numbers[i] != goods[-1]:
goods.append(numbers[i])
count = count + 1
if numbers[i+1] != goods[-1]:
goods.append(numbers[i+1])
The output from my debugging:
Number 1 added to: [1]
First count change: 1
Number 12 added to: [1, 2, 3, 12]
Normal count change: 2
Number 23 added to: [1, 2, 3, 12, 13, 14, 15, 23]
Normal count change: 3
Thanks everyone for your help!
Legman suggested the original solution I failed to implemented before I end up with another solution in this post.
MSeifert helped to find a the right way with the lists:
numbers = [1,2,3,5,8,10,12,13,14,15,17,19,21,23,24,25,26]
print("Numbers:", numbers)
goods = []
count = 0
for i in range(len(numbers)-1):
if numbers[i] + 1 == numbers[i+1]:
if goods == []:
goods.append([numbers[i]])
count = count + 1
elif numbers[i] != goods[-1][-1]:
goods.append([numbers[i]])
count = count + 1
if numbers[i+1] != goods[-1]:
goods[-1].extend([numbers[i+1]])
print("Sequences:", goods)
print("Number of sequences:", len(goods))
One way would be to iterate over pairwise elements:
l = [1,2,3,5,8,10,12,13,14,15,17,19,21,23,24,25,26]
res = [[]]
for item1, item2 in zip(l, l[1:]): # pairwise iteration
if item2 - item1 == 1:
# The difference is 1, if we're at the beginning of a sequence add both
# to the result, otherwise just the second one (the first one is already
# included because of the previous iteration).
if not res[-1]: # index -1 means "last element".
res[-1].extend((item1, item2))
else:
res[-1].append(item2)
elif res[-1]:
# The difference isn't 1 so add a new empty list in case it just ended a sequence.
res.append([])
# In case "l" doesn't end with a "sequence" one needs to remove the trailing empty list.
if not res[-1]:
del res[-1]
>>> res
[[1, 2, 3], [12, 13, 14, 15], [23, 24, 25, 26]]
>>> len(res) # the amount of these sequences
3
A solution without zip only requires small changes (the loop and the the beginning of the loop) compared to the approach above:
l = [1,2,3,5,8,10,12,13,14,15,17,19,21,23,24,25,26]
res = [[]]
for idx in range(1, len(l)):
item1 = l[idx-1]
item2 = l[idx]
if item2 - item1 == 1:
if not res[-1]:
res[-1].extend((item1, item2))
else:
res[-1].append(item2)
elif res[-1]:
res.append([])
if not res[-1]:
del res[-1]
Taken from python itertools documentation, as demonstrated here you can use itemgetter and groupby to do that using only one list, like so:
>>> from itertools import groupby
>>> from operator import itemgetter
>>>
>>> l = [1, 2, 3, 5, 8, 10, 12, 13, 14, 15, 17, 19, 21, 23, 24, 25, 26]
>>>
>>> counter = 0
>>> for k, g in groupby(enumerate(l), lambda (i,x):i-x):
... seq = map(itemgetter(1), g)
... if len(seq)>1:
... print seq
... counter+=1
...
[1, 2, 3]
[12, 13, 14, 15]
[23, 24, 25, 26]
>>> counter
3
Notice: As correctly mentioned by #MSeifert, tuple unpacking in the signature is only possible in Python 2 and it will fail on Python 3 - so this is a python 2.x solution.
This could be solved with dynamic programming. If you only want to know the number of sequences and don't actually need to know what the sequences are you should be able to do this with only a couple of variables. Realistically, as you're going through the list you only really need to know if you are currently in a sequence, if not if the next one is incremented by 1 making this the beginning of a sequence and if so is the next one greater than 1 making it the exit of a sequence. After that, you just need to make sure to end the loop one cell before the end of the list since the last cell cant form a sequence by itself and so that it doesn't cause an error when you're performing a check. Below is example code
isSeq=false
for i in range(len(numbers)-1):
if isSeq==false:
if numbers[i]+1==numbers[i+1]:
isSeq=true
count=count+1
elif
if numbers[i]+1!=numbers[i+1]:
isSeq=false
Here is a link to a dynamic programming tutorial.
https://www.codechef.com/wiki/tutorial-dynamic-programming

Print a line if conditions have not been met

Hello fellow stackoverflowers, I am practising my Python with an example question given to me (actually a Google interview practice question) and ran into a problem I did not know how to a) pose properly (hence vague title), b) overcome.
The question is: For an array of numbers (given or random) find unique pairs of numbers within the array which when summed give a given number. E.G: find the pairs of numbers in the array below which add to 6.
[1 2 4 5 11]
So in the above case:
[1,5] and [2,4]
The code I have written is:
from secrets import *
i = 10
x = randbelow(10)
number = randbelow(100) #Generate a random number to be the sum that we are after#
if number == 0:
pass
else:
number = number
array = []
while i>0: #Generate a random array to use#
array.append(x)
x = x + randbelow(10)
i -= 1
print("The following is a randomly generated array:\n" + str(array))
print("Within this array we are looking for a pair of numbers which sum to " + str(number))
for i in range(0,10):
for j in range(0,10):
if i == j or i>j:
pass
else:
elem_sum = array[i] + array[j]
if elem_sum == number:
number_one = array[i]
number_two = array[j]
print("A pair of numbers within the array which satisfy that condition is: " + str(number_one) + " and " + str(number_two))
else:
pass
If no pairs are found, I want the line "No pairs were found". I was thinking a try/except, but wasn't sure if it was correct or how to implement it. Also, I'm unsure on how to stop repeated pairs appearing (unique pairs only), so for example if I wanted 22 as a sum and had the array:
[7, 9, 9, 13, 13, 14, 23, 32, 41, 45]
[9,13] would appear twice
Finally forgive me if there are redundancies/the code isn't written very efficiently, I'm slowly learning so any other tips would be greatly appreciated!
Thanks for reading :)
You can simply add a Boolean holding the answer to "was at least one pair found?".
initialize it as found = false at the beginning of your code.
Then, whenever you find a pair (the condition block that holds your current print command), just add found = true.
after all of your search (the double for loop`), add this:
if not found:
print("No pairs were found")
Instead of actually comparing each pair of numbers, you can just iterate the list once, subtract the current number from the target number, and see if the remainder is in the list. If you convert the list to a set first, that lookup can be done in O(1), reducing the overall complexity from O(n²) to just O(n). Also, the whole thing can be done in a single line with a list comprehension:
>>> nums = [1, 2, 4, 5, 11]
>>> target = 6
>>> nums_set = set(nums)
>>> pairs = [(n, target-n) for n in nums_set if target-n in nums_set and n <= target/2]
>>> print(pairs)
[(1, 5), (2, 4)]
For printing the pairs or some message, you can use the or keyword. x or y is interpreted as x if x else y, so if the result set is empty, the message is printed, otherwise the result set itself.
>>> pairs = []
>>> print(pairs or "No pairs found")
No pairs found
Update: The above can fail, if the number added to itself equals the target, but is only contained once in the set. In this case, you can use a collections.Counter instead of a set and check the multiplicity of that number first.
>>> nums = [1, 2, 4, 5, 11, 3]
>>> nums_set = set(nums)
>>> [(n, target-n) for n in nums_set if target-n in nums_set and n <= target/2]
[(1, 5), (2, 4), (3, 3)]
>>> nums_counts = collections.Counter(nums)
>>> [(n, target-n) for n in nums_counts if target-n in nums_counts and n <= target/2 and n != target-n or nums_counts[n] > 1]
[(1, 5), (2, 4)]
List your constraints first!
numbers added must be unique
only 2 numbers can be added
the length of the array can be arbitrary
the number to be summed to can be arbitrary
& Don't skip preprocessing! Reduce your problem-space.
2 things off the bat:
Starting after your 2 print statements, the I would do array = list(set(array)) to reduce the problem-space to [7, 9, 13, 14, 23, 32, 41, 45].
Assuming that all the numbers in question will be positive, I would discard numbers above number. :
array = [x for x in array if x < number]
giving [7, 9, 9, 13, 13, 14]
Combine the last 2 steps into a list comprehension and then use that as array:
smaller_array = [x for x in list(set(array)) if x < number]
which gives array == [7, 9, 13, 14]
After these two steps, you can do a bunch of stuff. I'm fully aware that I haven't answered your question, but from here you got this. ^this is the kind of stuff I'd assume google wants to see.

Multiply element of list with other elements in the same list

I want to multiply an element of a list with all other elements.
For example:
def product(a,b,c):
return (a*b, a*c, a*b*c)
I have done this
def product(*args):
list = []
for index,element in enumerate(args):
for i in args:
if (args[index]*i) not in list:
list.append(args[index]*i)
return list
but this gives me [a*a, a*b,a*c, b*b] etc. I don't want the a*a, b*b, c*c bit in there.
you could check for equality
if (args[index]*i) not in list and args[index] != i:
itertools is your friend here:
from itertools import combinations
from functools import reduce, partial
from operator import mul
# Make a sum-like function for multiplication; I'd call it product,
# but that overlaps a name in itertools and our own function
multiplyall = partial(reduce, mul)
def product(*args):
# Loop so you get all two elements combinations, then all three element, etc.
for n in range(2, len(args) + 1):
# Get the combinations for the current combo count
for comb in combinations(args, n):
# Compute product and yield it
# yielding comb as well just for illustration
yield comb, multiplyall(comb)
I made it a generator function, because frankly, almost any function that's just slowly building a list element by element and returning it should really be a generator function (if the caller wants a list, they just do mylist = list(generatorfunc(...))), making it easier to use iteratively without blowing main memory when many arguments are passed.
Example usage:
>>> for pieces, prod in product(2, 3, 4):
print ' * '.join(map(str, pieces)), '=', prod
Which outputs:
2 * 3 = 6
2 * 4 = 8
3 * 4 = 12
2 * 3 * 4 = 24
So if the values are 2, 3, 4, 5 you want all and only these products:
2*3=6, 2*4=8, 2*5=10, 2*3*4=24, 2*3*5=30, 2*4*5=40, 2*3*4*5=120
This means taking all combinations of 3, 4, 5 and then multiplying them togther with 2. The itertools module has a combinations function, and reduce can be used in conjunction with operator.mul to do the calculation:
def product(first, *other):
for n in range(1, len(other) + 1):
for m in combinations(other, n):
yield reduce(mul, m, first)
list(product(2, 3, 4, 5))
Output:
[6, 8, 10, 24, 30, 40, 120]
Does your list have duplicate elements, like [2, 3, 4, 2]?
If it does not, here is a one liner:
First, with tags to illustrate the pattern:
a = ['a1','a2','a3']
lsta = [[x+y for y in [z for z in a if z != x]] for x in a]
lsta
[['a1a2', 'a1a3'], ['a2a1', 'a2a3'], ['a3a1', 'a3a2']]
And here, with numbers:
a =[2,3,4,5]
print [[x*y for y in [z for z in a if z != x]] for x in a]
[[6, 8, 10], [6, 12, 15], [8, 12, 20], [10, 15, 20]]
or the sum of the products, if you wish:
a =[2,3,4,5]
print [sum([x*y for y in [z for z in a if z != x]]) for x in a]
[24, 33, 40, 45]
If the list has duplicates, it gets more complicated. Do you want the first occurrence and the second occurrence of 2 in [2,3,4,2] to be separately calculated (you might need that for some purposes even though you will get the same value for both)?

Exercise python: how to group element in a list?

I've tried to solve the following exercise, without using datetime!
Exercise:
Given a list of int, such that the First three int represent a date,
the second three elementi represent a date etc..modify lst by grouping
every triple in One string with the numbers separeted by "/".
Example:
lst = [1, 2, 2013, 23, 9, 2011, 10, 11, 2000]
groupd(lst)
lst
['1/2/2013', '23/9/2011', '10/11/2000']
My attempt:
lst = [1, 2, 2013, 23, 9, 2011, 10, 11, 2000].
stri = str(lst).
def groupd(lst):.
cont = 1.
a = (stri.replace(',', '/')).
for x in lst:.
if len[x]>2:.
lst.insert(lst[0],a )].
print(a).
print(groupd(lst)).
PS: sorry for my english!! Thank you all!
You can use zip to create tuples and then format them to your strings:
>>> ['%d/%d/%d' % parts for parts in zip(lst[::3], lst[1::3], lst[2::3])]
['1/2/2013', '23/9/2011', '10/11/2000']
Starting from an offset (first argument to slicing) while skipping items (third argument to slicing) allows for windowing behavior.
More generically:
>>> N = 3
>>> ['/'.join(['%d'] * N) % parts for parts in zip(*[lst[start::N] for start in range(N)])]
['1/2/2013', '23/9/2011', '10/11/2000']
You can group the list by it's index using groupby from itertools:
from itertools import groupby
['/'.join(str(i[1]) for i in g) for _, g in groupby(enumerate(lst), key = lambda x: x[0]/3)]
# ['1/2/2013', '23/9/2011', '10/11/2000']
This is more of a functional approach where the answer is passed around with the recursive function.
lst1 = [1, 2, 2013, 23, 9, 2011, 10, 11, 2000]
lst2 = []
lst3 = [1,2, 2015]
lst4 = [1,2]
lst5 = [1]
lst6 = [1,2,2013, 23, 9]
def groupToDate(lst, acc):
if len(lst) < 3:
return acc
else:
# take first elements in list
day = lst[0]
month = lst[1]
year = lst[2]
acc.append(str(day) + '/' + str(month) + '/' + str(year))
return groupToDate(lst[3:len(lst)], acc)
print(groupToDate(lst1, []))
print(groupToDate(lst2, []))
print(groupToDate(lst3, []))
print(groupToDate(lst4, []))
print(groupToDate(lst5, []))
print(groupToDate(lst6, []))
It's also the basic approach of solving such problem if you don't want to use list comprehensions or groupby

Categories