Problems with recursion - python

Given:
numma = [1,2,3,4], n = 7
I expected the following code to return:
[[1,2,3],[1,2,4]]
Instead, it returns a list with four copies of:
[1,2,4]
and six copies of:
[1,2,3,4]
The script is intended to take the elements of the list numbers = numma in order, and form a new list (this_chain) with them, as long as the sum of the elements in this_chain does not exceed n = 7.
When adding a new element would break that condition, the list this_chain is appended to another list (all_chains), and the process begins again. If the original list (numbers) runs out of elements, the current this_chain is appended to all_chains and the script finishes, returning the list (of lists) called all_chains.
When I run it step by step, it follows the expected behavior, but upon reaching the return statement (step 84), instead of returning all_chains and finish, the arrow moves half a step downwards and then jumps back to the first statement (for i in ...). Then it goes back down and appends another copy of the current this_chain to all_chains, and so on, taking 66 additional steps and returning the output I mentioned above.
It was suggested to me to iterate over a tuple, instead of over a list, but since I want to remove elements from the iteration sequence I do not see how it could be done.
I am pretty puzzled. The four questions I'd like to ask, in order of importance, are the following:
Why does the program not finish upon reaching the return statement? I believed a return statement would always terminate any script.
Given the behavior described above, why does the script finally end, instead of keeping on appending copies of valid lists to the list called all_chains?
Why does the script append invalid elements to the list all_chains, that is, lists whose elements sum up more than n = 7?
Why are elements already in all_chains removed (or modified), so that they are not present in the final output, even if they were appended to the list all_chains previously?
My code:
def chain(numbers, n, all_chains=[], sum=0, this_chain=[]):
for i in range(len(numbers)):
if numbers[i] not in this_chain:
sum += numbers[i]
if sum <= n:
this_chain.append(numbers[i])
chain(numbers, n, all_chains, sum, this_chain)
else:
if this_chain not in all_chains:
all_chains.append(this_chain)
mocha = numbers[:]
mocha.remove(this_chain[-1])
chain(mocha, n, all_chains, sum=0, this_chain=[])
all_chains.append(this_chain)
return all_chains
numma = [1,2,3,4]
chain(numma, 7, chains=[], sum=0, this_chain=[])

