I would like to obtain a list of integers whose cubes add up to a target number. The integers in the list must all be distinct. My code works for all cases except for
n=sum([n*n*n for n in range(1001)])
...where the expected result is [6303, 457, 75, 14, 9, 7,5, 4] instead my output in my program is [6303, 457, 75, 15, 8, 4, 3, 2, 1]. How can I correct my program to correctly output the expected result?
def sum_of_cubes(n):
original=n
i=1
lst=[]
tot=0
while i**3<=n:
i+=1
lst.append(i-1)
n-=(i-1)**3
for j in range(lst[0],0,-1):
if j**3<n:
lst.append(j)
n-=j**3
if n==1:
lst.append(n)
for i in lst:
tot+=i**3
#if original-tot>1:
#return None
return lst
n=sum([n*n*n for n in range(1001)])
print(sum_of_cubes(n))
You can implement a backtracking search algorithm using a stack (instead of recursion). Of course, being an exponential algorithm, it only works for inputs of relatively small size.
from math import *
def sum_of_cubes(n):
bag = []
stack = [(floor(n**(1/3)), n, [])] # simulated initial call
while stack:
(k, n, s) = stack.pop()
if n == 0: bag.append(s)
elif k > 0:
stack.append((k-1, n, s)) # simulated recursive call
if n-k**3 >= 0: stack.append((k-1, n-k**3, [k]+s)) # simulated recursive call
return bag
Examples:
>>> sum_of_cubes(8)
[[2]]
>>> sum_of_cubes(11)
[]
>>> sum_of_cubes(855)
[[1, 5, 9], [7, 8]]
>>> sum_of_cubes(3473)
[[9, 14], [1, 6, 8, 14], [1, 3, 4, 5, 8, 14], [2, 3, 8, 9, 13], [1, 2, 4, 5, 6, 11, 12], [1, 2, 3, 5, 7, 8, 9, 12], [2, 4, 5, 6, 9, 10, 11]]
>>> sum_of_cubes(sum(n**3 for n in range(11)))
[[1, 4, 6, 14], [2, 3, 4, 9, 13], [1, 2, 3, 4, 6, 8, 13], [1, 2, 6, 7, 9, 12], [1, 2, 3, 4, 5, 7, 9, 12], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
>>> sum_of_cubes(15000)
[[2, 3, 5, 6, 9, 12, 23], [3, 5, 8, 10, 11, 14, 21], [3, 9, 11, 17, 20], [1, 3, 6, 8, 11, 17, 20], [2, 4, 5, 6, 7, 11, 17, 20], [2, 3, 5, 14, 16, 20], [3, 5, 9, 14, 15, 20], [1, 3, 5, 6, 8, 14, 15, 20], [6, 8, 11, 13, 14, 20], [3, 4, 5, 8, 11, 13, 14, 20], [2, 4, 5, 10, 11, 12, 14, 20], [5, 7, 9, 11, 12, 14, 20], [1, 5, 6, 7, 8, 11, 12, 14, 20], [5, 6, 7, 8, 9, 10, 11, 14, 20], [2, 3, 5, 7, 8, 9, 11, 12, 13, 20], [3, 5, 6, 8, 15, 17, 18], [5, 6, 7, 8, 11, 12, 17, 18], [1, 2, 5, 7, 8, 9, 11, 14, 15, 18], [2, 3, 5, 6, 8, 12, 15, 16, 17], [2, 3, 4, 5, 6, 11, 13, 14, 15, 17]]
Remark If you are interested in getting only one of the possible solutions, you can use this other version of the algorithm:
def sum_of_cubes(n):
stack = [(floor(n**(1/3)), n, [])]
while stack:
(k, n, s) = stack.pop()
if n == 0: return s
elif k > 0:
stack.append((k-1, n, s))
if n-k**3 >= 0: stack.append((k-1, n-k**3, [k]+s))
Example:
>>> sum_of_cubes(sum(n**3 for n in range(1001)))
[4, 5, 7, 9, 14, 75, 457, 6303]
The problem with your algorithm is this:
while i**3 <= n:
i += 1
lst.append(i-1)
It appends "15" which is lesser than "14"+"9", so your algorytm would not also work for n=3473 for example, wich is 14^3+9^3.
Solution: The only thing I can think of it is maybe to make the forbidden list, basically your algorithm first tries the original one, if it fails, try variants that exclude 1-all member/s of that unsuccessful list...or something like that maybe?
Also you should have checker like:
if original != tot:
doSomething()
lst=[]
etc....
This should get you to right track, good luck.
EDIT:
Ok so recursive function would solve the problem...if you had infinite CPU power/time, i will show solution to eliminate ONE black member, if there would be 2 or more numbers like 15, it will fail.
if original != tot:
lst= doSomething(original, lst)
doSomething(n,lst):
returnList=[]
newList=[]
orig=n
for blackMember in lst:
n=orig
#basicaly what you wrote before but try add before append and lowering n
if theNumberYOuWantToAdd != blackMember:
n -= theNumberYOuWantToAdd**3
newList.append(theNumberYOuWantToAdd)
....
....
....
if tot == original:
returnList = newList
#ofc if you want recursive functions you can do this just...it will freeze on big numbers:
#else:
#superBlastlist = newList + lst #dunno how to connect them, but you have idea
#returnList=doSomething(orig, superBlastlist)
return returnList
So you have idea...
So basically, for 1-2 levels of blacklisting numbers, it would be CPU acceptable, but i would reccomend dont go above 1e10 combinations, then even GPU helping would not save you :D
2nd Idea:
You can also make a list of A^3+B^3 where it's lower than n, then use that, but it will still be beatable by combinations of A+B+C etc..
Related
I have a problem checking multiple conditions at once.
import itertools
def repeats(input1, input2):
return [int(dz) for dz in input1 if int(dz) in input2]
n_combs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
filters = [[[1, 2, 3, 6, 7, 8, 11, 12, 16, 17, 21, 22], [5]], [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [7]], [[20, 21, 22, 23, 24], [2]]]
combinacoes = itertools.combinations(n_combs, 15)
for comb in combinacoes:
for filtro, maxx in filters:
if len(repeats(filtro, comb)) in maxx:
print(comb)
Basically, I need a combination to only print if:
contain 5 items from this list: [1, 2, 3, 6, 7, 8, 11, 12, 16, 17, 21, 22]
contain 7 items from this list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
contain 2 items from this list: [20, 21, 22, 23, 24]
The above code does not do the 3 validations at the same time and that's what I need.
I think your loop is working properly only that the last element in filters does not meet the conditions you have specified. Edit it to include smaller numbers like I have done below. Also, I have reduced the number of elements in n_combs so that my answer becomes easy to understand. This is because looping through the more than 3 million entries in 25 combination 15 is not a very good idea for demonstration purposes. Try to run the edited version below and you will see what I am saying.
import itertools
def repeats(input1, input2):
return [int(dz) for dz in input1 if int(dz) in input2]
n_combs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
filters = [[[1, 2, 3, 6, 7, 8, 11, 12, 16, 17, 21, 22], [5]], [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [7]], [[2, 4, 7,9], [2]]]
combinacoes = itertools.combinations(n_combs, 15)
for comb in combinacoes:
for filtro, maxx in filters:
if len(repeats(filtro, comb)) in maxx:
print(comb, maxx)
Let me know if this solves your problem.
I assume you want to apply an arbitrary number of tests to some value.
Since in Python functions are first class citizens, you can have a list of validator functions:
def iterable_contains_1(value):
return 1 in value
def iterable_contains_2(value):
return 2 in value
validators = [iterable_contains_1, iterable_contains_2]
Then you can call all your validations:
for item in ([1, 2, 3], [2, 3, 4], [1, 3, 4], [3, 4, 5]):
if all(validator(item) for validator in validators):
print('do something with', item)
This will print only do something with [1, 2, 3] as it is the only list passing both tests.
[edit]
I think you are looking for set.
def validator1(iterable):
return len(
set(iterable).intersection(
[1, 2, 3, 6, 7, 8, 11, 12, 16, 17, 21, 22]
)) >= 5
def validator2(iterable):
return len(
set(iterable).intersection(
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
)) >= 7
def validator3(iterable):
return len(
set(iterable).intersection(
[20, 21, 22, 23, 24]
)) >= 2
I found a resolution:
import itertools
def repeats(input1, input2):
return [int(dz) for dz in input1 if int(dz) in input2]
n_combs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
filters = [[[1, 2, 3, 6, 7, 8, 11, 12, 16, 17, 21, 22], [5]], [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [7]], [[20, 21, 22, 23, 24], [2]]]
combinacoes = itertools.combinations(n_combs, 15)
for comb in combinacoes:
if all([len(repeats(filtro, comb)) in qtd for filtro, qtd in filters]):
print(comb)
Remembering that the values used in this post were just examples, to simplify and better understand logic.
Now the algorithm is able to validate a combination on all filters at the same time, and allow one condition to be satisfied for all.
I have a list l=[2,3,4,5,6,99,7,8,9,10,11,12,100,13,14,15,16,17,18,101] and I will like to split l into sublists [2,3,4,5,6], [7,8,9,10,11,12],[13,14,15,16,17,18], meaning that I use 'separator' digits 99,100,101 that belong to separators = [99,100,101] as a flag to indicate where I should go on to the next list.
In particular, these sublists may not have the same number of elements, but are different in size of only 1 (5 or 6). Note: There may be more than 3 separators.
Is there an efficient way to do this in Python 3? I thought of first extracting the indices of the separator elements and then slice the list accordingly but it seems far too complex and computationally intensive..
Some insight will be great!
Add on (suggestion from #Netwave): My attempt (which clearly does not work):
g = []
for i in l:
if i in separators:
g += [l[:l.index(i)]]
Output:
>>> g
[[2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 99, 7, 8, 9, 10, 11, 12], [2, 3, 4, 5, 6, 99, 7, 8, 9, 10, 11, 12, 100, 13, 14, 15, 16, 17, 18]]
Use groupby:
from itertools import groupby
separators = [99, 100, 101]
l = [2, 3, 4, 5, 6, 99, 7, 8, 9, 10, 11, 12, 100, 13, 14, 15, 16, 17, 18, 101]
splits = [list(values) for key, values in groupby(l, key=lambda x: x not in separators) if key]
print(splits)
Output
[[2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17, 18]]
I hope you are looking for something similar to the below code. You need to iterate over your list and keep checking if the element is present in the separator list. This can be done other way around, as you said by iterating over the separator list and finding the index of the elements in the main list. For the sake of simplicity I took the former approach. (Make a note of the use of endindex variable):
l=[2,3,4,5,6,99,7,8,9,10,11,12,100,13,14,15,16,17,18,101]
seperator = [99,100,101]
list_of_list = []
endindex = 0
for i in range(0,len(l),1):
if l[i] in seperator:
list_of_list.append(l[endindex:i])
endindex = i + 1
print(list_of_list)
Ouput:
[[2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17, 18]]
Easier for use in a function:
import itertools
def split(l,l2):
return [list(v) for k,v in itertools.groupby(l,lambda x: x in l2) if not k]
l = [2, 3, 4, 5, 6, 99, 7, 8, 9, 10, 11, 12, 100, 13, 14, 15, 16, 17, 18, 101]
print(split(l,[99, 100, 101]))
Output:
[[2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17, 18]]
Realized a duplicate of Split a list into nested lists on a value
I have the following code for a list of lists with the intention of creating a matrix of numbers:
grid=[[1,2,3,4,5,6,7],[8,9,10,11,12],[13,14,15,16,17],[18,19,20,21,22]]
On using the following code which i figured out would reverse the list, it produces a matrix ...
for i in reversed(grid):
print(i)
The output is:
[18, 19, 20, 21, 22]
[13, 14, 15, 16, 17]
[8, 9, 10, 11, 12]
[1, 2, 3, 4, 5, 6, 7]
I want however, the output to be as below, so that the numbers "connect" as they go up:
[22,21,20,19,18]
[13,14,15,16,17]
[12,11,10,9,8]
[1,2,3,4,5,6,7]
Also, for an upvote, I'd be interested in more efficient ways of generating the matrix in the first place. For instance, to generate a 7x7 array - can it be done using a variable, for instance 7, or 49. Or for a 10x10 matrix, 10, or 100?
UPDATE:
Yes, sorry - the sublists should all be of the same size. Typo above
UPDATE BASED ON ANSWER BELOW
These two lines:
>>> grid=[[1,2,3,4,5,6,7],[8,9,10,11,12],[13,14,15,16,17],[18,18,20,21,22]]
>>> [lst[::-1] for lst in grid[::-1]]
produce the following output:
[[22, 21, 20, 18, 18], [17, 16, 15, 14, 13], [12, 11, 10, 9, 8], [7, 6, 5, 4, 3, 2, 1]]
but I want them to print one line after the other, like a matrix ....also, so I can check the output is as I specified. That's all I need essentially, for the answer to be the answer!
You need to reverse the list and also the sub-lists:
[lst[::-1] for lst in grid[::-1]]
Note that lst[::-1] reverses the list via list slicing, see here.
You can visualize the resulting nested lists across multiples lines with pprint:
>>> from pprint import pprint
>>> pprint([lst[::-1] for lst in grid[::-1]])
[[22, 21, 20, 19, 18],
[17, 16, 15, 14, 13],
[12, 11, 10, 9, 8],
[7, 6, 5, 4, 3, 2, 1]]
usually 2D matrices are created, manipulated with numpy
then index slicing can reorder rows, columns
import numpy as np
def SnakeMatrx(n):
Sq, Sq.shape = np.arange(n * n), (n, n) # Sq matrix filled with a range
Sq[1::2,:] = Sq[1::2,::-1] # reverse odd row's columns
return Sq[::-1,:] + 1 # reverse order of rows, add 1 to every entry
SnakeMatrx(5)
Out[33]:
array([[21, 22, 23, 24, 25],
[20, 19, 18, 17, 16],
[11, 12, 13, 14, 15],
[10, 9, 8, 7, 6],
[ 1, 2, 3, 4, 5]])
SnakeMatrx(4)
Out[34]:
array([[16, 15, 14, 13],
[ 9, 10, 11, 12],
[ 8, 7, 6, 5],
[ 1, 2, 3, 4]])
if you really want a list of lists:
SnakeMatrx(4).tolist()
Out[39]: [[16, 15, 14, 13], [9, 10, 11, 12], [8, 7, 6, 5], [1, 2, 3, 4]]
numpy is popular but not a official Standard Library in Python distributions
of course it can be done with list manipulation
def SnakeLoL(n):
Sq = [[1 + i + n * j for i in range(n)] for j in range(n)] # Sq LoL filled with a range
for row in Sq[1::2]:
row.reverse() # reverse odd row's columns
return Sq[::-1][:] # reverse order of rows
# or maybe more Pythonic for return Sq[::-1][:]
# Sq.reverse() # reverse order of rows
# return Sq
SnakeLoL(4)
Out[91]: [[16, 15, 14, 13], [9, 10, 11, 12], [8, 7, 6, 5], [1, 2, 3, 4]]
SnakeLoL(5)
Out[92]:
[[21, 22, 23, 24, 25],
[20, 19, 18, 17, 16],
[11, 12, 13, 14, 15],
[10, 9, 8, 7, 6],
[1, 2, 3, 4, 5]]
print(*SnakeLoL(4), sep='\n')
[16, 15, 14, 13]
[9, 10, 11, 12]
[8, 7, 6, 5]
[1, 2, 3, 4]
Simple way of python:
list(map(lambda i: print(i), [lst[::-1] for lst in grid[::-1]]))
I have the following problem. I have a list like:
>>> l = list(range(20))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> # What I want:
>>> [[0, 1, 2, 3], [3, 4, 5, 6], [6, 7, 8, 9] , ...]
How can I get my list into such k pieces with length 4 in the most pythonic way? I feel like I miss something obvious here. I am fully aware of How do you split a list into evenly sized chunks? but still have no clue...
Thanks in advance!
A direct copy of an answer to question you have posted link to. The only change is the step in xrange - from n to n - 1:
def chunks(l, n):
for i in xrange(0, len(l), n - 1):
yield l[i:i + n]
list(chunks(range(20), 4))
[[0, 1, 2, 3], [3, 4, 5, 6], [6, 7, 8, 9], [9, 10, 11, 12], [12, 13, 14, 15], [15, 16, 17, 18], [18, 19]]
zip(*[iter(list(range(20)))]*4)
a = range(20)
b = [a[i:i+4] for i in xrange(0, len(a), 4)]
print b
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15], [16, 17, 18, 19]]
I am looking for the most pythonic way of splitting a list of numbers into smaller lists based on a number missing in the sequence. For example, if the initial list was:
seq1 = [1, 2, 3, 4, 6, 7, 8, 9, 10]
the function would yield:
[[1, 2, 3, 4], [6, 7, 8, 9, 10]]
or
seq2 = [1, 2, 4, 5, 6, 8, 9, 10]
would result in:
[[1, 2], [4, 5, 6], [8, 9, 10]]
Python 3 version of the code from the old Python documentation:
>>> # Find runs of consecutive numbers using groupby. The key to the solution
>>> # is differencing with a range so that consecutive numbers all appear in
>>> # same group.
>>> from itertools import groupby
>>> from operator import itemgetter
>>> data = [ 1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28]
>>> for k, g in groupby(enumerate(data), lambda i_x: i_x[0] - i_x[1]):
... print(list(map(itemgetter(1), g)))
...
[1]
[4, 5, 6]
[10]
[15, 16, 17, 18]
[22]
[25, 26, 27, 28]
The groupby function from the itertools module generates a break every time the key function changes its return value. The trick is that the return value is the number in the list minus the position of the element in the list. This difference changes when there is a gap in the numbers.
The itemgetter function is from the operator module, you'll have to import this and the itertools module for this example to work.
Alternatively, as a list comprehension:
>>> [map(itemgetter(1), g) for k, g in groupby(enumerate(seq2), lambda i_x: i_x[0] - i_x[1])]
[[1, 2], [4, 5, 6], [8, 9, 10]]
This is a solution that works in Python 3 (based on previous answers that work in python 2 only).
>>> from operator import itemgetter
>>> from itertools import *
>>> groups = []
>>> for k, g in groupby(enumerate(seq2), lambda x: x[0]-x[1]):
>>> groups.append(list(map(itemgetter(1), g)))
...
>>> print(groups)
[[1, 2], [4, 5, 6], [8, 9, 10]]
or as a list comprehension
>>> [list(map(itemgetter(1), g)) for k, g in groupby(enumerate(seq2), lambda x: x[0]-x[1])]
[[1, 2], [4, 5, 6], [8, 9, 10]]
Changes were needed because
Removal of tuple parameter unpacking PEP 3113
map returning an iterator instead of a list
Another option which doesn't need itertools etc.:
>>> data = [1, 4, 5, 6, 10, 15, 16, 17, 18, 22, 25, 26, 27, 28]
>>> spl = [0]+[i for i in range(1,len(data)) if data[i]-data[i-1]>1]+[None]
>>> [data[b:e] for (b, e) in [(spl[i-1],spl[i]) for i in range(1,len(spl))]]
... [[1], [4, 5, 6], [10], [15, 16, 17, 18], [22], [25, 26, 27, 28]]
I like this one better because it doesn't require any extra libraries or special treatment for first case:
a = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 20, 21, 22]
b = []
subList = []
prev_n = -1
for n in a:
if prev_n+1 != n: # end of previous subList and beginning of next
if subList: # if subList already has elements
b.append(subList)
subList = []
subList.append(n)
prev_n = n
if subList:
b.append(subList)
print a
print b
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 20, 21, 22]
[[1, 2, 3, 4, 5, 6, 7, 8], [10, 11, 12], [15, 16, 17, 18], [20, 21, 22]]
My way
alist = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 20, 21, 22]
newlist = []
start = 0
end = 0
for index,value in enumerate(alist):
if index < len(alist)-1:
if alist[index+1]> value+1:
end = index +1
newlist.append(alist[start:end])
start = end
else:
newlist.append(alist[start: len(alist)])
print(newlist)
Result
[[1, 2, 3, 4, 5, 6, 7, 8], [10, 11, 12], [15, 16, 17, 18], [20, 21, 22]]