This is my first question here so I accept any/all tips in case my question is not properly worded.
First off, the problem:
Say we have the following array A = [(1,'cat'), (1,'dog'), (0,'giraffe'), (0,'cheetah'),(1,'elephant')]
I need to use an algorithm that will sort this list based on the first element of each component (meaning the number). It is specifically mentioned that it does not have to be a stable algorithm (I guess meaning that we do not need to consider the sorting of the words after the number, just the 0s being before all the 1s)
I should also mention that I do not wish to employ the strategies of creating side-arrays that I will populate with the results as the algorithm sorts them. The requirement is for the algorithm to sort the array within the same array.
The code I have devised is the following:
def SwapAround(A, start, end):
start_pos = start
for i in range(start,end):
if A[i] < A[end]:
A[i], A[start_pos] = A[start_pos], A[i]
start_pos = start_pos + 1
A[start_pos], A[end] = A[end], A[start_pos]
return start_pos
A=[(1,'cat'),(0,'dog'),(1,'kangaroo'),(0,'mule'),(1,'giraffe'),(1,'dog'),
(0,'bat')]
SwapAround(A,0,len(A) - 1)
print(A)
I am having rather peculiar issues with it, although I am pretty confident that it should be working (at least walking down the code line by line it made good sense to me).
The code sorts the list, but not completely... for example, my most recent output was:
[(0, 'bat'), (0, 'dog'), (1, 'kangaroo'), (0, 'mule'), (1, 'giraffe'), (1, 'dog'), (1, 'cat')]
With a smaller list, it has actually worked, and I was dumbfounded to wake up this morning to add a couple more components to my list and it not working.
Could you possibly tell me if you can see something readily "wrong" that I should consider, and the rationale behind that so that I may fully comprehend it.
Second Question: If I wanted to add components to my list that had the number 2 as well (meaning my algorithm would have to sort between 0,1,2 instead of just 0 and 1) would you use a completely different algorithm or should swapping still work? My initial thought was that Swapping would not work properly (and it hasn't with this algorithm) and maybe a CountingSort or RadixSort would work better, so your input would be very much appreciated.
Your algorithm is conceptually wrong, though it's already close to the solution.
The important problem with your algorithm is that only the first element to swap will ever change. Instead you need to move both the index of the last and first element.
def sort_binary_key(arr):
l, u = 0, len(arr) - 1
while l < u:
if arr[l] == 1 and arr[u] == 0:
arr[l], arr[u] = arr[u], arr[l]
elif arr[l] == 1:
u -= 1
elif arr[u] == 0:
l += 1
else:
u -= 1
l += 1
For the second part of the question:
Yes, it's possible. Run the above algorithm twice. In the first run, move all elements with 0 to the lower part of the array, all other elements to the upper. Afterwards run the same algorithm over the second part of the array, sorting between elements with 1 and 2 as first tuple-member.
def sort_binary_key(arr, l, u, key_mapper):
while l < u:
al, au = key_mapper(arr[l]), key_mapper(arr[u])
if al == 1 and au == 0:
arr[l], arr[u] = arr[u], arr[l]
elif al == 1:
u -= 1
elif au == 0:
l += 1
else:
u -= 1
l += 1
return l
def sort_ternary(arr):
tmp = sort_binary_key(arr, 0, len(arr) - 1, lambda t: 0 if t == 0 else 1)
sort_binary_key(arr, tmp, len(arr) - 1, lambda t: 0 if t == 1 else 1)
Note that the above code is only intended as a demonstration. This code works with lists of integers instead of the input provided by OP.
Your algorithm could not work from a much deeper level.
You running an O(n) complexity general sorting algorithm. There is no algorithm for general sorting with that average complexity (if you find one, let me know!). Check this table of complexities of different sorting algorithms.
If your scenario is only with 0 and 1, and you want all the 0 at the start and 1 at the end (unstable), then it could be done with O(n)
(Check Paul's Answer to see how) and your algorithm is utterly different.
You can use python list sort and just pass the key:
A = [(1,'cat'), (1,'dog'), (0,'giraffe'), (0,'cheetah'),(1,'elephant')]
sorted(A, key=lambda x: x[0])
>>> [(0, 'giraffe'), (0, 'cheetah'), (1, 'cat'), (1, 'dog'), (1, 'elephant')]
You can also make it consider secondary key (first name and secondly a number):
B = [(1,'cat'), (0,'cat'), (0,'giraffe'), (0,'cheetah'),(1,'elephant')]
sorted(B, key=lambda x: (x[1], x[0]))
>>> [(0, 'cat'), (1, 'cat'), (0, 'cheetah'), (1, 'elephant'), (0, 'giraffe')]
Python has a default sorting and in your case, you could simply write:
C = [(1,'cat'), (0,'cat'), (0,'giraffe'), (0,'cheetah'),(1,'elephant')]
sorted(C)
>>> [(0, 'cat'), (1, 'cat'), (0, 'cheetah'), (1, 'elephant'), (0, 'giraffe')]
Related
Consider having an array of items that have a size and a value. I need to write a method where I end up with minimal groups/clusters where all objects in a group must have similar values (as close as possible), while the sum of all sizes is between predefined min- and max- sumSize (ideally just over the min value).
#EXAMPLE
items = {
# NAME, Value, Size
('A', 10, 3),
('B', 11, 1),
('C', 10, 7),
('D', 15, 11),
('E', 22, 5),
('F', 20, 7),
('G', 25, 1)};
minSumSize = 10;
maxSumSize = 100;
##SOLUTION: {'A','B', 'C'}, {'D'}, {'E', 'F', 'G'}
Ideally, there should be a minimal value deviation(aka group by the same value), but quite often items' size with the same value (e.g. A and C) don't sum up to the minimum size. There are no objects that are over the maximum size and items are non-splittable items. Bonus: If a group can be broken down further while still being over the minimum SumSize, then do that
What would be the most optimal approach/algorithm that I can base my code on?
I would love to get an idea which direction or method should I use. I honestly don't care in which programming language, as the logic would be the same everywhere.
What I have tried so far:
I haven't completed almost any of the methods I researched of fear I am going down a rabbit hole that is wrong, but some suggestions I found were:
1D k-means, DBscan or a variation of the 0/1 knapsack problem where the amount of knapsacks is undefined
something based graph theory? Technically, this problem can be represented as a weighted undirected graph where the nodes carry the weights and the edges carry the value difference between 2 nodes. And then, ideally, remove all unneeded edges, so you end up with a bunch of sub-graphs(groups) that overall have a minimal sum of edges and the sum of the node weight is in the size constraints.
A simpler brute-force strategy where you cluster first by value similarity using mean difference/standard difference deviation and then group by size constraints, but quite often you end up with edge cases where groups need to merge up or down, which is not ideal and lead to a pretty messy code solution with a lot of edge cases.
UPDATE:
I hope I can clear some stuff by adding a bit more context. When I created the premise, I added some of my bias and focused on grouping based on sum size. As much as that is important, it can be a bit arbitrary as the maximum sum size is 5x to 10x the minimum and each item size can be anywhere from 10% to 2x the minimum sum. I also wrote that the groups should be broken down to a minimum, but they don't have to be as long as they are in the range. My reasoning is that this would help with having similar values grouped together, but I might be wrong. They also don't need to have an equal amount of items, just items similar in value. The amount of elements and sum size don't need to be similar across groups.
On another hand, I should have also named Value -> Price, because the reason for grouping by similar value is that after all items are grouped together, a group value is chosen for the whole group by taking the highest valued item in the group, which would raise the value of all other items in the group. So in the end we want to end up with groups where the least amount of items' value is affected, especially the ones that have a size big enough to have their own group. We want to avoid having items with vastly different values in the same group, while items that have a size big enough to "have their own group" should preferably be by themselves as they are not going to be affected by this group value (there is no problem adding small-sized items with smaller value to this 1-item-group as long as they cannot be added to a closer value group). An example of this would be if Item 'B' had a size 5. This would mean that the new groups would be {'A', 'B'} and {'C'}, instead of {'A', 'B', 'C'}.
Your requirements are somewhat vague. For example it isn't clear when it is preferable to throw a small value into a group to keep it close to the minimum size. Or when it is better to have 2 groups almost exactly the minimum size if it makes a third one a bit bigger.
But the problem is clearly NP complete. It is a variant on the multiple subset sum problem with lots of subsets. And therefore even the dynamic programming pseudo-polynomial solutions are not going to work out for you.
So probably not best to try to actually BE optimal, just reasonable. And the actual bounds you gave are so wide that there almost sure to be many solutions. So for a reasonable running time I would suggest a greedy algorithm.
The general idea is that process items by value and move on to the next group when you have roughly the right size. Proceeding, as much as possible, from smallest value to largest, breaking ties with largest size to smallest. With backtracking if you run into a dead end.
Unfortunately you have a problem. Your problem is that you might need to backtrack not only past adding things to the current group, but also back into what you assigned to another group. Recursion is great for the first kind of backtrack, but not so much the second.
So instead of the implicit stack of recursion, we're going to have an explicit stack. Every member of the stack will look like (choices, choice) where choices is an array, and choice is an index of which choice was last looked at. (-1 if we have not yet processed any choices.)
And choices will look like [(action0, i0), (action1, i1), (action2, i2)]. Where i is the index of the element of interest, and action will be one of:
new: Start a new group at i
add: Add element i to the last group
skip: Skip past i to find another element to add
state: Restore some state when we unwind
By making the greedy choice first in choices we'll try to "find the obvious solution" first. But since all choices are represented, we will find a solution if there is one to be found.
And now the inside of the key loop looks like:
choices, choice = choice_stack.pop()
if -1 < choice:
unwind choices[choice]
choice += 1
if choice < len(choices):
choice_stack.append((choices, choice)) # Record we are here.
do choices[choice] # If new, we save state to choice_stack in the process
figure out next_choices
if next_choices:
choice_stack.append((next_choices, -1))
And here is complete running Python.
class Element:
def __init__(self, name, value, size):
self.name = name
self.value = value
self.size = size
def __str__(self):
return self.name
def __repr__(self):
return self.name
def find_groups (elements, min_size, max_size):
# Make sure that elements are sorted by value, then size descending.
elements = list(sorted(elements, key=lambda e: (e.value, -e.size)))
if 0 == len(elements):
return [] # Trivial.
# Set up some state.
groups = []
used = set()
size_remaining = sum((e.size for e in elements))
size_tail = size_remaining
# Aim to divide evenly between as many groups as we might make.
target_size = size_remaining / (size_remaining // min_size)
min_i = 0
i = 0
current_size = 0
choice_stack = []
choice_stack.append(
([('new', 0)], -1))
while 0 < len(choice_stack) and min_i < len(elements):
choices, choice = choice_stack.pop()
if -1 < choice:
# Need to undo the last choice taken.
action, i = choices[choice]
if action == 'state':
# We cheat and hide the state in i
size_remaining, target_size, current_size = i
elif action == 'new':
min_i = i
groups.pop()
elif action == 'add':
used.remove(i)
if i < min_i:
min_i = i
groups[-1].pop()
current_size -= elements[i].size
size_remaining += elements[i].size
size_tail += elements[i].size
elif action == 'skip':
size_tail += elements[i].size
else:
raise Exception(f"Invalid action {action} while unwinding")
choice += 1
if choice < len(choices):
# Record that we are trying this choice
choice_stack.append((choices, choice))
# Actually do it.
action, i = choices[choice]
# We need to do special processing for new.
if action == 'new':
# Save state for rewind.
state = [size_remaining, target_size, current_size]
# Setting 0 here means we will rewind if we hit this.
choice_stack.append(([('state', state)], 0))
size_tail = size_remaining
groups.append([])
current_size = 0
# Figure out the target group and feasibility.
target_count = size_remaining // min_size
if 0 < target_count:
# We fail to get here if the last group is too small.
target_size = size_remaining / target_count
if max_size < target_size:
# We can't process anything.
continue
if action == "new":
pass # We aren't going to actually do anything.
elif action == "add":
used.add(i)
groups[-1].append(elements[i])
current_size += elements[i].size
size_remaining -= elements[i].size
size_tail -= elements[i].size
if min_i == i:
min_i += 1
i += 1
elif action == "skip":
size_tail -= elements[i].size
i += 1
else:
raise Exception(f"Invalid action {action} in processing element {i}")
# Skip past any previously used elements.
while i in used:
if min_i == i:
min_i += 1
i += 1
# And now what is our next action?
next_choices = []
can_new = (action == 'add' and min_size <= current_size and current_size <= max_size)
if i == len(elements):
# Special case.
if can_new:
next_choices = [('new', min_i)]
else:
can_add = (current_size + elements[i].size <= max_size and min_size <= current_size + size_tail)
prefer_add = (current_size + elements[i].size <= target_size)
if can_new:
if prefer_add:
next_choices = [('add', i), ('new', min_i)]
else:
next_choices = [('new', min_i), ('add', i)]
elif can_add:
next_choices = [('add', i)]
# Skipping creates a value gap so is ALWAYS a last choice.
can_skip = (action != 'new' and min_size <= current_size + size_tail - elements[i].size)
if can_skip:
next_choices.append(('skip', i))
if 0 < len(next_choices):
choice_stack.append((next_choices, -1))
if 0 == len(groups):
return None
else:
return groups
items = [
Element('A', 10, 3),
Element('B', 11, 1),
Element('C', 10, 7),
Element('D', 15, 11),
Element('E', 22, 5),
Element('F', 20, 7),
Element('G', 25, 1),
]
print(find_groups(items, 10, 100))
print(find_groups(items, 9, 12))
print(find_groups(items, 9, 11))
And you can see in the last two that it tries to be greedy, but if it needs to will backtrack. And will always find a solution if one exists. (Though backtracking can take exponentially long.)
Is there a way to continue calling a function only if a condition is met without using loops/recursion/comprehension? I can only use map and filter.
I am bubble sorting a list and I need to reiterate until there is one whole pass without any swap. I use a counter for this.
This is what I have so far:
def sort(l, i, cnt):
if i < len(l) - 1 and l[i] > l[i+1]:
l[i], l[i+1] = l[i+1], l[i]
cnt += 1
return l[i]
def main(l):
cnt = 0
l = list(map(lambda i: sort(l, i, cnt), range(len(l))))
I'm not sure how to continue calling sort only if cnt != 0. Any help is appreciated.
It is un uncommon requirement, but it is possible, provided you are allowed to use other functions to actually execute the iterators. Because both map and filter both only return iterators, so you will have to use sum, list, or tuple for example to actually cause the iterators to return their values.
Here I would use a function that compares 2 consecutive elements in a list, returns 0 in they are in increasing order and swap them and returns 1 if they are not. Using sum on a map on this function will return the number of swaps on a pass:
def sort2(l, i):
if (l[i] > l[i+1]):
l[i], l[i+1] = l[i+1], l[i]
return 1
return 0
You can the execute a pass with:
sum(map(lambda i: sort2(l, i), range(len(l) -1)))
And you execute a full bubble sort by using a second map for all the passes:
sum(map(lambda j: sum(map(lambda i: sort2(l, i), range(len(l) -j))), range(1, len(l))))
In order to stop as soon as one pass resulted in 0 swap, I would filter with a function raising a StopIteration, when it gets 0, because if we use sum, a pass returns 0 as soon as the list is sorted, and the StopIteration will gently stops the iterators:
def stop(x):
# print(x) # uncomment to control the actual calls
if x == 0:
raise StopIteration
return True
Let us combine everything:
tuple(filter(stop, map(lambda j: sum(map(lambda i: sort2(l, i), range(len(l) -j))
), range(1, len(l)))))
Demo:
With l = [1, 3, 5, 2, 4], this gives (with the print uncommented in stop):
2
1
0
(2, 1)
So we have correctly got:
first pass caused 2 swaps ((5,2) and (5,4))
second pass caused 1 swap ((3,2))
third pass caused 0 swap and the stop filter actually stopped the iterators.
That being said, it is a nice exercise, but I would never do that in real world programs...
You might want to check itertools.takewhile; it seems like it is the one you are looking for. See the documentation for itertools for details.
takewhile(lambda x: x<5, [1,4,6,4,1])
# --> 1 4
According to the comments, your goal is to sort a list of numbers between 1 and 50 using map and filter without loops, comprehensions or recursion. Therefore your actual question (about a do/while equivalent for implementing bubble sort) is an XY problem because the correct solution is not to use bubble sort at all. When asking questions in future, please say what your actual problem is - don't just ask how to fix your attempted solution.
The counting sort algorithm is suitable here; something akin to counting sort can be implemented using map and filter without side-effects:
def counting_sort(numbers):
min_x, max_x = min(numbers), max(numbers)
r_x = range(min_x, max_x + 1)
r_i = range(len(numbers))
count_ge = list(map(sum, map(lambda x: map(x.__ge__, numbers), r_x)))
return list(map(lambda i: min_x + sum(map(i.__ge__, count_ge)), r_i))
Note that this is a very inefficient variant of counting sort; it runs in O(nk) time instead of O(n + k) time. But it meets the requirements.
As part of a dynamical programming assignment, I find myself having to do the following.
I have two sorted lists of length 2 tuples (ordered pairs, representing scores on two criteria). One pair of values can only be considered strictly greater than another if it is greater on one criterion and not lower on the other. So, (1,8) and (2,7) are incomparable, while (1,7) is lower than (2,8).
Each input list contains only values that are incomparable with each other. My method merges the two lists, omitting duplicates as well as any values that are strictly inferior to another value in the new, bigger list. Then it sorts the new list.
For example, the following input produces this result:
combine([(1,8), (2, 6), (3, 4)], [(2, 7), (3, 3)])
[(1, 8), (2, 7), (3, 4)]
Here's the code I have currently produced:
def combine(left, right):
# start with lists sorted from biggest to smallest
newlist = left+right
leftlen = len(left)
for i in range(leftlen - 1, -1, -1):
l = newlist[i] # candidate value to be inserted in
for j in range(len(newlist) - 1, leftlen - 1, -1):
r = newlist[j]
if r[0] >= l[0]: # cell with >= food encountered without having previously encountered cell with less water
if r[1] >= l[1]: # this cell also has more water - del candidate
del newlist[i]
leftlen -=1
elif r[0] == l[0]: # equal food and less water than candidate - candidate wins
del newlist[j]
break # either way, no need to consider further cells -
# if this cell doesn't beat candidate, then candidate belongs
if r[1] <= l[1]: # cell with less water encountered before cell with more food
del newlist[j]
for k in range(j -1, leftlen - 1, -1): # continue through right list, deleting cells until a value with
# higher food is found
r = newlist[k]
if r[0] > l[0]: break
else: del newlist[k]
break
newlist.sort(reverse=True)
return newlist
This code does work, but I am wondering if there is a faster approach to solving this kind of problem? When the lists are long, I end up making a lot of pairwise comparisons.
I've tried to prune out some unnecessary comparisons, relying upon the fact that items in each list are always greater on one criterion and lesser on the other. Thus, the lists are reverse sorted on the first value in the tuple, and therefore also sorted on the second value!
One idea I had was to try and use a different ADT - some type of tree perhaps, but I'm not sure if this is going to help or not.
Any suggestions? This is for an assignment, so I'm looking for ideas rather than for somebody to rewrite the whole thing for me :) Cheers!
Essentially what I'm trying to do is write the enumerate function recursively. I'm fully aware that doing this recursively is not optimal and that there is already a built-in enumerate function in python.
def enumeration(mylist, idx=0):
if idx == 0:
return [(idx, mylist[idx]), enumeration(mylist, idx+1)]
elif idx != len(mylist)-1:
return (idx, mylist[idx]), enumeration(mylist, idx+1)
else:
return idx, mylist[idx]
Now it's not the enumeration part that's difficult. It's getting the tuples to match up where they should be. If mylist was set to ["The", "tree", "then"], the output would be "[(0, "the"), ((1, "tree"), (2, "then"))] when I want it to look like [(0, "the"), (1, "tree"), (2, "then")].
I know what is causing the problem but I have no idea how to fix it. I've tried using '+' instead of ',' in the return statements but that just leads to [(0, "the", 1, "tree", 2, "then")] which I also don't want.
The problem is that the return type of your enumeration is not consistent:
if the index is 0 it returns a list[pair, enumeration()]
if the index is neither 0 nor end-of-list, it returns a Pair[Pair, enumeration]
if the index is end-of-list, it returns a simple pair
You need your various cases to be consistently typed in such a way that you can "recombine" your items. Since enumeration() should return a sequence, logically each recursive case needs to return a list (of pairs).
Then you need to do a recursive case analysis. Generally, recursion consists of two items:
how do you decide to end the recursion and what do you do in that case
what do you do in the normal case
Here the terminal case is pretty simple: if you're past the end of the list, just return an empty list. Incidentally this is something you're not handling at all: try calling enumerate([]) and see what happens.
Then the normal recursive case: just generate a pair of (index, item), call enumerate() with an incremented index, and concatenate the two.
def enumerate(mylist, idx=0):
# note: terminal case is really `idx == len(mylist)`, you may want to generate an error if `idx > len(mylist)` instead as that's an incorrect use of the API
if idx >= len(mylist):
return []
else:
return [(idx, mylist[idx])] + enumerate(mylist, idx+1)
Other than the behaviour with empty lists, your version also uses idx completely differently: in the original enumerate it's just the index that's returned, it's not used to manipulate the input (indeed the input is not necessarily indexable). So e.g. enumerate(range(3), 5) would yield (5, 0), (6, 1), (7, 2). Your version would either error out or end, because it would try to access the 5th item of a range(3).
this will work
def enumeration(mylist, idx=0):
if idx == 0:
return [(idx, mylist[idx])]+(enumeration(mylist, idx+1))
elif idx < len(mylist)-1:
return [(idx, mylist[idx])]+(enumeration(mylist, idx+1))
else:
return [(idx, mylist[idx])]
output:
mylist=["asd","gsd","fff","adf"]
enumeration(mylist, idx=0)
[(0, 'asd'), (1, 'gsd'), (2, 'fff'), (3, 'adf')]
First of use a list to hold the values.
Second instead of calling the next recursion with a , use + to add to the list of values , this way each tuple gets added to the same list.
I am curious to find out a function to check if a given list is periodic or not and return the periodic elements. lists are not loaded rather their elements are generated and added on the fly, if this note will make the algorithm easier anyhow.
For example, if the input to the function is [1,2,1,2,1,2,1,2], the output shall be (1,2).
I am looking for some tips and hints on the easier methods to achieve this.
Thanks in advance,
This problem can be solved with the Knuth-Morris-Pratt algorithm for string matching. Please get familiar with the way the fail-links are calculated before you proceed.
Lets consider the list as something like a sequence of values (like a String). Let the size of the list/sequence is n.
Then, you can:
Find the length of the longest proper prefix of your list which is also a suffix. Let the length of the longest proper prefix suffix be len.
If n is divisible by n - len, then the list is periodic and the period is of size len. In this case you can print the first len values.
More info:
GeeksForGeeks article.
Knuth-Morris-Pratt algorithm
NOTE: the original question had python and python-3.x tags, they were edited not by OP, that's why my answer is in python.
I use itertools.cycle and zip to determine if the list is k-periodic for a given k, then just iterate all possible k values (up to half the length of the list).
try this:
from itertools import cycle
def is_k_periodic(lst, k):
if len(lst) < k // 2: # we want the returned part to repaet at least twice... otherwise every list is periodic (1 period of its full self)
return False
return all(x == y for x, y in zip(lst, cycle(lst[:k])))
def is_periodic(lst):
for k in range(1, (len(lst) // 2) + 1):
if is_k_periodic(lst, k):
return tuple(lst[:k])
return None
print(is_periodic([1, 2, 1, 2, 1, 2, 1, 2]))
Output:
(1, 2)
Thank you all for answering my question. Neverthelss, I came up with an implementation that suits my needs.
I will share it here with you looking forward your inputs to optimize it for better performance.
The algorithm is:
assume the input list is periodic.
initialize a pattern list.
go over the list up to its half, for each element i in this first half:
add the element to the pattern list.
check if the pattern is matched throughout the list.
if it matches, declare success and return the pattern list.
else break and start the loop again adding the next element to the pattern list.
If a pattern list is found, check the last k elements of the list where k is len(list) - len(list) modulo the length of the pattern list, if so, return the pattern list, else declare failure.
The code in python:
def check_pattern(nums):
p = []
i = 0
pattern = True
while i < len(nums)//2:
p.append(nums[i])
for j in range(0, len(nums)-(len(nums) % len(p)), len(p)):
if nums[j:j+len(p)] != p:
pattern = False
break
else:
pattern = True
# print(nums[-(len(nums) % len(p)):], p[:(len(nums) % len(p))])
if pattern and nums[-(len(nums) % len(p)) if (len(nums) % len(p)) > 0 else -len(p):] ==\
p[:(len(nums) % len(p)) if (len(nums) % len(p)) > 0 else len(p)]:
return p
i += 1
return 0
This algorithm might be inefficient in terms of performance but it checks the list even if the last elements did not form a complete period.
Any hints or suggestions are highly appreciated.
Thanks in advance,,,
Let L the list. Classic method is: use your favorite algorithm to search the second occurence of the sublist L in the list L+L. If the list is present at index k, then the period is L[:k]:
L L
1 2 1 2 1 2 1 2 | 1 2 1 2 1 2 1 2
1 2 1 2 1 2 1 2
(This is conceptually identical to #KonstantinYovkov's answer). In Python: example with strings (because Python has no builtin sublist search method):
>>> L = "12121212"
>>> k = (L+L).find(L, 1) # skip the first occurrence
>>> L[:k]
'12'
But:
>>> L = "12121"
>>> k = (L+L).find(L, 1)
>>> L[:k] # k is None => return the whole list
'12121'