How to write this statement more compactly without a ++ operator? - python

Here's what I want to write:
groups[m][n] = groups[m - 1][n] or ++gid
Here's what I have to write:
g = groups[m - 1][n]
if g:
groups[m,n] = g
else:
gid += 1
groups[m][n] = gid
Is there no more compact way of writing that in Python simply because it lacks a ++ operator?
A larger sample from a method I'm working on:
groups = [[0] * self.columns] * self.rows
gid = 0
for m in xrange(self.rows):
for n in xrange(self.columns):
stone = self[m, n]
if stone == self[m - 1, n]:
if groups[m - 1][n]:
groups[m][n] = groups[m - 1][n]
else:
gid += 1
groups[m][n] = gid
elif stone == self[m, n - 1]:
if groups[m][n - 1]:
groups[m][n] = groups[m][n - 1]
else:
gid += 1
groups[m][n] = gid
I think it's a lot harder to read when I have to blow it out like that, plus I'm evaluating m-1 twice... I'm not sure how I can condense it though.
This is what I came up with:
I created a wrapper class around int:
class Int(object):
def __init__(self, i):
self.i = i
def pre(self, a=1):
self.i += a
return Int(self.i)
def post(self, a=1):
cpy = Int(self.i)
self.i += a
return cpy
def __repr__(self):
return str(self.i)
def __nonzero__(self):
return self.i != 0
Which can be used like this:
def group_stones(self):
groups = [[None for _ in xrange(self.cols)] for _ in xrange(self.rows)]
gid = Int(0)
for m in xrange(self.rows):
for n in xrange(self.cols):
stone = self[m, n]
if stone == self[m - 1, n]:
groups[m][n] = groups[m - 1][n] or gid.pre()
elif stone == self[m, n - 1]:
groups[m][n] = groups[m][n - 1] or gid.pre()
else:
groups[m][n] = gid.pre()
Much like I would do in other languages.

gid = [0] # list - mutable object
def incremented(gid):
gid[0] += 1
return gid[0]
groups[m][n] = groups[m - 1][n] or incremented(gid)
You can add some "magic" to your Int class:
class C(object):
...
def __add__(self, other):
self.i += other
return self.__class__(self.i)
def __radd__(self, other):
cpy = self.__class__(self.i)
self.i += other
return cpy
>>> print Int(2) + 1 # pre
3
>>> i = Int(2)
>>> print 1 + i # post
2
>>> print i
3

Technically more compact, but not really more readable nor less DRY:
groups[m][n], gid = (groups[m-1][n], gid) if groups[m-1][n] else (gid+1, gid+1)
Less compact (for a single usage, at least), more readable:
def test_or_inc(val, accum):
return (val, accum) if val else (accum+1, accum+1)
groups[m][n], gid = test_or_inc(groups[m-1][n], gid)
Another option is to make gid something you can pass by reference... such as a property of an object, or an item in a list.

If you put the gid generation in a function you can do that. For example (using the global scope):
gid = 0
def newgid(): global gid; gid += 1; return gid
Now you can write:
groups[m][n] = groups[m - 1][n] or newgid()
Of course it would be better to put the gid and newgid in its own class or in the class where your other methods are.

Related

I don't know what is wrong with this python code

class FC():
def data(self, a, b):
self.n1 = a
self.n2 = b
def add(self):
return (self.n1 + self.n2)
>>> def pbnc(start, num):
pb = FC()
pb.data(start, start)
while (num > 0):
print(pb.add())
pb.data(pb.n2, pb.add())
num -= 1
>>> def pbnc(1, 10)
SyntaxError: invalid syntax
I'm currently learning 'Class' in python. And I can't find the wrong thing in this code. Is it wrong to use classes in other functions?
Appears to be a simple indentation error, this should be fine:
class FC():
def data(self, a, b):
self.n1 = a
self.n2 = b
def add(self):
return (self.n1 + self.n2)
def pbnc(self, start, num):
pb = FC()
pb.data(start, start)
while (num > 0):
print(pb.add())
pb.data(pb.n2, pb.add())
num -= 1
'''
# Uncomment this part if you want this method outside of the class
def pbnc(start, num):
pb = FC()
pb.data(start, start)
while (num > 0):
print(pb.add())
pb.data(pb.n2, pb.add())
num -= 1
'''

