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