Home > database >  How to stop recursion when an answer is found in Python?
How to stop recursion when an answer is found in Python?

Time:12-10

I'm trying to build a simple contact tracing system in Python. The following function should get 2 persons in my database and return if they've had (in)direct contact, and if indirect it returns the amount of people in between them.

contact_tracing_datastructure = [{'Koen', 'Pieter'}, {'Kim', 'Koen', 'Bart'}, {'Tom', 'Pieter'}, {'Yana', 'Bart'}, {'Wouter', 'Yana'}, {'Wouter', 'Bert'}, {'Koen'}]

already_checked = set()

def x_had_contact_with_y_with_distance(meetings, person_x, person_y, distance=0):
    contacts_x = give_contacts(meetings, person_x)
    already_checked.add(person_x)
    if person_y in contacts_x:
        return True, distance
    elif len(already_checked) != len(population):
        distance  = 1
        for i in contacts_x:
            found = x_had_contact_with_y_with_distance(meetings, i, person_y, distance)
            if found:
                return True, distance
    else:
        return False

print(x_had_contact_with_y_with_distance(contact_tracing_datastructure, "Koen", "Wouter")

The function keeps going however until maximum recursion depht is reached, even though I've added the already_checked variable to make it so it doesn't check a person twice. The function should return (True, 2) in this case. Give_contacts(person_x) simply gives a set of people person_x has been in contact with.
How can i stop the recursion when a solution has been found? Thanks in advance!

CodePudding user response:

reshape

First I would reshape your data from list(set(str)) to dict(str,set(str)) -

# list of set of string
input = [{'Koen', 'Pieter'}, {'Kim', 'Koen', 'Bart'}, {'Tom', 'Pieter'}, {'Yana', 'Bart'}, {'Wouter', 'Yana'}, {'Wouter', 'Bert'}, {'Koen'}]

# dict of (string -> set of string)
index = dict()
for s in input:
  for name in s:
    if name in index:
      index[name] = index[name] | s - {name}
    else:
      index[name] = s - {name}
for (name, others) in index.items():
  print(name, others)
Pieter {'Koen', 'Tom'}
Koen {'Pieter', 'Bart', 'Kim'}
Kim {'Koen', 'Bart'}
Bart {'Yana', 'Koen', 'Kim'}
Tom {'Pieter'}
Yana {'Bart', 'Wouter'}
Wouter {'Yana', 'Bert'}
Bert {'Wouter'}

graph

The superior index shape allows you to easily write graph(t, q) -

def graph(t, q):
  def loop(r, q):
    yield r
    for name in t[q]:
      if name not in r:
        yield from loop((*r, name), name)
  return loop((q,), q)
for line in graph(index, "Koen"):
  print(line)
('Koen',)
('Koen', 'Bart')
('Koen', 'Bart', 'Kim')
('Koen', 'Bart', 'Yana')
('Koen', 'Bart', 'Yana', 'Wouter')
('Koen', 'Bart', 'Yana', 'Wouter', 'Bert')
('Koen', 'Pieter')
('Koen', 'Pieter', 'Tom')
('Koen', 'Kim')
('Koen', 'Kim', 'Bart')
('Koen', 'Kim', 'Bart', 'Yana')
('Koen', 'Kim', 'Bart', 'Yana', 'Wouter')
('Koen', 'Kim', 'Bart', 'Yana', 'Wouter', 'Bert')

trace

Utilizing graph we can easily write trace(t, q, p) -

def trace(t, p, q):
  for line in graph(t, p):
    if line[-1] == q:
      yield line

Let's see the ways Koen traces to Kim -

for contact in trace(index, "Koen", "Kim"):
  print(len(contact) - 1, "->".join(contact))
2 Koen->Bart->Kim
1 Koen->Kim

And how Bert traces to Kim -

for contact in trace(index, "Bert", "Kim"):
  print(len(contact) - 1, "->".join(contact))
5 Bert->Wouter->Yana->Bart->Koen->Kim
4 Bert->Wouter->Yana->Bart->Kim

And how Yana traces Wouter -

for contact in trace(index, "Yana", "Wouter"):
  print(len(contact) - 1, "->".join(contact))
1 Yana->Wouter

resilience

Modify graph so that any names can be searched, even names that don't appear in the graph -

def graph(t, q):
  def loop(r, q):
    try:                                       # <- try
      yield r
      for name in t[q]:
        if name not in r:
          yield from loop((*r, name), name)
    except KeyError:                           # <- name not found
      return                                   # <- stop iteration
  return loop((q,), q)
for contact in trace(index, "Vivian", "Sylvia"):
  print(len(contact) - 1, "->".join(contact))
(no output)
  • Related