binary search delete min

I am wondering in the following code,
In the code below i is set to min child, why is this done instead of say just having i = i*2. Both ways 'lower' the level of the binary heap, I am just curious as to why the minimum would be chosen (Assuming both children are NOT smaller than parent, why is the smaller of the two larger children chosen instead of one of them arbitrarily)
To be clear these methods below also belong to the binaryheap class
def percDown(self,i):
while (i * 2) <= self.currentSize:
mc = self.minChild(i)
if self.heapList[i] > self.heapList[mc]:
tmp = self.heapList[i]
self.heapList[i] = self.heapList[mc]
self.heapList[mc] = tmp
i = mc
def minChild(self,i):
if i * 2 + 1 > self.currentSize:
return i * 2
else:
if self.heapList[i*2] < self.heapList[i*2+1]:
return i * 2
else:
return i * 2 + 1
The code for the binary heap class is
class BinHeap:
def __init__(self):
self.heapList = [0]
self.currentSize = 0
def delMin(self):
retval = self.heapList[1]
self.heapList[1] = self.heapList[self.currentSize]
self.currentSize = self.currentSize - 1
self.heapList.pop()
self.percDown(1)
return retval

What exactly is not iterable here, and how to fix it?

This kind of questions has been asked a lot here, but I still don't know why it happens here. The error massage is:
TypeError: 'P' object is not iterable
and is invoked by for p in self.parents: from __str__
Why P isn't iterable?
class P:
limit = 8
def __init__(self,x=0,y=0,p=None):
self.x = x
self.y = y
self.parents = p
def __str__(self):
s = ''
for p in self.parents:
s += '({},{}->)'.format(p.x,p.y)
s += '({},{}->)'.format(self.x,self.y)
return s + '\n'
def next_move(self):
def valid(x):
return x >= 0 and x < P.limit
x,y = self.x,self.y
a = [-1,1]
b = [-2,2]
ls = [(x+i, y+j) for i in a for j in b if valid(x+i) and valid(y+j)]
ls +=[(x+i, y+j) for i in b for j in a if valid(x+i) and valid(y+j)]
to_return = []
for i in range(len(ls)):
to_return += P(ls[i][0],ls[i][1],self)
return to_return
p = P()
print (p.next_move())
EDIT:
My issue here is not how to remove the error (using append) but how to use next_move() to create a list of new P that are a horse-move (Chess) away from the parent, and also append said parent to the parents attribute in the new object. The answers you gave me help avoid the error, but I don't know how to proceed
Two issues:
Firstly, You are trying to add a P instance to a list via +=. But += for lists roughly corresponds to extend and takes an iterable:
for i in range(len(ls)):
to_return += [P(...)]
# OR
# to_return.append(P(...))
Secondly, when you call the constructor with P(ls[i][0], ls[i][1], self), you are passing self as the p parameter which should be iterable itself. You might want to use P(ls[i][0], ls[i][1], [self]) instead.
You are actually getting this error at the following line:
to_return += P(ls[i][0],ls[i][1],self)
This is because you should be appending to to_return like this
to_return.append(P(ls[i][0],ls[i][1],self))
or, if you have any particular reasons to do so, like this:
to_return += [P(ls[i][0],ls[i][1],self)]
In addition, that self you passed as argument is not iterable either. Then you would have the problem in __str__, if it was to be called.
to_return.append(P(ls[i][0], ls[i][1], [self]))
Finally, I believe you meant __repr__ instead of __str__, so that you print:
[(0,0->)(1,2->)
, (0,0->)(2,1->)
]
Try this:
class P:
limit = 8
def __init__(self, x=0, y=0, p=None):
self.x = x
self.y = y
self.parents = p
def __str__(self):
s = ''
for p in self.parents:
s += '({},{}->)'.format(p.x, p.y)
s += '({},{}->)'.format(self.x, self.y)
return s + '\n'
def next_move(self):
def valid(x):
return 0 <= x < P.limit
x, y = self.x, self.y
a = [-1, 1]
b = [-2, 2]
ls = [(x + i, y + j) for i in a for j in b if valid(x + i) and valid(y + j)]
ls += [(x + i, y + j) for i in b for j in a if valid(x + i) and valid(y + j)]
to_return = []
for i in range(len(ls)):
to_return.append(P(ls[i][0], ls[i][1], self))
return to_return
p = P()
print(p.next_move())
See changes in Line 27
Because to_return is a list, so we can't use + operator. We can use append function for list.