Why does the program not finish upon reaching the return statement?
I believed a return statement would always terminate any script.
return does not terminate the script; it leaves the current routine and returns control to the one that called it. When you're in a recursive call, it simply returns control to the "elder" version of itself that called it.
Given the behavior described above, why does the script finally
end, instead of keeping on appending copies of valid lists to the list
called all_chains?
It ends because it all active invocations eventually make it to the bottom of the function, ending their executions.
Why does the script append invalid elements to the list all_chains,
that is, lists whose elements sum up more than n = 7?
The problem is when you return to try adding 4, the list you think you have is [1, 2], but it actually reads [1, 2, 3] because ... well, see the next point.
Why are elements already in all_chains removed (or modified), so
that they are not present in the final output, even if they were
appended to the list all_chains previously?
Wherever you append to a list, you append a reference to the original, rather than a local copy. This comes from using mutable objects (as #tobias_k already noted). Thus, when you append [1, 2, 3] to the list (several times), all of these are the same object. When you later mistakenly append 4 to the list (thinking that the list has only [1, 2] in it), you change all the references to [1, 2, 3, 4]. I've fixed this in the code below.
Finally, you have all those extra lists because of the missing return statements; instead of stopping when you should, you go through the rest of the function, making more calls, and finding more copies of the same solutions.
I've added some stuff to your code so you can watch the execution. Most of all, I've added some ugly, but useful, print statements to track the execution. I have modules to handle the indentation, counts, and argument printing in more readable form, you can experiment to see what you find most readable.
I've also replaced your straight append operations with copies, so you can differentiate the problems better and learn more from your mistakes.
Remember, good choices come from experience.
Experience comes from bad choices.
import copy
invocation = 0
def chain(numbers, n, all_chains=[], sum=0, this_chain=[]):
global invocation
invocation += 1
local_id = invocation
indent = " "
print indent*local_id, local_id, "ENTER", "\tsum", sum, "\tthis", this_chain, "\tall", all_chains
for i in range(len(numbers)):
if numbers[i] not in this_chain:
sum += numbers[i]
if sum <= n:
print indent*local_id, local_id, "append to this", sum, this_chain, numbers[i]
this_chain.append(numbers[i])
chain(numbers, n, all_chains, sum, this_chain)
else:
if this_chain not in all_chains:
print indent*local_id, local_id, "append to all 1", this_chain
all_chains.append(copy.copy(this_chain))
mocha = numbers[:]
mocha.remove(this_chain[-1])
chain(mocha, n, all_chains, sum=0, this_chain=[])
print indent*local_id, local_id, "append to all 2", this_chain
all_chains.append(copy.copy(this_chain))
print indent*local_id, local_id, "LEAVE", all_chains
return all_chains
numma = [1, 2, 3, 4]
result = chain(numma, 7)
print
for x in result:
print x
Execution trace:
1 ENTER sum 0 this [] all []
1 append to this 1 [] 1
2 ENTER sum 1 this [1] all []
2 append to this 3 [1] 2
3 ENTER sum 3 this [1, 2] all []
3 append to this 6 [1, 2] 3
4 ENTER sum 6 this [1, 2, 3] all []
4 append to all 1 [1, 2, 3]
5 ENTER sum 0 this [] all [[1, 2, 3]]
5 append to this 1 [] 1
6 ENTER sum 1 this [1] all [[1, 2, 3]]
6 append to this 3 [1] 2
7 ENTER sum 3 this [1, 2] all [[1, 2, 3]]
7 append to this 7 [1, 2] 4
8 ENTER sum 7 this [1, 2, 4] all [[1, 2, 3]]
8 append to all 2 [1, 2, 4]
8 LEAVE [[1, 2, 3], [1, 2, 4]]
7 append to all 2 [1, 2, 4]
7 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4]]
6 append to all 2 [1, 2, 4]
6 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4]]
5 append to all 2 [1, 2, 4]
5 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4]]
4 append to all 2 [1, 2, 3]
4 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3]]
3 append to all 2 [1, 2, 3]
3 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3]]
2 append to this 7 [1, 2, 3] 4
9 ENTER sum 7 this [1, 2, 3, 4] all [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3]]
9 append to all 2 [1, 2, 3, 4]
9 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3, 4]]
2 append to all 2 [1, 2, 3, 4]
2 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4]]
1 append to all 2 [1, 2, 3, 4]
1 LEAVE [[1, 2, 3], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 4], [1, 2, 3], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
[1, 2, 3]
[1, 2, 4]
[1, 2, 4]
[1, 2, 4]
[1, 2, 4]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4]

Related

Trying to write a combinations function in Python on my own, not working

I'm trying to solve this problem here: https://codingbat.com/prob/p252079?parent=/home/peter#norvig.com
In math, a "combination" of a set of things is a subset of the things. We define the function combinations(things, k) to be a list of all the subsets of exactly k elements of things. Conceptually, that's all there is, but there are some questions to settle: (A) how do we represent a subset? (B) What order are the elements within each subset? (C) What order to we list the subsets? Here's what we will agree to: (A) a subset will be a list. (B) The order of elements within a list will be the same as the order within 'things'. So, for example, for combinations([1, 2, 3], 2) one of the subsets will be [1, 2]; whereas [2, 1] is not a subset. (C) The order of subsets will be lexicographical or sorted order -- that is, combinations([1, 2, 3], 2) returns [ [1, 2], [1, 3], 2, 3] ] because [1, 2] < [1, 3] < [2, 3]. You might want to use the function 'sorted' to make sure the results you return are properly ordered.
combinations([1, 2, 3, 4, 5], 2) → [[1, 2], [1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5], [3, 4], [3, 5], [4, 5]]
combinations([1, 2, 3], 2) → [[1, 2], [1, 3], [2, 3]]
combinations([1, 2, 3, 4, 5, 6], 5) → [[1, 2, 3, 4, 5], [1, 2, 3, 4, 6], [1, 2, 3, 5, 6], [1, 2, 4, 5, 6], [1, 3, 4, 5, 6], [2, 3, 4, 5, 6]]
Here's my code:
def combinations(things, k):
if k == 0 or k == len(things):
return [things]
elif len(things) < k:
return
else:
finalcomb = []
subcomb1 = combinations(things[1:], k - 1)
subcomb2 = combinations(things[1:], k)
for i in range(len(combinations(things[1:], k - 1))):
firstelement = [things[0]]
firstelement += combinations(things[1:], k - 1)[i]
finalcomb.append(firstelement)
for j in range(len(combinations(things[1:], k))):
finalcomb.append(combinations(things[1:], k)[j])
return finalcomb
However, this is the output:
Haven't hit 10 reputation yet so it's a link to the error. I'm not sure what I did wrong, can anybody help me out? Thank you so much.
The problem is this. When k == 0 it shouldn't return [things]. It should return an empty array. Similar to when len(things) < k:. This is because, when k == 0, it means we that we have already found all the numbers for that specific combination.
But there's one more problem. We're returning an empty array. However, in the for loops, we're iterating over the returned array. So if the array is empty, nothing happens. So what we should really return is an empty 2D array. I won't go into too much detail about what the problem is since it's better for you to try and understand why it's not working. Try adding print statements inside and outside the for loops.
Anyway, the working code looks like this:
def combinations(things, k):
if k == len(things):
return [things[:]]
if len(things) < k or k == 0:
return [[]]
finalcomb = []
subcomb1 = combinations(things[1:], k - 1)
subcomb2 = combinations(things[1:], k)
for comb in subcomb1:
firstelement = [things[0]]
firstelement += comb
finalcomb.append(firstelement)
finalcomb += subcomb2
return finalcomb
Note a few things:
Use the variables you've already assigned (I'm assuming you forgot about them)
Lists can be concatenated using +, similar to strings. If you return within an if statement, you don't need an else for the next line since if the if statement is satisfied, it would definitely not go to the else.
You simply can try using itertools:
import itertools
output = []
for nums in itertools.combinations([1, 2, 3, 4, 5], 2):
output.append(list(nums))
print(output)
output:
[[1, 2], [1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5], [3, 4], [3, 5], [4, 5]]
For 3 nums:
import itertools
output = []
for nums in itertools.combinations([1, 2, 3, 4, 5], 3):
output.append(list(nums))
print(output)
Output:
[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]

