fork download
  1. # -*- coding: utf-8 -*-
  2.  
  3. from __future__ import print_function
  4. from itertools import islice, cycle, chain, izip, repeat
  5.  
  6.  
  7. class keeping_iterator(object):
  8. '''Holds the iterator and keeps the last value returned by the iterator'''
  9. def __init__(self,iterable):
  10. self.it = iter(iterable)
  11. self.val = next(self.it) #get the first value
  12.  
  13. def next(self):
  14. self.val = next(self.it)
  15. return self.val
  16.  
  17. def __iter__(self):
  18. return self
  19.  
  20.  
  21. def consume_iterator_until_values_match(iter1,iter2):
  22. '''Advance the iterators until their values match.
  23. Returs the number of iterations it took'''
  24. for n,(i1,i2) in enumerate(izip(iter1,iter2),1):
  25. if i1==i2:
  26. return n
  27.  
  28.  
  29. def detect_cycle(iterable):
  30. '''Detect a cycle in iterable object.
  31. Iterable must return an independend iterator on iter(iterable)'''
  32.  
  33. tortoise = keeping_iterator(iterable)
  34. hare = keeping_iterator(islice((iterable),None,None,2)) #two steps at a time
  35. #it will start from the first value and only then will be advancing 2 values at a time
  36.  
  37. consume_iterator_until_values_match(tortoise,hare)
  38.  
  39. hare = tortoise #put hare in the place of tortoise
  40. tortoise = keeping_iterator(iterable) #start tortoise from the very beginning
  41. mu = consume_iterator_until_values_match(chain(repeat(tortoise.val,1),tortoise),
  42. chain(repeat(hare.val,1),hare)) - 1
  43. #begin with the current val of hare.val and the value of tortoise which is in the first position
  44. # ' - 1' since the first value corresponds to mu=0
  45.  
  46. hare = tortoise
  47. tortoise_val = tortoise.val
  48. lamb = consume_iterator_until_values_match(repeat(tortoise_val),hare)
  49.  
  50. print('mu: {0}'.format(mu))
  51. print('lamb: {0}'.format(lamb))
  52.  
  53.  
  54. if __name__ == '__main__':
  55.  
  56. class iterable(object):
  57. '''Emulate the object returning iterators having cycles'''
  58. def __init__(self,beginning,cycling):
  59. '''
  60. beginning: the beginning part with no cycles (its length corresponds to mu)
  61. cycling: the part which will be cycling (its length corresponds to lamb)
  62. '''
  63. self.beginning = beginning
  64. self.cycling = cycling
  65.  
  66. def __iter__(self):
  67. return chain(iter(self.beginning),cycle(iter(self.cycling)))
  68.  
  69. detect_cycle(iterable('1234','678'))
Success #stdin #stdout 0.03s 6712KB
stdin
Standard input is empty
stdout
mu: 4
lamb: 3