Multivariable Cumulants and Moments in python

In Mathematica I can convert multivariable moments in cumulants and back using MomentConvert:
MomentConvert[Cumulant[{2, 2,1}], "Moment"] // TraditionalForm
as one can try in wolframcloud.
I would like to do exactly the same in python. Is there any library in python capable of this?
At least the one direction I now programmed by myself:
# from http://code.activestate.com/recipes/577211-generate-the-partitions-of-a-set-by-index/
from collections import defaultdict
class Partition:
def __init__(self, S):
self.data = list(S)
self.m = len(S)
self.table = self.rgf_table()
def __getitem__(self, i):
#generates set partitions by index
if i > len(self) - 1:
raise IndexError
L = self.unrank_rgf(i)
result = self.as_set_partition(L)
return result
def __len__(self):
return self.table[self.m,0]
def as_set_partition(self, L):
# Transform a restricted growth function into a partition
n = max(L[1:]+[1])
m = self.m
data = self.data
P = [[] for _ in range(n)]
for i in range(m):
P[L[i+1]-1].append(data[i])
return P
def rgf_table(self):
# Compute the table values
m = self.m
D = defaultdict(lambda:1)
for i in range(1,m+1):
for j in range(0,m-i+1):
D[i,j] = j * D[i-1,j] + D[i-1,j+1]
return D
def unrank_rgf(self, r):
# Unrank a restricted growth function
m = self.m
L = [1 for _ in range(m+1)]
j = 1
D = self.table
for i in range(2,m+1):
v = D[m-i,j]
cr = j*v
if cr <= r:
L[i] = j + 1
r -= cr
j += 1
else:
L[i] = r // v + 1
r %= v
return L
# S = set(range(4))
# P = Partition(S)
# for x in P:
# print (x)
# using https://en.wikipedia.org/wiki/Cumulant#Joint_cumulants
import math
def Cum2Mom(arr, state):
def E(op):
return qu.expect(op, state)
def Arr2str(arr,sep):
r = ''
for i,x in enumerate(arr):
r += str(x)
if i<len(arr)-1:
r += sep
return r
if isinstance( arr[0],str):
myprod = lambda x: Arr2str(x,'*')
mysum = lambda x: Arr2str(x,'+')
E=lambda x: 'E('+str(x)+')'
myfloat = str
else:
myfloat = lambda x: x
myprod = np.prod
mysum = sum
S = set(range(len(arr)))
P = Partition(S)
return mysum([
myprod([myfloat(math.factorial(len(pi)-1) * (-1)**(len(pi)-1))
,myprod([
E(myprod([
arr[i]
for i in B
]))
for B in pi])])
for pi in P])
print(Cum2Mom(['a','b','c','d'],1) )
import qutip as qu
print(Cum2Mom([qu.qeye(3) for i in range(3)],qu.qeye(3)) )
It's designed to work with qutip opjects and it also works with strings to verify the correct separation and prefactors.
Exponents of the variables can be represented by repeating the variable.

Which data structure to use when implementing graph representation using adjacency list

