Array of tuples in Python - python

This is driving me nuts. The following code is basically the question. The first part halts almost immediately. The second seems to get stuck. However, they should be doing the exact same thing, since one == two.
one = [[(0,)]] + [[], [], [], [], [], [], [], [], []]
n = 9
two = [[(0,)]] + ([[]] * n)
if (one == two):
print "It is indeed the same thing!"
print "Fast:"
strings = one
print str(strings)
for j in range(0, n):
for i in strings[j]:
strings[j + 1].append(i + (0,))
strings[j + 1].append(i + (1,))
print "Slow:"
strings = two
print str(strings)
for j in range(0, n):
for i in strings[j]:
strings[j + 1].append(i + (0,))
strings[j + 1].append(i + (1,))

Equals == tests whether the arrays "look" the same (using standard tests of equality).
However they don't contain the same objects! i.e. the object references are not the same.
is tests whether the arrays are actually the same.
[[],[],[]] has three different lists.
[[]]*3 has three references to the same list.
So in one, for each j you're adding to a different sub-list. But in two you are adding to the same sub-list all the time.
Note re: testing with ==: equals is a method belonging to the list, and can be overridden to compare in the way you want to. It usually does "the sensible thing", which in this case, involves seeing if the lists are the same length, and if so, whether each of the elements are the same -- using == also. But is, on the other hand, is a more primitive thing: it literally sees if you are really referring to the same memory object.
to create multiple new (different) objects, you could do
[ [] for i in range(9) ]

The two options don't do the same thing. You can simplify your problem by the following two code snippets:
s = {"hello": "world"}
a = [s] * 2
print(a)
> [{'hello': 'world'}, {'hello': 'world'}]
Obviously you created a list with two dictionaries. But you created a list with the multiplier. That means, instead of two new separate dictionaries you create a list with two references to a single dictionary. Any change to this dictionary will affect all items in your list. Let's go on with our example:
s["hello"] = "python"
print(a)
> [{'hello': 'python'}, {'hello': 'python'}]
So we changed the initial dictionary which affects all elements. To summarize this: In your fast example you create a list with 10 individual list items. In your second example you create a list with one list that is referenced by each item. Changing this item like you do in the for loop will accumulate the size for all elements. So you create new items for each element on each iteration. In your example you should see a massive memory consumption because each iteration creates new elements for all elements in the list.

I suggest Python has no problem to loop over the first array, because its been initialized as its supposed to, while the other one only exists "theoretically" in memory.
Comparing each other initializes the second one and the result is both arrays are the same.
Looping has the same effect. While looping over the first one Python knows what to do, but looping over the other one makes Python initialize the array over and over again which maybe takes a while, because its a new array with every iteration.
But its only a suggestion.
And your code almost made my computer freeze :/

Related

first list gets auto updated aftter appending another second list to it and then changing the second list in Python

please take a look at this code
a = [int(x) for x in bin(2)[2:]]
coordinates = []
sum = [1,3]
for x in a:
if x == 1:
coordinates.append(sum)
m = ((3*(sum[0]**2)+A) * libnum.invmod(2*sum[1],p)) % p
x3 = (m**2 - 2*sum[0]) % p
sum[1] = ((m*(sum[0]-x3) - sum[1]) + p) % p
sum[0] = x3
my sum list gets updated in the loop and the new values should be added to the coordinates list if they match the conditions. the problem is that after appending the sum list to the coordinates list whenever the values change in sum they also change in coordinates. there is some kind of link between them. Can you please help me with this problem? How can I unlink them so that just the values are appended and stay fixed.
This is because when you assign a list value to a new variable, it only stores the reference number, when you append this variable to a new list, the same reference number is being used. So when you later change the value, it changes the values of the list defined by that reference number, hence changing what that variable stores wherever it was used.
e.g
coordinates = []
some = [1,3] # avoid using 'sum' as variable name as it is a built in function
coordinates.append(some)
print("coordinates initially:", coordinates)
some.append("interesting")
print("coordinates after chaning 'some':", coordinates)
Output:
coordinates initially: [[1, 3]]
coordinates after chaning 'some': [[1, 3, 'interesting']]
This doenst make sense for small lists but when you consider that lists can hold huge amounts of values, it makes it much faster to use reference numbers
Thus, the solution is that we need to infact append a copy of the some list and not just the reference number.
For this, we can do the following:
import copy
# We can use the copy.deepcopy() function and pass it the list we need copied
coordinates.append(copy.deepcopy(some))

