Parameter update during function call - python

So, I am writing a code to shift the elements of a particular list within a list of lists towards right.
def right(state,index):
r_state=state
new_state = []
for j in range(1,len(r_state[index])):
new_state.append(r_state[index][j-1])
new_state.insert(0, r_state[index][-1])
r_state[index]=new_state
return r_state
#case1
for i in range(2):
print(right([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]], i))
#case2
def printer(node):
for i in range(2):
print(right(node, i))
printer([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
case 1 gives me the output that I want(only one sublist corresponding to the index changed):
[[4, 1, 2, 3], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
[[1, 2, 3, 4], [8, 5, 6, 7], [9, 10, 11, 12], [13, 14, 15, 16]]
But case 2 ends up updating my list of lists
[[4, 1, 2, 3], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
[[4, 1, 2, 3], [8, 5, 6, 7], [9, 10, 11, 12], [13, 14, 15, 16]]
Why is the list being updated? Also, how can I modify case2 to get the same output as case 1?

The problem is that assignment does not make a copy in Python. You have to explicitly copy. In your own code, to copy the list, change
r_state = state
To
r_state = state[:]
Also, you will see r_state = list(state) used as well. Python 3 has the even more explicit:
r_state = state.copy()
You can also use a list-comprehension to make a new list. Here is a quick-and-dirty approach using modular arithmetic to shift the elements of your sequence:
>>> def shift_right(lst, shift):
... modulus = len(lst)
... return [lst[(i + shift)%modulus] for i in range(len(lst))]
...
>>> def right(state, index):
... return [shift_right(sub, 1) if i == index else sub for i, sub in enumerate(state)]
...
>>> test = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
>>> shift_right(test[0], 1)
[2, 3, 4, 1]
>>> shift_right(test[0], 2)
[3, 4, 1, 2]
>>> shift_right(test[0], 3)
[4, 1, 2, 3]
>>> shift_right(test[0], 4)
[1, 2, 3, 4]
>>> shift_right(test[0], 5)
[2, 3, 4, 1]
>>> right(test, 0)
[[2, 3, 4, 1], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
>>> right(test, 1)
[[1, 2, 3, 4], [6, 7, 8, 5], [9, 10, 11, 12], [13, 14, 15, 16]]
That last comprehension is perhaps a bit too dense, and could be written like this instead:
>>> def right(state, index):
... result = []
... for i, sub in enumerate(state):
... if i == index:
... result.append(shift_right(sub, 1))
... else:
... result.append(sub)
... return result
...
>>> right(test, 0)
[[2, 3, 4, 1], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
>>> right(test, 1)
[[1, 2, 3, 4], [6, 7, 8, 5], [9, 10, 11, 12], [13, 14, 15, 16]]
>>> right(test, 2)
[[1, 2, 3, 4], [5, 6, 7, 8], [10, 11, 12, 9], [13, 14, 15, 16]]
>>> right(test, 3)
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [14, 15, 16, 13]]
>>>

Related

Sum of cubes in python

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..

Given two lists of lists find specific matches - fastest solution

Given two lists of lists of arbitrary length, let's say list1 and list2 I want to divide the lists in list1 into subsets of lists, if they contain only one of the lists of list2.
I give you a specific example:
list1 = [[1, 2, 3, 4], [1, 2, 3, 5, 6, 8], [1, 2, 3, 6, 7], [1, 2, 3, 6, 8, 9, 10],
[1, 2, 3, 6, 8, 11, 12], [1, 2, 4, 5, 9, 10], [1, 2, 4, 5, 11, 12],
[1, 2, 5, 6, 7, 9, 10], [1, 2, 5, 6, 7, 11, 12], [1, 2, 5, 6, 8, 9, 10],
[1, 2, 5, 6, 8, 11, 12], [3, 4, 5, 6, 8], [3, 5, 9, 10], [3, 5, 11, 12],
[4, 6, 7], [4, 6, 8, 9, 10], [4, 6, 8, 11, 12], [9, 10, 11, 12]]
list2 = [[2], [6, 7], [6, 8], [9,9]]
and then desired outcome of the function would be for "inner" matches:
[[1, 2, 3, 4],
[1, 2, 4, 5, 11, 12],
[4, 6, 7],
[3, 4, 5, 6, 8],
[4, 6, 8, 11, 12],
[3, 5, 9, 10],
[9, 10, 11, 12]]
and for the "outer" matches (that are consequently the remaining items in list_1):
[(1, 2, 5, 6, 8, 11, 12),
(1, 2, 5, 6, 7, 11, 12),
(4, 6, 8, 9, 10),
(1, 2, 5, 6, 7, 9, 10),
(1, 2, 3, 5, 6, 8),
(1, 2, 3, 6, 8, 11, 12),
(1, 2, 3, 6, 7),
(3, 5, 11, 12),
(1, 2, 4, 5, 9, 10),
(1, 2, 5, 6, 8, 9, 10),
(1, 2, 3, 6, 8, 9, 10)]
I coded a quick and dirty solution that produces the desired outcome, but does not scale well for very long lists (for example 100000 & 2500).
My solution:
from itertools import chain
def find_all_sets(list1,list2):
d = {}
d2 = {}
count = 0
for i in list2:
count = count + 1
set2 = set(i)
d['set'+str(count)] = set2
d['lists'+str(count)] = []
first = []
d2['match'+str(count)] = []
for a in list1:
set1 = set(a)
if d['set'+str(count)].issubset(set1) == True:
first.append(a)
d['lists'+str(count)].append(first)
d2['match'+str(count)].append(d['lists'+str(count)])
count = 0
count2 = -1
d3 = {}
all_sub_lists = []
for i in d2.values():
count = count + 1
count2 = count2 + 1
d3['final'+str(count)] = []
real = []
for item in i:
for each_item in item:
for each_each_item in each_item:
seta= set(each_each_item)
save = []
for i in list2:
setb = set(i)
a=setb.issubset(seta)
save.append(a)
index_to_remove = count2
new_save = save[:index_to_remove] + save[index_to_remove + 1:]
if True not in new_save:
real.append(each_each_item)
d3['final'+str(count)].append(real)
all_sub_lists.append(real)
inner_matches = list(chain(*all_sub_lists))
setA = set(map(tuple, inner_matches))
setB = set(map(tuple, list1))
outer_matches = [i for i in setB if i not in setA]
return inner_matches, outer_matches
inner_matches, outer_matches = find_all_sets(list1,list2)
I am looking for a faster way to process large lists. Please excuse if the terminology of "inner" an "outer" matches is unclear. I did not know how else to call them.
Here is my suggestion (let me know if you need it as a function):
inner_matches=[]
outer_matches=[]
for i in list1:
if sum(1 for k in list2 if set(k).intersection(set(i))==set(k))==1:
inner_matches.append(i)
else:
outer_matches.append(i)
print(inner_matches)
#[[1, 2, 3, 4], [1, 2, 4, 5, 11, 12], [3, 4, 5, 6, 8], [3, 5, 9, 10], [4, 6, 7], [4, 6, 8, 11, 12], [9, 10, 11, 12]]
print(outer_matches)
#[[1, 2, 3, 5, 6, 8], [1, 2, 3, 6, 7], [1, 2, 3, 6, 8, 9, 10], [1, 2, 3, 6, 8, 11, 12], [1, 2, 4, 5, 9, 10], [1, 2, 5, 6, 7, 9, 10], [1, 2, 5, 6, 7, 11, 12], [1, 2, 5, 6, 8, 9, 10], [1, 2, 5, 6, 8, 11, 12], [3, 5, 11, 12], [4, 6, 8, 9, 10]]
Here's a solution that uses issubset() to detect the inner lists. Using your sample data it's faster than your algorithm by a factor of nearly 4.
inner = []
outer = []
search_sets = [set(l) for l in list2]
for l in list1:
if sum(s.issubset(l) for s in search_sets) == 1:
inner.append(l)
else:
outer.append(l)
print(f'{inner = }')
print()
print(f'{outer = }')
Output
inner = [[1, 2, 3, 4], [1, 2, 4, 5, 11, 12], [3, 4, 5, 6, 8], [3, 5, 9, 10], [4, 6, 7], [4, 6, 8, 11, 12], [9, 10, 11, 12]]
outer = [[1, 2, 3, 5, 6, 8], [1, 2, 3, 6, 7], [1, 2, 3, 6, 8, 9, 10], [1, 2, 3, 6, 8, 11, 12], [1, 2, 4, 5, 9, 10], [1, 2, 5, 6, 7, 9, 10], [1, 2, 5, 6, 7, 11, 12], [1, 2, 5, 6, 8, 9, 10], [1, 2, 5, 6, 8, 11, 12], [3, 5, 11, 12], [4, 6, 8, 9, 10]]

how to get individual numbers of a box

I'm struggling to get each of the four numbers of a python array contained within a 2x2 into their separate arrays much like a Sudoku grid. Order doesn't matter. I would have tried writing a code or something but my mind has gone blank.
example grid
[
[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]
]
I want to be able to get it in the form
[
[ 1, 2, 5, 6],
[ 3, 4, 7, 8],
[ 9, 10, 13, 14],
[11, 12, 15, 16]
]
Here is a pure Python solution. If you are after something more readable consider using NumPy (see below).
>>> from itertools import count, chain
>>>
# create 2x2 blocks of 2x2
>>> c = count(1)
>>> L4D = [[[[next(c) for i in range(2)] for j in range(2)] for k in range(2)] for l in range(2)]
>>> L4D
[[[[1, 2], [3, 4]], [[5, 6], [7, 8]]], [[[9, 10], [11, 12]], [[13, 14], [15, 16]]]]
# swap middle dimensions
>>> L4D = [zip(*i) for i in L4D]
# next line is not necessary, only here so we can see what's going on
>>> L4D = [list(i) for i in L4D]
>>> L4D
[[([1, 2], [5, 6]), ([3, 4], [7, 8])], [([9, 10], [13, 14]), ([11, 12], [15, 16])]]
# join first two and last two dimensions
>>> result = [list(chain.from_iterable(j)) for j in chain.from_iterable(L4D)]
>>> result
[[1, 2, 5, 6], [3, 4, 7, 8], [9, 10, 13, 14], [11, 12, 15, 16]]
If using NumPy is an option this can be simplified. Here are three different possibilities. The first is a direct translation of the pure Python solution:
>>> import numpy as np
>>>
>>> np.arange(1, 17).reshape(2, 2, 2, 2).swapaxes(1, 2).reshape(4, 4)
array([[ 1, 2, 5, 6],
[ 3, 4, 7, 8],
[ 9, 10, 13, 14],
[11, 12, 15, 16]])
>>> np.block(list(map(list, np.arange(1, 17).reshape(2, 2, 2, 2))))
array([[ 1, 2, 5, 6],
[ 3, 4, 7, 8],
[ 9, 10, 13, 14],
[11, 12, 15, 16]])
>>> a = np.arange(4).reshape(2, 2)
>>> b = np.ones((2, 2), dtype = int)
>>> 4 * np.kron(a, b) + np.kron(b, a) + 1
array([[ 1, 2, 5, 6],
[ 3, 4, 7, 8],
[ 9, 10, 13, 14],
[11, 12, 15, 16]])
I finally found a solution to my question with a little modification to this answer
cells = [[] * 4 for x in range(4)]
for row_index, row in enumerate(grid):
for col_index, col in enumerate(row):
i = col_index//2 + 2*(row_index//2)
cells[i].append(col)

Replacing a vertical sublist in a list of lists

This question is an extension to this question.
I'm representing a two-dimensional array using list of lists, L, say:
[ [1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4] ]
For a given sub-list, say [9, 99], I want to replace a specific sub-list in the "2-D" list by this sublist using something intuitive like:
L[1][0:2] = sublist
# which updates `L` to:
[ [1, 2, 3, 4],
[1, 9, 99, 4],
[1, 2, 3, 4],
[1, 2, 3, 4] ] # not in this format, but written like this for clarity
This works for horizontal replacements, but not for vertical replacements since, as we can't slice to separate lists like this: L[0:2][0]. If I had to use this slicing system, I could transpose L (Transpose list of lists), then use this slicing method, then transpose it back. But that's not efficient, even for the sake of simplicity.
What would be an efficient way to replicate L[0:2][0] and get this output?
[ [1, 2, 3, 4],
[1, 9, 3, 4],
[1, 99, 3, 4],
[1, 2, 3, 4] ]
Note: Assume len(sublist) <= len(L), for vertical replacements (which is the focus of this question).
Looping approach:
def replaceVert(al : list, repl:list, oIdx:int, iIdx:int):
for pos in range(len(repl)):
al[oIdx+pos][iIdx] = repl[pos]
a = [ [1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16] ]
print(a) # [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
replaceVert(a,['ä','ü'],2,2) # this is a one liner ;)
print(a) # [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 'ä', 12], [13, 14, 'ü', 16]]
Transpose/slice/transpose approach:
I overread the mentioning of "no transposing". This is using transpose, change, transpose method with slicing which is not wanted by the Q. It is a answer for the title of this question, so I decided to leave it in for future people search SO and stumble over this Q:
a = [ [1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16] ]
b = list(map(list,zip(*a))) # will make [ [1,5,9,13], ... ,[4,8,12,16]]
b[1][0:2]=['a','b'] # replaces what you want here (using a and b for clarity)
c = list(map(list,zip(*b))) # inverts b back to a's form
print(a)
print(b)
print(c)
Output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]] # a
[[1, 5, 9, 13], ['a', 'b', 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]] # b replaced
[[1, 'a', 3, 4], [5, 'b', 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]] # c
Timing 4x4 list, 2 replaces:
setuptxt = """
def replaceVert(al : list, repl:list, oIdx:int, iIdx:int):
for pos in range(len(repl)):
al[oIdx+pos][iIdx] = repl[pos]
a = [ [1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16] ]
"""
zipp = """b = list(map(list,zip(*a)))
b[1][0:2]=['a','b']
c = list(map(list,zip(*b)))
"""
import timeit
print(timeit.timeit("replaceVert(a,['ä','ü'],2,2)",setup = setuptxt))
print(timeit.timeit(stmt=zipp, setup=setuptxt))
Output:
looping: 12.450226907037592
zipping: 7.50479947070815
The method wit ZIPPing (transpose/slice/transpose) needs roughly 60% of the time for 4x4 lists.
Bigger list 1000x1000 and ~70 elements replaced:
setuptxt = """
def replaceVert(al : list, repl:list, oIdx:int, iIdx:int):
for pos in range(len(repl)):
al[oIdx+pos][iIdx] = repl[pos]
a = [ [kk for kk in range(1+pp,1000+pp)] for pp in range(1,1000)]
repl = [chr(mm) for mm in range(32,100)]
"""
import timeit
print(timeit.timeit("replaceVert(a,repl,20,5)",number=500, setup = setuptxt))
zipp = """b = list(map(list,zip(*a)))
b[20][5:5+len(repl)]=repl
c = list(map(list,zip(*b)))
"""
print(timeit.timeit(stmt=zipp, setup=setuptxt,number=500))
Output:
looping: 0.07702917579216137
zipping: 69.4807168493871
Looping wins. Thanks #Sphinx for his comment

Split list in special chunks

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]]

Categories

Resources