# -*- coding: utf-8 -*-
from __future__ import print_function
from itertools import islice, cycle, chain, izip, repeat
def detect_cycle(iterable):
'''Detect a cycle in iterable object.
Iterable must return an independend iterator on iter(iterable)'''
tortoise = iter(iterable)
hare = islice(iter(iterable),None,None,2) #two steps at a time
#it will start from the first value and only then will be advancing 2 values at a time
for tortoise_val, hare_val in islice(izip(tortoise,hare),1,None): #skip the first value
if tortoise_val==hare_val:
break
hare = tortoise #put hare in the place of tortoise
hare_val = tortoise_val
tortoise = iter(iterable) #start tortoise from the very beginning
mu = 0
for tortoise_val, hare_val in izip(tortoise,chain(repeat(hare_val,1),hare)):
#begin with the current val of hare.val and the value of tortoise which is in the first position
if tortoise_val==hare_val:
break
mu += 1
lamb = 0
hare = tortoise
for hare_val in hare:
lamb += 1
if hare_val == tortoise_val: #check AFTER the first step by hare
break
print('mu: {0}'.format(mu))
print('lamb: {0}'.format(lamb))
if __name__ == '__main__':
class iterable(object):
'''Emulate the object returning iterators having cycles'''
def __init__(self,beginning,cycling):
'''
beginning: the beginning part with no cycles (its length corresponds to mu)
cycling: the part which will be cycling (its length corresponds to lamb)
'''
self.beginning = beginning
self.cycling = cycling
def __iter__(self):
return chain(iter(self.beginning),cycle(iter(self.cycling)))
detect_cycle(iterable('1234','678'))
IyAtKi0gY29kaW5nOiB1dGYtOCAtKi0KCmZyb20gX19mdXR1cmVfXyBpbXBvcnQgcHJpbnRfZnVuY3Rpb24KZnJvbSBpdGVydG9vbHMgaW1wb3J0IGlzbGljZSwgY3ljbGUsIGNoYWluLCBpemlwLCByZXBlYXQKCgpkZWYgZGV0ZWN0X2N5Y2xlKGl0ZXJhYmxlKToKICAgICcnJ0RldGVjdCBhIGN5Y2xlIGluIGl0ZXJhYmxlIG9iamVjdC4KICAgIEl0ZXJhYmxlIG11c3QgcmV0dXJuIGFuIGluZGVwZW5kZW5kIGl0ZXJhdG9yIG9uIGl0ZXIoaXRlcmFibGUpJycnCgogICAgdG9ydG9pc2UgPSBpdGVyKGl0ZXJhYmxlKQogICAgaGFyZSA9IGlzbGljZShpdGVyKGl0ZXJhYmxlKSxOb25lLE5vbmUsMikgICN0d28gc3RlcHMgYXQgYSB0aW1lCiAgICAjaXQgd2lsbCBzdGFydCBmcm9tIHRoZSBmaXJzdCB2YWx1ZSBhbmQgb25seSB0aGVuIHdpbGwgYmUgYWR2YW5jaW5nIDIgdmFsdWVzIGF0IGEgdGltZQoKICAgIGZvciB0b3J0b2lzZV92YWwsIGhhcmVfdmFsIGluIGlzbGljZShpemlwKHRvcnRvaXNlLGhhcmUpLDEsTm9uZSk6ICNza2lwIHRoZSBmaXJzdCB2YWx1ZQogICAgICAgIGlmIHRvcnRvaXNlX3ZhbD09aGFyZV92YWw6CiAgICAgICAgICAgIGJyZWFrCiAgICAKICAgIGhhcmUgPSB0b3J0b2lzZSAjcHV0IGhhcmUgaW4gdGhlIHBsYWNlIG9mIHRvcnRvaXNlCiAgICBoYXJlX3ZhbCA9IHRvcnRvaXNlX3ZhbAogICAgdG9ydG9pc2UgPSBpdGVyKGl0ZXJhYmxlKSAjc3RhcnQgdG9ydG9pc2UgZnJvbSB0aGUgdmVyeSBiZWdpbm5pbmcKICAgIG11ID0gMAogICAgZm9yIHRvcnRvaXNlX3ZhbCwgaGFyZV92YWwgaW4gaXppcCh0b3J0b2lzZSxjaGFpbihyZXBlYXQoaGFyZV92YWwsMSksaGFyZSkpOgogICAgICAgICNiZWdpbiB3aXRoIHRoZSBjdXJyZW50IHZhbCBvZiBoYXJlLnZhbCBhbmQgdGhlIHZhbHVlIG9mIHRvcnRvaXNlIHdoaWNoIGlzIGluIHRoZSBmaXJzdCBwb3NpdGlvbgogICAgICAgIGlmIHRvcnRvaXNlX3ZhbD09aGFyZV92YWw6CiAgICAgICAgICAgIGJyZWFrCiAgICAgICAgbXUgKz0gMQoKICAgIGxhbWIgPSAwCiAgICBoYXJlID0gdG9ydG9pc2UKICAgIGZvciBoYXJlX3ZhbCBpbiBoYXJlOgogICAgICAgIGxhbWIgKz0gMQogICAgICAgIGlmIGhhcmVfdmFsID09IHRvcnRvaXNlX3ZhbDogI2NoZWNrIEFGVEVSIHRoZSBmaXJzdCBzdGVwIGJ5IGhhcmUKICAgICAgICAgICAgYnJlYWsKCiAgICBwcmludCgnbXU6IHswfScuZm9ybWF0KG11KSkKICAgIHByaW50KCdsYW1iOiB7MH0nLmZvcm1hdChsYW1iKSkKICAgICAKCmlmIF9fbmFtZV9fID09ICdfX21haW5fXyc6CiAgICAKICAgIGNsYXNzIGl0ZXJhYmxlKG9iamVjdCk6CiAgICAgICAgJycnRW11bGF0ZSB0aGUgb2JqZWN0IHJldHVybmluZyBpdGVyYXRvcnMgaGF2aW5nIGN5Y2xlcycnJwogICAgICAgIGRlZiBfX2luaXRfXyhzZWxmLGJlZ2lubmluZyxjeWNsaW5nKToKICAgICAgICAgICAgJycnCmJlZ2lubmluZzogdGhlIGJlZ2lubmluZyBwYXJ0IHdpdGggbm8gY3ljbGVzIChpdHMgbGVuZ3RoIGNvcnJlc3BvbmRzIHRvIG11KQpjeWNsaW5nOiB0aGUgcGFydCB3aGljaCB3aWxsIGJlIGN5Y2xpbmcgKGl0cyBsZW5ndGggY29ycmVzcG9uZHMgdG8gbGFtYikKJycnCiAgICAgICAgICAgIHNlbGYuYmVnaW5uaW5nID0gYmVnaW5uaW5nCiAgICAgICAgICAgIHNlbGYuY3ljbGluZyA9IGN5Y2xpbmcKCiAgICAgICAgZGVmIF9faXRlcl9fKHNlbGYpOgogICAgICAgICAgICByZXR1cm4gY2hhaW4oaXRlcihzZWxmLmJlZ2lubmluZyksY3ljbGUoaXRlcihzZWxmLmN5Y2xpbmcpKSkKCiAgICBkZXRlY3RfY3ljbGUoaXRlcmFibGUoJzEyMzQnLCc2NzgnKSkK