Finding every possible list made by removing the first element from either of two lists and appending to the new list

I have two lists of ints, a and b, which do not necessarily have the same length. I would like to create new lists from these by removing either the first element of a, or the first element of b, and appending it to the new list, repeating this step until both a and b are empty. At each step in this process, the number of possible lists grows exponentially, and I'd like to know how to generate every list that is possible to create in this way.
So far, I've only managed to work out that the number of possible lists is equal to sum((2**i for i in range(len(a) + len(b)))). I have no idea how to proceed with this, and would appreciate any pointers.
For information, my end goal is to work out the sum of the differences between consecutive elements for each list, and find the minimum of these.
I think this can be achieved by using recursion. Some code.
permutation = [0]*10 # size of this list has to be equal to lenth of list1 + length of list2. (you can have 4 as the size of the list).
def task(list1,list2,index):
if len(list1)==0 and len(list2)==0: # if length of both the list is 0, we print the
print(permutation) # permutation list
return
if len(list1)>0:
permutation[index] = list1[0]
modified_list1 = list1[:] # Since lists in python are passed by reference, I am making a copy of the list
modified_list1.pop(0) # Removing the first element
task(modified_list1,list2,index+1) #and calling the function again using the modified list.
if len(list2)>0:
permutation[index] = list2[0]
modified_list2 = list2[:]
modified_list2.pop(0)
task(list1,modified_list2,index+1)
if __name__=="__main__":
list1 = [1]
list2 = [4,5,6]
task(list1,list2,0)
Recursive solutions can be a little tricky to understand, I will encourage you
to take a copy and pen and try simulating it for small input, you will
understand how things are working.
For your next task, when we are printing the permutation list, you can compute the differences of the adjacent numbers and store your result in any way you want.

TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'