Python program to shift elements of array

I am trying to create a python program to shuffle an array so that the horizontal and vertical rows never have a repeat number.
Input: [1,2,3,4]
Output:
1 2 3 4
2 3 4 1
3 4 1 2
4 1 2 3
My program calculates the shifting of each element correctly, but when it appends the list to the output list, the output list only has repeat copies of the last item in the list.
def numbers(list_of_numbers):
finalValues = [list_of_numbers]
#print(list_of_numbers)
for i in range(1,len(list_of_numbers)):
print("Last array of final: ", finalValues[-1])
tempArray = finalValues[-1]
print("Temp Array: ",tempArray)
temp = tempArray[0]
for j in range(0,len(list_of_numbers)-1):
tempArray[j] = tempArray[j+1]
tempArray[-1] = temp
finalValues.append(tempArray)
print("Final Values: ",finalValues)
return finalValues
numbers([1,2,3,4])
Program Output
[[4, 1, 2, 3], [4, 1, 2, 3], [4, 1, 2, 3], [4, 1, 2, 3]]
Correct Output
[[1,2,3,4], [2,3,4,1], [3,4,1,2], [4,1,2,3]]
The problem comes from the line:
tempArray = finalValues[-1]
You don't create a copy of the previous list, but only a new name to refer to it. After that, all changes you make to tempArray are actually changes to this list, and when you finally do:
finalValues.append(tempArray)
you just add another reference to this same list in finalValues.
In the end, finalValues contains 4 references to the same list, which you can access with finalValues[0], finalValues[1]...
What you need is to create a new list by copying the previous one. One way to do it is to use a slice:
tempArray = finalValues[-1][:]
You can find other ways to close or copy a list in this question
And so, the complete code gives the expected output:
Last array of final: [1, 2, 3, 4]
Temp Array: [1, 2, 3, 4]
Final Values: [[1, 2, 3, 4], [2, 3, 4, 1]]
Last array of final: [2, 3, 4, 1]
Temp Array: [2, 3, 4, 1]
Final Values: [[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2]]
Last array of final: [3, 4, 1, 2]
Temp Array: [3, 4, 1, 2]
Final Values: [[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
[[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
Thierry has provided a very comprehensive explanation of why your code doesn't work as you expect. As such it is the best answer to your question.I have added my answer just as an example of you you can code this in a less complex way .
create the 2d list with the first index as list of numbers. for each iteration take the last index of temp and slice from index 1 to the end then add on index 0.
then return the list
def numbers(list_of_numbers):
temp = [list_of_numbers]
for _ in range(1, len(list_of_numbers)):
temp.append(temp[-1][1:] + temp[-1][0:1])
return temp
print(numbers([1,2,3,4]))
OUTPUT
[[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
The problems is in shallow assignment of arrays. You should make deep copy, to really clone arrays, to make them independent.
I did it in your own code. There are a few changes of your code:
import copy that it have been added to first row.
Three usages of copy.deepcopy function instead of =(simple assignment).
import copy
def numbers(list_of_numbers):
finalValues = copy.deepcopy([list_of_numbers])
#print(list_of_numbers)
for i in range(1,len(list_of_numbers)):
print("Last array of final: ", finalValues[-1])
tempArray = copy.deepcopy(finalValues[-1])
print("Temp Array: ",tempArray)
temp = tempArray[0]
for j in range(0,len(list_of_numbers)-1):
tempArray[j] = tempArray[j+1]
tempArray[-1] = temp
finalValues.append(copy.deepcopy(tempArray))
print("Final Values: ",finalValues)
return finalValues
numbers([1,2,3,4])
Program Output
[[4, 1, 2, 3], [4, 1, 2, 3], [4, 1, 2, 3], [4, 1, 2, 3]]
Program Output
[[1,2,3,4], [2,3,4,1], [3,4,1,2], [4,1,2,3]]

Splitting a list of arbitrary size into N-not-equal parts [duplicate]

This question already has answers here:
How to group a list of tuples/objects by similar index/attribute in python?
(3 answers)
Closed 8 years ago.
I see splitting-a-list-of-arbitrary-size-into-only-roughly-n-equal-parts. How about not-equal splitting? I have list having items with some attribute (value which can be retrieved for running same function against every item), how to split items having same attribute to be new list e.g. new sublist? Something lambda-related could work here?
Simple example could be:
list = [1, 1, 1, 2, 3, 3, 3, 3, 4, 4]
After fancy operation we could have:
list = [[1, 1, 1], [2], [3, 3, 3, 3], [4, 4]]
>>> L = [1, 1, 1, 2, 3, 3, 3, 3, 4, 4]
>>> [list(g) for i, g in itertools.groupby(L)]
[[1, 1, 1], [2], [3, 3, 3, 3], [4, 4]]
>>> L2 = ['apple', 'aardvark', 'banana', 'coconut', 'crow']
>>> [list(g) for i, g in itertools.groupby(L2, operator.itemgetter(0))]
[['apple', 'aardvark'], ['banana'], ['coconut', 'crow']]
You should use the itertools.groupby function from the standard library.
This function groups the elements in the iterable it receives (by default using the identity function, i.e., checking consequent elements for equality), and for each streak of grouped elements, it reutrns a 2-tuple consisting of the streak representative (the element itself), and an iterator of the elements within the streak.
Indeed:
l = [1, 1, 1, 2, 3, 3, 3, 3, 4, 4]
list(list(k[1]) for k in groupby(l))
>>> [[1, 1, 1], [2], [3, 3, 3, 3], [4, 4]]
P.S. you should avoid using list as a variable name, as it would conflict with the built-in type/function.
Here's a pretty simple roll your own solution. If the 'attribute' in question is simply the value of the item, there are more straightforward approaches.
def split_into_sublists(data_list, sizes_list):
if sum(sizes_list) != len(data_list):
raise ValueError
count = 0
output = []
for size in sizes_list:
output.append(data_list[count:count+size])
count += size
return output
if __name__ == '__main__':
data_list = [1, 1, 1, 2, 3, 3, 3, 3, 4, 4]
sizes_list = [3,1,4,2]
list2 = [[1, 1, 1], [2], [3, 3, 3, 3], [4, 4]]
print(split_into_sublists(data_list, sizes_list) == list2) # True

Confounding recursive list append in Python

I'm trying to create a pair of functions that, given a list of "starting" numbers, will recursively add to each index position up to a defined maximum value (much in the same way that a odometer works in a car--each counter wheel increasing to 9 before resetting to 1 and carrying over onto the next wheel).
The code looks like this:
number_list = []
def counter(start, i, max_count):
if start[len(start)-1-i] < max_count:
start[len(start)-1-i] += 1
return(start, i, max_count)
else:
for j in range (len(start)):
if start[len(start)-1-i-j] == max_count:
start[len(start)-1-i-j] = 1
else:
start[len(start)-1-i-j] += 1
return(start, i, max_count)
def all_values(fresh_start, i, max_count):
number_list.append(fresh_start)
new_values = counter(fresh_start,i,max_count)
if new_values != None:
all_values(*new_values)
When I run all_values([1,1,1],0,3) and print number_list, though, I get:
[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1],
[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1],
[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1],
[1, 1, 1], [1, 1, 1], [1, 1, 1]]
Which is unfortunate. Doubly so knowing that if I replace the first line of all_values with
print(fresh_start)
I get exactly what I'm after:
[1, 1, 1]
[1, 1, 2]
[1, 1, 3]
[1, 2, 1]
[1, 2, 2]
[1, 2, 3]
[1, 3, 1]
[1, 3, 2]
[1, 3, 3]
[2, 1, 1]
[2, 1, 2]
[2, 1, 3]
[2, 2, 1]
[2, 2, 2]
[2, 2, 3]
[2, 3, 1]
[2, 3, 2]
[2, 3, 3]
[3, 1, 1]
[3, 1, 2]
[3, 1, 3]
[3, 2, 1]
[3, 2, 2]
[3, 2, 3]
[3, 3, 1]
[3, 3, 2]
[3, 3, 3]
I have already tried making a copy of fresh_start (by way of temp = fresh_start) and appending that instead, but with no change in the output.
Can anyone offer any insight as to what I might do to fix my code? Feedback on how the problem could be simplified would be welcome as well.
Thanks a lot!
temp = fresh_start
does not make a copy. Appending doesn't make copies, assignment doesn't make copies, and pretty much anything that doesn't say it makes a copy doesn't make a copy. If you want a copy, slice it:
fresh_start[:]
is a copy.
Try the following in the Python interpreter:
>>> a = [1,1,1]
>>> b = []
>>> b.append(a)
>>> b.append(a)
>>> b.append(a)
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> b[2][2] = 2
>>> b
[[1, 1, 2], [1, 1, 2], [1, 1, 2]]
This is a simplified version of what's happening in your code. But why is it happening?
b.append(a) isn't actually making a copy of a and stuffing it into the array at b. It's making a reference to a. It's like a bookmark in a web browser: when you open a webpage using a bookmark, you expect to see the webpage as it is now, not as it was when you bookmarked it. But that also means that if you have multiple bookmarks to the same page, and that page changes, you'll see the changed version no matter which bookmark you follow.
It's the same story with temp = a, and for that matter, a = [1,1,1]. temp and a are "bookmarks" to a particular array which happens to contain three ones. And b in the example above, is a bookmark to an array... which contains three bookmarks to that same array that contains three ones.
So what you do is create a new array and copy in the elements of the old array. The quickest way to do that is to take an array slice containing the whole array, as user2357112 demonstrated:
>>> a = [1,1,1]
>>> b = []
>>> b.append(a[:])
>>> b.append(a[:])
>>> b.append(a[:])
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> b[2][2] = 2
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 2]]
Much better.
When I look at the desired output I can't help but think about using one of the numpy grid data production functions.
import numpy
first_column, second_column, third_column = numpy.mgrid[1:4,1:4,1:4]
numpy.dstack((first_column.flatten(),second_column.flatten(),third_column.flatten()))
Out[23]:
array([[[1, 1, 1],
[1, 1, 2],
[1, 1, 3],
[1, 2, 1],
[1, 2, 2],
[1, 2, 3],
[1, 3, 1],
[1, 3, 2],
[1, 3, 3],
[2, 1, 1],
[2, 1, 2],
[2, 1, 3],
[2, 2, 1],
[2, 2, 2],
[2, 2, 3],
[2, 3, 1],
[2, 3, 2],
[2, 3, 3],
[3, 1, 1],
[3, 1, 2],
[3, 1, 3],
[3, 2, 1],
[3, 2, 2],
[3, 2, 3],
[3, 3, 1],
[3, 3, 2],
[3, 3, 3]]])
Of course, the utility of this particular approach might depend on the variety of input you need to deal with, but I suspect this could be an interesting way to build the data and numpy is pretty fast for this kind of thing. Presumably if your input list has more elements you could have more min:max arguments fed into mgrid[] and then unpack / stack in a similar fashion.
Here is a simplified version of your program, which works. Comments will follow.
number_list = []
def _adjust_counter_value(counter, n, max_count):
"""
We want the counter to go from 1 to max_count, then start over at 1.
This function adds n to the counter and then returns a tuple:
(new_counter_value, carry_to_next_counter)
"""
assert max_count >= 1
assert 1 <= counter <= max_count
# Counter is in closed range: [1, max_count]
# Subtract 1 so expected value is in closed range [0, max_count - 1]
x = counter - 1 + n
carry, x = divmod(x, max_count)
# Add 1 so expected value is in closed range [1, max_count]
counter = x + 1
return (counter, carry)
def increment_counter(start, i, max_count):
last = len(start) - 1 - i
copy = start[:] # make a copy of the start
add = 1 # start by adding 1 to index
for i_cur in range(last, -1, -1):
copy[i_cur], add = _adjust_counter_value(copy[i_cur], add, max_count)
if 0 == add:
return (copy, i, max_count)
else:
# if we have a carry out of the 0th position, we are done with the sequence
return None
def all_values(fresh_start, i, max_count):
number_list.append(fresh_start)
new_values = increment_counter(fresh_start,i,max_count)
if new_values != None:
all_values(*new_values)
all_values([1,1,1],0,3)
import itertools as it
correct = [list(tup) for tup in it.product(range(1,4), range(1,4), range(1,4))]
assert number_list == correct
Since you want the counters to go from 1 through max_count inclusive, it's a little bit tricky to update each counter. Your original solution was to use several if statements, but here I have made a helper function that uses divmod() to compute each new digit. This lets us add any increment to any digit and will find the correct carry out of the digit.
Your original program never changed the value of i so my revised one doesn't either. You could simplify the program further by getting rid of i and just having increment_counter() always go to the last position.
If you run a for loop to the end without calling break or return, the else: case will then run if there is one present. Here I added an else: case to handle a carry out of the 0th place in the list. If there is a carry out of the 0th place, that means we have reached the end of the counter sequence. In this case we return None.
Your original program is kind of tricky. It has two explicit return statements in counter() and an implicit return at the end of the sequence. It does return None to signal that the recursion can stop, but the way it does it is too tricky for my taste. I recommend using an explicit return None as I showed.
Note that Python has a module itertools that includes a way to generate a counter series like this. I used it to check that the result is correct.
I'm sure you are writing this to learn about recursion, but be advised that Python isn't the best language for recursive solutions like this one. Python has a relatively shallow recursion stack, and does not automatically turn tail recursion into an iterative loop, so this could cause a stack overflow inside Python if your recursive calls nest enough times. The best solution in Python would be to use itertools.product() as I did to just directly generate the desired counter sequence.
Since your generated sequence is a list of lists, and itertools.product() produces tuples, I used a list comprehension to convert each tuple into a list, so the end result is a list of lists, and we can simply use the Python == operator to compare them.

Python List filled with duplicates

Here is the code.
x=0
result=[]
for n in range(1,5):
x=x+n;
for i in range(1,10):
if x%i==0:
result.append(i)
print(x,result)
Here I have generated triangular numbers.I want to find the divisors of each triangular number.but when I execute the code I am getting the following output.
1 [1]
3 [1, 1]
3 [1, 1, 3]
6 [1, 1, 3, 1]
6 [1, 1, 3, 1, 2]
6 [1, 1, 3, 1, 2, 3]
6 [1, 1, 3, 1, 2, 3, 6]
10 [1, 1, 3, 1, 2, 3, 6, 1]
10 [1, 1, 3, 1, 2, 3, 6, 1, 2]
10 [1, 1, 3, 1, 2, 3, 6, 1, 2, 5]
Also same triangular number is repeated for several times.So I need an output looks like,
1 [1]
3 [1, 3]
6 [1, 2, 3, 6]
10 [1, 2, 5]
How can I get the output like this?thank you.
You are appending to the same result list inside the loop. You should create a new result list in the outer loop. And your print statement is wrongly indented. Move it outside the inner loop.
Your code should be:
x=0
for n in range(1,5):
x=x+n;
result = []
for i in range(1,10):
if x%i==0:
result.append(i)
print(x,result)
Output:
1 [1]
3 [1, 3]
6 [1, 2, 3, 6]
10 [1, 2, 5]
To find divisor of a number you can use this function
def divisors(n):
divisors=[]
for i in range(1,int(n**0.5)+1):
if n%i == 0:
divisors.extend((i,n/i))
return list(set(divisors))
if you find a number i which can divide a number n, both n and n/i become divisors..
The only problem will be with perfect square numbers where its root will be added twice(if i become root of n, n/i and i are equal and both will be added to list).
It is solved by set() function.

Categories