I have a graph that is very big about 1,000,000 nodes and many edges. This is what i wanted to know which is the best suited data structure when implementing an adjacency list. Here are the objects that i keep track of
Edge list
Node to node connection list
I am coding with python so I used a set(because according to this it has a o(1) average insertion time) for edge list and a dictionary to node to node connection list(by making it completely hashable according to How to make an object properly hashable?). Here is my code
class node:
def __init__(self, name = ""):
self.__name = name
def getName(self):
return self.__name
def __str__(self):
return self.__name
def __hash__(self):
return hash(self.__name)
def __lt__(self, other):
if(type(self) != type(other)):
return NotImplemented
return self.__name.__lt__(other.__name)
def __eq__(self, other):
if(type(self)) != type(other):
return NotImplemented
return self.__name == other.__name
class Edge:
def __init__(self, name = "", node1 = None, node2 = None, weight = 0):
self.__name = name
self.__firstNode = node1
self.__secondNode = node2
self.__weight = weight
def getName(self):
return self.__name
def getFirstNode(self):
return self.__firstNode
def getSecondNode(self):
return self.__secondNode
def getWeight(self):
return self.__weight
def __lt__(self, other):
if(type(self) != type(other)):
return NotImplemented
return self.__name.__lt__(other.__name) and self.__firstNode.__lt__(other.__firstNode) and self.__secondNode.__lt__(other.__secondNode) and self.__weight.__lt__(other.__weight)
def __eq__(self, other):
if(type(self) != type(other)):
return NotImplemented
return self.__name == other.__name and self.__firstNode == other.__firstNode and self.__secondNode == other.__secondNode and self.__weight == other.__weight
def __str__(self):
return self.__name + " " + str(self.__firstNode) + " " + str(self.__secondNode) + " " + str(self.__weight)
def __hash__(self):
return hash(hash(self.__name) + hash(self.__firstNode) + hash(self.__secondNode) + hash(self.__weight))
class graph:
def __init__(self):
self.__nodeToNode = {}
self.__edgeList = set()
def addEdge(self, edge):
if(type(edge) != type(Edge())):
return False
self.__edgeList.add(edge)
if(not edge.getFirstNode() in self.__nodeToNode):
self.__nodeToNode[edge.getFirstNode()] = set()
self.__nodeToNode[edge.getFirstNode()].add(edge.getSecondNode())
if(not edge.getSecondNode() in self.__nodeToNode):
self.__nodeToNode[edge.getSecondNode()] = set()
self.__nodeToNode[edge.getSecondNode()].add(edge.getSecondNode())
return True
def getNodes(self):
return dict(self.__nodeToNode)
def getEdges(self):
return set(self.__edgeList)
import string
import random
import time
grp = graph()
nodes = [None] * 20000
for i in range(20000):
st = ''.join(random.SystemRandom().choice(string.ascii_letters) for i in range(10))
node1 = node(st)
nodes[i] = node1
current = time.time()
for i in range(3000000):
rdm = random.randint(0, 199)
rdm2 = random.randint(0, 199)
st = ''.join(random.SystemRandom().choice(string.ascii_letters) for i in range(10))
eg = Edge(st, nodes[rdm], nodes[rdm2])
grp.addEdge(eg)
last = time.time()
print((last - current))
nodes = grp.getNodes()
edges = grp.getEdges()
but this code runs very slowly can i make it faster? If so by using what data structure?
Let me introduce you a way to create an adjacency list:
Suppose you have the input like this:
4 4
1 2
3 2
4 3
1 4
The first line contains 2 numbers V and E, the next E lines defines an edge between two vertices.
You can either create a .txt file and read the input or directly type in via sys.stdin.read():
input = sys.stdin.read()
data = list(map(int, input.split()))
n, m = data[0:2]
data = data[2:]
edges = list(zip(data[0:(2 * m):2], data[1:(2 * m):2]))
x, y = data[2 * m:]
adj = [[] for _ in range(n)]
x, y = x - 1, y - 1
for (a, b) in edges:
adj[a - 1].append(b - 1)
adj[b - 1].append(a - 1)
Let's output the adjacency list adj:
>>> print(adj)
[[1, 3], [0, 2], [1, 3], [2, 0]]
adj[0] have two adj nodes: 1 and 3. Meaning the node 1 have two adj nodes: 2 and 4.
And now, if you want a directed, weighted graph, you just need to modify the input like this:
4 4
1 2 3 # edge(1, 2) has the weight of 3
3 2 1
4 3 1
1 4 2
input = sys.stdin.read()
data = list(map(int, input.split()))
n, m = data[0:2]
data = data[2:]
edges = list(zip(zip(data[0:(3 * m):3], data[1:(3 * m):3]), data[2:(3 * m):3]))
data = data[3 * m:]
adj = [[] for _ in range(n)]
cost = [[] for _ in range(n)]
for ((a, b), w) in edges:
adj[a - 1].append(b - 1)
cost[a - 1].append(w)
You store the weight in cost, and for example, cost[0][1] = 3, cost[0][3] = 2.
Hope this helped!

Categories