3

What is the most efficient way in python for picking multiple n consecutive integers from n consecutive list, picking up one integer from each list. Here n is quite large..say in the order of 100s.

L1 = [5,3,2,7,1]
L2 = [3,5,6,8,9,21,2]
L3 = [5,3,6,7,3,9]

I'd like to print out the ranges of consecutive integers from consecutive lists, where first element is picked up from first list, second element from second list and so on and so forth:

Candidate solution [5,6,7], [1,2,3], [7,8,9]
rombi
  • 199
  • 3
  • 22
  • So you want the largest consecutive range inside every list, but without respecting the current ordering? Can there be multiple ranges of equal length in one list? If so, how to handle them? – Byte Commander May 02 '16 at 06:54
  • @ByteCommander No No No. You got it all wrong. I don't want largest continuous range inside every list. I want the consecutive elements picked up from consecutive list, not within a list. Hope i made myself clear. – rombi May 02 '16 at 06:59
  • Ah, so you have 3 lists and form consecutive ranges of length 3 by picking one number from each list? And is the number of lists always 3 or can it vary? – Byte Commander May 02 '16 at 07:03
  • @AKS i have tried sorting each list and then picking every element in each list and checking whether they are consecutive integers, but the worst case complexity for that can be O(l1*l2*l3), where l1,l2 and l3 are the length of the list L1, L2, L3. If i have 100 such list then the performance will suffer. – rombi May 02 '16 at 07:03
  • @ByteCommander You got it right . Number of list can vary and is generally in the range of 100's . – rombi May 02 '16 at 07:05
  • I think a faster way would be to make a sole list from the ones you have, indicating where the numbers are coming from (for instance with tuples (number, index of the list of origin)), and then sort that list and look for consecutive elements that satisfy the condition you want. It should be in O(n*log(n)) using merg sort for instance, where n is the sum of all length of l1, l2, etc. – zaphodef May 02 '16 at 07:23

4 Answers4

1

I'd try sorting the lists first, using an efficient sorting algorithm. You can try Bubble Sort but other sorting algorithms might work as well.

You can then run through the integer from 1-n and look for continuous ranges of integer in the string. This way you have a maximum of n² operations for the sorting and n operations for the "finding".

Is this fast enough for you?

Harper
  • 1,073
  • 1
  • 10
  • 23
1
L1 = [5,3,2,7,1]
L2 = [3,5,6,8,9,21,2]
L3 = [5,3,6,7,3,9]
cons_l = []
L = [L2] + [L3] #+[L4] #+ ...+ ..... ### Add any number of list here..

j = 0
for l1 in L1:
   cons_l.append([])
   cons_l[j].append(l1)
   for l in range(0, len(L)):
      if l1+l+1 in L[l]:
         cons_l[j].append(l1+l+1)
      else:
         del cons_l[j]
         j -= 1
         break
   j += 1
print cons_l
SuperNova
  • 25,512
  • 7
  • 93
  • 64
  • If suppose instead of 3 list, I have 100 list..then how will you scale your algorithm .? – rombi May 02 '16 at 07:29
  • 1
    @rombi, Please take a look at the code. I have made some changes to provide solution for the dynamic list(upto n). – SuperNova May 02 '16 at 08:19
0

Maybe using sets will be fast enough for your application? Is a bit brute force-y, but if I understood correctly, it is compliant with your restrictions:

lists = [
  [5,3,2,7,1],
  [3,5,6,8,9,21,2],
  [5,3,6,7,3,9],
]

candidates = list()

# Without the first one
rest_of_sets = [set(l) for l in lists[1:]]

for fe in lists[0]:
    skip_partial = False
    for i, s in enumerate(rest_of_sets, 1):
        if fe + i not in s:
            skip_partial = True
            break
    if not skip_partial:
        candidates.append(range(fe, fe+len(sets)))

print candidates
MariusSiuram
  • 3,380
  • 1
  • 21
  • 40
0

You can use list comprehensions:

In [23]: ls = [[5,3,2,7,1],[3,5,6,8,9,21,2],[5,3,6,7,3,9],]

In [24]: l = len(ls)

In [25]: [list(range(s,s+l)) for s in ls[0] if all(i in l for i,l in zip(range(s+1,s+l),ls[1:]))]
Out[25]: [[5, 6, 7], [7, 8, 9], [1, 2, 3]]

The gust of it is, for every number in the first list generate a sequence of incremented numbers and check if each one is contained in the corresponding list in the sequence of remaining lists.

Note that all stops iterating over the generator expression as soon as a condition is not satisfied, improving the efficiency of the method.

For a LARGE instance of the problem, it may be worth to convert all the lists to sets before the list comprehension, ls = [set(l) for l in ls]


Addendum

A variation w/o list comprehension using a for loop and a conditional statement, note that the inner lists were converted to sets before the search for sequences.

ls = [[5, 3, 2, 7, 1], [3, 5, 6, 8, 9, 21, 2], [5, 3, 6, 7, 3, 9]]
l = len(ls)
ls = [set(li) for li in ls]

candidates = []
for n in ls[0]:
    if all(i in l for i, l in zip(range(n+1, n+l), ls[1:])):
        candidates.append(list(range(n, n+l)))
gboffi
  • 22,939
  • 8
  • 54
  • 85