I'm trying to take the values from the previous function and use in another function. This is my first programming class and language, and i'm totally lost.
I figured out how to take the variables from astlist and put them into the function distance, but now Python is telling me I can't use these variables in an equation because they're in a list now? Is that what it's saying?
I'm also just printing the lists to see if they are running. These are two of my functions, and the functions are both defined in my main function.
I'm taking these lists and eventually putting them into files, but I need to figure out why the equation isn't working first. Thanks!
def readast():
astlist = []
for j in range(15):
list1 = []
for i in range(3):
x = random.randint(1,1000)
y = random.randint(1,1000)
z = random.randint(1,1000)
list1.append([x,y,z])
astlist.append(list1)
print(astlist)
return astlist
def distance(astlist):
distlist = []
for row in range(len(astlist)):
x, y, z = astlist[row]
x1 = x**2
y2 = y**2
z2 = z**2
equation = math.sqrt(x+y+z)
distlist.append(equation)
print(distlist)
return distlist
The variable astlist is a list. You're adding list1 to it several times which is also a list. But you're also adding a list to list1 each time: list1.append([x,y,z]). So ultimately astlist is a list containing multiple lists which each contain a list with three integers.
So when you write x,y,z=astlist[row] the variables x, y and z are actually lists, not integers. This means you're trying to compute x**2 but x is a list, not a number. This is why Python is giving you an error message as ** doesn't support raising a list to a power.
I'm not sure what you're trying to accomplish with all these lists but you should change the code so that you're only trying to raise numbers to the power of two and not lists.
There are a few problems here:
Firstly the loop at the top of readast() sets list1 to [] 15 times - I'm not sure what you're trying to do here. If you are trying to generate 15 sets of x,y,z coordinates then it is the second range - in your example the range(3)
- that you need to change.
Then you keep adding lists of [x,y,z] to (the same) list1, then adding the whole of list1 to astlist. However, Python actually stores a pointer to the list rather than a copy so when you add items to list1, it adds items to list1 whereever list1 is included in another list:
In this example the random numbers are replaced with sequential numbers for clarity (the first random number is 1, the second 2 and so on):
After first cycle of loop:
list1: [[1,2,3]]
astlist: [[[1,2,3]]]
After second cycle of loop:
list1: [[1,2,3],[4,5,6]]
astlist: [[[1,2,3],[4,5,6]],[[1,2,3],[4,5,6]]]
and so on
As you can see, list1 is now a list of lists, and astlist is now a list of duplicates of list1 (a list of lists of lists)
list1 is probably redundant and you probably want just
astlist.append([x,y,z])
in the first bit.
In the second function, you use
for row in range(len(astlist)):
x,y,z=astlist[row]
...
but actually the following would be better:
for row in astlist:
x,y,z=row
...
or even:
for x,y,z in astlist:
...
as for loops in Python iterate over members of a sequence (or other iterable value) rather being just a simple counter. What you are doing with the range(len(astlist)) construct is actually generating a list [1,2,3...] and iterating over that.
If you particularly need a numerical index then you can use the enumerate function which returns a series of (index,value) pairs that you can iterate over thus:
for i,value in enumerate(['apple','banana','cherry']):
print 'value {} is {}'.format(i,value)
value 0 is apple
value 1 is ball
value 2 is cherry
Hope this helps
I will refer to the specific type error (TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'), for some reason arrays are iterable objects for operations like addition: sum(array), but, power: array**2, pow(array,2). You can solve this with some extra steps as follow:
x1 = [j**2 for j in x]
also I recommend to use sum function
sum(x,y,z)
remember all this is to avoid the error message that you were referring to
that way you apply the power of 2 to each element in the array, getting a new array and avoiding the error message that you were asking help for. It seems to me that you are looking to get a normalization of your data using norm L2, if that is true, well, I think you are missing half of it.

Modifying a list in place: Calculating a sieve of primes in a python list

Problem number 10 of Project Euler asks us to calculate the sum of all primes below 2,000,000.
My algorithm for the same is:
Construct a sieve for numbers under 2,000,000
Sum all the numbers in the sieve.
I am not comfortable with the code creating multiple lists instead of performing computations over the same list.
Given below is my code:
def main(number_above):
list_of_numbers = list(range(number_above))
list_of_numbers = calculate_sieve(list_of_numbers)
print summation_of_primes(list_of_numbers)
def calculate_sieve(list_of_numbers):
for prime in list_of_numbers:
if prime >= 2 and prime != 'C':
multiple = 2
while multiple * prime < len(list_of_numbers):
list_of_numbers[ prime * multiple ] = 'C'
multiple += 1
return list_of_numbers
def summation_of_primes(list_of_numbers):
summation = 0
for element in list_of_numbers:
if element != 'C':
summation += element
return summation - 1
The steps in which the lists are created:
First, a list is created of numbers in range(2,000,000)
Second, this list is passed on to the calculate_sieve function which cancels all composites.
Then, the calculate_sieve function returns a list to the main function.
Finally, this list is passed on to the summation function.
Is python operating on the same list in place, or does it hold more than one list at a time?
If it is creating multiple copies of the list, is there a way to minimize memory usage by operating on the list in place?
Is python operating on the same list in place?
Yes, mostly.
passing a list as an argument to a function does not create a new list.
modifying elements in a list does not create a new list.
returning a list from a function does not create a new list.
The only code you have that may potentially create a duplicate list is this line:
list_of_numbers = list(range(number_above))
In Python 2.7 and below, range already returns a list. Calling list on the result will create a second list. You can safely just write list_of_numbers = range(number_above) and save yourself a little memory.
In 3.X, range returns a range object, so the list call is necessary there, if you want to subsequently do assignment, like list_of_numbers[ prime * multiple ] = 'C'.
You can check it very easily:
In [1]: def some_func(seq):
...: seq.append(1)
...:
In [2]: s = []
In [3]: some_func(s)
In [4]: s
Out[4]: [1]
Since s got modified some_func cannot operate on a copy of it.
Identifiers (or variables) in python are always references to an object. They are not memory locations. They are names to an object. When you pass an object to a function the arguments simply creates a new reference to that object, so there's no copying involved.
I believe all your computations are performed on the same list in place, a list is mutable and it is passed by reference, hence I believe in your case there will only be one list in memory at the same time.

How do I handle the following situation in Python?

I want to say
a[current] = value
rather than saying
a.append(value)
because I want to show that the current value is value. The former listing shows this better. I come from C, so I am a bit confused with python lists. In C I preallocate space, so a[current] would exist and contain junk before I assign it value. Can I do something similar in Python?
You can do something like
[0] * 10
which will result in
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
But your approach will probably not be very "pythonic". If switching to Python, you should also think about thinking in python. ;-)
When I first started using Python I ran into this problem all the time. You can do things like '[0]*10', but is inelegant, and will cause you problems if you try to do the same thing for lists of lists.
I finally solved the problem by realizing most of the time I just needed to reformulate the problem into something more pythonic. List comprehensions (as noted above) will almost always be the correct answer. If you are preallocating space, that means you are probably going to iterate over some other list, do some operation, then set an element in the new list to that value:
newList = [op(e) for e in oldList]
You can get fancier. If for example, you don't want all the elements, you can set up a filter:
newList = [op(e) for e in oldList if e < 5]
which will only put items that are less than 5 into the newList.
However, sometimes list comprehension isn't want you want. For example, if you're doing math-oriented coding, then numpy can come to the rescue:
myVector = numpy.zeros(10)
will create an array with 10 elements.
You can allocate a list of length n by using
my_list = [None] * n
Obviously, the list will be initialised rather than containing junk.
That said, note that often a list comprehension is a good replacement for a loop containing calls to list.append().
If you want to create a list with n elements initialized to zero (read "preallocate") in Python, use this:
somelist = [0] * n
Is this what you want?
If you don't like append, you can do things like
a = [None]*10
for i in range(10):
a[i] = something()
you might be interested also in python arrays.
I think that the most approximate syntax would be:
a.insert(current, value)
but if current isn't the last position in the array, insert will allocate some extra space and shift everything after current in one position. Don't know if this is the desired behavior. The following code is just like an append:
a.insert(len(a), value)
If you want to show that the current value is 'value', why don't you just use a variable for it?
a.append(value)
current_value = value
If you are maintaining a separate current variable to indicate where the next item will be inserted (that is, your line a[current] = value is followed immediately by current += 1 and you wish you could just write a[current++] = value), then you're writing C in Python and should probably stop. :-)
Otherwise you probably want to preallocate the list with the number of items you want it to contain, as others have shown. If you want a list that will automatically extend to fill in missing values, such that a[100] = value will work even if the list only has 50 items, this can be done with a custom __setitem__() method on a list subclass:
class expandinglist(list):
def __setitem__(self, index, value):
length = len(self)
if index < length:
list.__setitem__(self, index, value)
elif index = length: # you don't actually need this case, it's just a bit
self.append(value) # faster than the below for adding a single item
else:
self.extend(([0] * (index - length)) + [value])
lyst = expandinglist()
lyst[5] = 5
print lyst
>> [0, 0, 0, 0, 0, 5]

Categories

Resources