Let Os be a partially ordered set, and given any two objects O1 and O2 in Os, F(O1,O2) will return 1 if O1 is bigger than O2, -1 if O1 is smaller than O2, 2 if they are incomparable, and 0 if O1 is equal to O2.
I need to find the subset Mn of elements is Os that are the minimum. That is for each A in Mn, and for each B in Os, F(A,B) is never equal to 1.
It is not hard to do, but I am convinced it could be done in a more pythonic way.
The fast and dirty way is:
def GetMinOs(Os):
Mn=set([])
NotMn=set([])
for O1 in Os:
for O2 in Os:
rel=f(O1,O2)
if rel==1: NotMn|=set([O1])
elif rel==-1: NotMn|=set([O2])
Mn=Os-NotMn
return Mn
In particular I am not happy with the fact that I am essentially going through all the elements N^2 times. I wonder if there could be a dynamic way. By "dynamic" I don't mean merely fast, but also such that once something is discovered being not a possible in the minimum, maybe it could be taken off. And doing all this in a pythonic, elegant way
GetMinOs2
below, "dynamically" removes elements which are known to be non-minimal. It uses a list Ol
which starts with all the elements of Os
.
A "pointer" index l
points to the "end" of the list Ol
. When a non-minimal element is found, its position is swapped with the value in Ol[l]
and the pointer l
is decremented so the effective length of Ol
is shrunk.
Doing so removes non-minimal elements, so you don't check them again.
GetMinOs2
assumes f
has the normal properties of a comparison function: transitivity, commutativity, etc.
In the test code below, with a dreamt-up f
, my timeit run shows about a 54x improvement in speed:
def f(O1,O2):
if O1%4==3 or O2%4==3: return 2
return cmp(O1,O2)
def GetMinOs(Os):
Mn=set([])
NotMn=set([])
for O1 in Os:
for O2 in Os:
rel=f(O1,O2)
if rel==1: NotMn|=set([O1])
elif rel==-1: NotMn|=set([O2])
Mn=Os-NotMn
return Mn
def GetMinOs2(Os):
Ol=list(Os)
l=len(Ol)
i=0
j=1
while i<l:
while j<l:
rel=f(Ol[i],Ol[j])
if rel==1:
l-=1
Ol[i]=Ol[l]
j=i+1
break
elif rel==-1:
l-=1
Ol[j]=Ol[l]
else:
j+=1
else:
i+=1
j=i+1
return set(Ol[:l])
Os=set(range(1000))
if __name__=='__main__':
answer=GetMinOs(Os)
result=GetMinOs2(Os)
assert answer==result
The timeit results are:
% python -mtimeit -s'import test' 'test.GetMinOs2(test.Os)'
1000 loops, best of 3: 22.7 msec per loop
% python -mtimeit -s'import test' 'test.GetMinOs(test.Os)'
10 loops, best of 3: 1.23 sec per loop
PS. Please be warned: I haven't thoroughly checked the algorithm in GetMinOs2, but I think the general idea is right. I've put a little test at the end of the script which shows it works at least on the sample data set(range(1000))
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With