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 iterator_keeping_value(object):
  8. '''Holds the iterator and keeps the last val returned by the iterator'''
  9. def __init__(self,iterable):
  10. self.it = iter(iterable)
  11. self.val = next(self.it) #get the first value on initializing
  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 detect_cycle(iterable):
  22. '''Detect a cycle in iterable object.
  23. Iterable must return an independend iterator on iter(iterable)'''
  24.  
  25. tortoise = iterator_keeping_value(iterable)
  26. hare = iterator_keeping_value(islice(iter(iterable),None,None,2)) #two steps at a time
  27.  
  28. for tortoise_val, hare_val in izip(tortoise,hare):
  29. if tortoise_val==hare_val:
  30. break
  31.  
  32. hare = tortoise #put hare in the place of tortoise
  33. tortoise = iterator_keeping_value(iterable) #start tortoise from the very beginning
  34. mu = 0
  35. for tortoise_val, hare_val in chain(repeat((tortoise.val, hare.val),1),izip(tortoise,hare)):
  36. #begin with the current val of hare.val and the value of tortoise which is in the first position
  37. if tortoise_val==hare_val:
  38. break
  39. mu += 1
  40.  
  41. lamb = 0
  42. tortoise_val = tortoise.val #save the value of tortoise since the object is reassigned to hare
  43. hare = tortoise
  44. for hare_val in hare:
  45. lamb += 1
  46. if hare_val == tortoise_val: #check AFTER the first step by hare
  47. break
  48.  
  49. print('mu: {0}'.format(mu))
  50. print('lamb: {0}'.format(lamb))
  51.  
  52.  
  53. if __name__ == '__main__':
  54.  
  55. class iterable(object):
  56. '''Emulate the object returning iterators having cycles'''
  57. def __init__(self,beginning,cycling):
  58. '''
  59. beginning: the beginning part with no cycles (its length corresponds to mu)
  60. cycling: the part which will be cycling (its length corresponds to lamb)
  61. '''
  62. self.beginning = beginning
  63. self.cycling = cycling
  64.  
  65. def __iter__(self):
  66. return chain(iter(self.beginning),cycle(iter(self.cycling)))
  67.  
  68. detect_cycle(iterable('1234','678'))
  69.  
Success #stdin #stdout 0.03s 6712KB
stdin
Standard input is empty
stdout
mu: 4
lamb: 3