Comb Sorting Program - python

I am trying to write a program in python which should sort a list within a list.
Examples -
List before sorting: [[2, 1], [2, 2], [3, 3], [3, 1], [1, 1], [1, 2]]
List after sorting: [[1, 1], [1, 2], [2, 1], [2, 2], [3, 1], [3, 3]]
List before sorting: [[3, 3], [2, 2], [1, 2], [2, 1], [3, 1], [1, 1]]
List after sorting: [[1, 1], [2, 1], [1, 2], [2, 2], [3, 1], [3, 3]]
List before sorting: [[1, 1], [3, 3], [2, 1], [2, 2], [1, 2], [3, 1]]
List after sorting: [[1, 1], [1, 2], [2, 1], [2, 2], [3, 1], [3, 3]]
My code:
import math
def combsort(list1):
gap = len(list1)
shrink = 1.3
sorted = False
while sorted == False:
gap = gap/shrink
if gap > 1:
sorted = False
else:
gap = 1
sorted = True
i = 0
while i + gap < gap:
distance1 = math.sqrt(list1[i[0]]**2 + list1[i[1]]**2)
distance2 = math.sqrt(list1[i+gap[0]]**2 + list1[i+gap[1]]**2)
if distance1 > distance2:
temporary = list1[i]
list1[i] = list1[i + gap]
temporary = list1[i + gap]
sorted = False
i = i + 1
list1 = [[2, 1], [2, 2], [3, 3], [3, 1], [1, 1], [1, 2]]
combsort(list1)
print(list1)
My code doesn't work and prints out the exact same list. Any help?
This is what I was given to follow:
Comb sort is a variation of bubble sort that generally performs more efficient sorting. It accomplishes this by moving low values near the end of the list further toward the front of the list than bubble sort would during early iterations.
implement a function called combsort that does the following:
Takes as input a 2D list that contains information representing x/y points in 2D space. Each item in the list will be a list with 2
items an x and a y coordinate. For example, the list could be [ [0,
1],[2, 1], [3, 3], [1, 1], … ]
List item Performs an in-place sort (i.e., does not create a new list, but modifies the original) using the comb sort algorithm that
sorts the 2D list such that points with lower 2 Euclidean distance to
the origin (0, 0) appear earlier in the list. In this case, you are
comparing distances instead of directly comparing list values – it may
be useful to implement and use a distance calculation function. Note –
the Euclidean distance of a point (x, y) from the origin (0, 0) can be
calculated with the following equation: distance(x,y) = �! + �!
Does not return a value. As the input list is sorted in place, it will be modified directly and these modifications will be reflected
outside the function, so a return value is not needed.

The while loop never gets executed because its condition cannot be true:
while i + gap < gap:
Hint: use more print statements or a debugger to check the values of things while the program is running.

The error is in your while statement, it should be:
while i + gap < len(list1):
as shown in the pseudocode here. Currently the while loop is never even entered... which hides your other problems (see below).
You are indexing into a 2-D list wrong:
list1[i[0]] #get first item in list `i` and use that number to index into list1
list1[i][0] #get the ith sublist in list1 and get its first item
You need to make sure you use integers for your list indices:
gap = int(gap/shrink)
And for the grand finale....you swapped the order of your assignment, ruining your swap logic
temporary = list1[i + gap] #wrong
list1[i + gap] = temporary #should be this
You can also do this without the use of temporary in Python:
list1[i+gap], list1[i] = list1[i], list1[i+gap]
After all these changes, the code works as expected:
>>>combsort([[3, 3], [2, 2], [1, 2], [2, 1], [3, 1], [1, 1]])
[[1, 1], [2, 1], [1, 2], [2, 2], [3, 1], [3, 3]]

Related

Order a nested list based on custom condition

I have a list of lists:
[[1, 0], [2, 1], [5, 4], [1, 3], [4, 1], [3, 2], [0, <NA>]]
The list having <NA> as second element will always be the first list in nested list.
In the two subsequent lists, the first element of first list should match with second element of second list. e.g.: [0, <NA>], [1, 0], [2, 1]
The resultant list should cover all the elements from original list.
Expected output:
[[0, <NA>], [1, 0], [2, 1], [3, 2], [1, 3], [4, 1], [5, 4]]
Here, after [1, 0], we could have gone to [4, 1] as well; but that would be wrong since we won't be able to cover all the elements in the original list. I am using Python as programming language here. Any help would be appreciated. Please and thanks.
(Swapping your <NA> for a None) this looks for the longest path through the list that visits all elements exactly once.
def sort_path(elements):
def f(prefix, seq):
# get the current element to match
curr = prefix[-1][0] if len(prefix) > 0 else None
# get possible next nodes in path
next = [x for x in seq if x[1] == curr]
# get candidate paths from each next node
candidates = [f(prefix + [n], [x for x in seq if x != n]) for n in next]
# return the longest path from the candidates (or the prefix if no candidates)
return prefix if len(candidates) == 0 else max(candidates, key=len)
result = f([], elements)
return result if len(result) == len(elements) else None
input = [[1, 0], [2, 1], [5, 4], [1, 3], [4, 1], [3, 2], [0, None]]
print(sort_path(input))
# gives: [[0, None], [1, 0], [2, 1], [3, 2], [1, 3], [4, 1], [5, 4]]
This code produces your expected output using recursion.
your_list = [[1, 0], [2, 1], [5, 4], [1, 3], [4, 1], [3, 2], [0, '<NA>']]
first_item = [x for x in your_list if x[1] == '<NA>'][0] # Assuming only one of '<NA>' exists
remaining_list = your_list.copy()
remaining_list.remove(first_item)
def get_custom_order(remaining_list, ordered_list):
work_copy = remaining_list.copy()
start_value = ordered_list[-1][0]
for item in remaining_list:
if item[1] == start_value:
ordered_list.append(item)
work_copy.remove(item)
get_custom_order(work_copy, ordered_list)
break
return ordered_list
ordered_list = get_custom_order(remaining_list, [first_item])
print(ordered_list)
However, my answer is incomplete. This code only works because of the sorting of your list. It does not fulfill your requirement to cover sorting of all elements. I'll try to fix that and update my answer.

How to find a list of all possible decompositions of a list into chunks of size bigger than 2

For a given N, I have a list of all numbers from 0 to N-1
A = list(range(0,N));
and I want to find a list of all possible decompositions into lists of sizes two or higher, without repetitions. For example, for N=4 I have
A = [0,1,2,3];
and the output I want is
OUT = [[[0, 1, 2, 3]], [[0, 1], [2, 3]], [[0, 2], [1, 3]], [[0, 3], [1, 2]]];
Up to N=5, the decomposition of the initial list into only two pieces (of length 2 and 3) makes the problem very easy. However, I can't find a way to do it for higher N, since the lists of length four must be further split into two lists of length 2.
Does anyone have any suggestions on how to solve this? I feel there must be a straightforward recursive trick to do this, but I have been trying for a day now, and I am a little stuck!
Thanks!
PS: the results for N smaller than 6 are:
N=1) OUT = [[[0]]];
N=2) OUT = [[[0, 1]]];
N=3) OUT = [[[0, 1, 2]]];
N=4) OUT = [[[0, 1, 2, 3]], [[0, 1], [2, 3]], [[0, 2], [1, 3]], [[0, 3], [1, 2]]];
N=5) OUT = [[[0, 1, 2, 3, 4]], [[0, 1], [2, 3, 4]], [[0, 2], [1, 3, 4]], [[0, 3], [1, 2, 4]], [[0, 4], [1, 2, 3]], [[1, 2], [0, 3, 4]], [[1, 3], [0, 2, 4]], [[1, 4], [0, 2, 3]], [[2, 3], [0, 1, 4]], [[2, 4], [0, 1, 3]], [[3, 4], [0, 1, 2]]];
PPS: I am a physicist and haven't been programming for a while; the code probably looks terrible, and the problem might be very easy... sorry for that!
Consider the last item i.e N-1, suppose we have two sets of combinations. one for list(range(0,N-1)) and one for list(range(0,N-2)). Now, if you want to put the last item in these combinations, you should have a different approach for each one of them, which I've explained below:
Combinations of list(range(0, N-1)): To put the last item in these combinations, you have no choice other than to put it in one of the sets that are already available to understand this better consider that you have all combinations for four and now you want to add 5 to this combination. So you have :
[[[0, 1, 2, 3]], [[0, 1], [2, 3]], [[0, 2], [1, 3]], [[0, 3], [1, 2]]]
Adding last item( i.e 4) to these combination would get us something like bellow:
[[[0, 1, 2, 3, 4]], [[0, 1, 4], [2, 3]], [[0, 1], [2, 3, 4]], [[0, 2, 4], [1, 3]], [[0, 2], [1, 3, 4]], [[0, 3, 4], [1, 2]], [[0, 3], [1, 2, 4]]]
We put 4 in every combination and make new combinations out of them. So for this part, we need a recursive call for N-1. As you can see in this situation, each sets that N-1 is in has at least three items. That's because we expanded our set, which already existed and had at least two items.
Combinations of N-2 items: I've considered this for when N-1 is in a set with only two items. To find these combinations, we need to select one of the rest N-1 items and consider that selected item with item N-1 as one set and find every other combination for the rest of N-2 items. To give you an example again, consider N = 5 so the last item is 4. If we want to pair the last item with 3, we could build all combinations for (0, 1, 2) and put pair of the (3, 4) to the mix. It would be something like this for N=5:
[[[0 , 1 , 2] , [3 , 4]] , [[0 , 1 , 3] , [2 , 4]] , [[0 , 2 , 3] , [1 , 4]] , [[ 1 , 2 , 3] , [0 , 4]]]
I've implemented this using recursive functions in python. I'm not a python developer, so there may be some enhancements in implementations, but the algorithm is working fine:
import copy
def recursive_builder(arr):
if len(arr) == 3:
return [[[arr[0], arr[1], arr[2]]]]
if len(arr) == 4:
return [[[arr[0], arr[1], arr[2], arr[3]]], [[arr[0], arr[1]], [arr[2], arr[3]]], [[arr[0], arr[2]], [arr[1], arr[3]]], [[arr[0], arr[3]], [arr[1], arr[2]]]]
temp_array = arr[0:len(arr)-1]
recursive_builder_one_step_before = recursive_builder(temp_array)
new_from_one_step_before = []
last_item = arr[len(arr)-1]
for item in recursive_builder_one_step_before:
for i in range(0 , len(item)):
temp_item = copy.deepcopy(item)
temp_item[i].append(last_item)
new_from_one_step_before.append(temp_item)
new_from_two_step_before = []
for i in range(0 , len(temp_array)):
new_arr = temp_array[:i] + temp_array[i+1 :]
recursive_builder_two_step_before = recursive_builder(new_arr)
new_from_two_step_before_inner = []
for item in recursive_builder_two_step_before:
new_item = item + [[temp_array[i] , last_item]]
new_from_two_step_before_inner.append(new_item)
new_from_two_step_before = new_from_two_step_before + new_from_two_step_before_inner
return new_from_two_step_before + new_from_one_step_before
N=6
recursive_builder(list(range(0,N)))
You could run this code on Colab
Edit: I've added memorization to improve the performance a little bit, but my build_from_memory is not O(1), so the improvement could be much better if I could improve the performance of that function.
memotization_dict = {3 : [[[0, 1, 2]]] , 4 : [[[0, 1, 2, 3]], [[0, 1], [2, 3]], [[0, 2], [1, 3]], [[0, 3], [1, 2]]] }
def build_from_memory(arr):
memory = memotization_dict[len(arr)]
ret_val = []
for item in memory:
l2 = []
for i in item:
l1 = []
for j in i:
l1.append(arr[j])
l2.append(l1)
ret_val.append(l2)
return ret_val
def recursive_builder(arr):
if len(arr) in memotization_dict:
return build_from_memory(arr)
temp_array = arr[0:len(arr)-1]
recursive_builder_one_step_before = recursive_builder(temp_array)
new_from_one_step_before = []
last_item = arr[len(arr)-1]
for item in recursive_builder_one_step_before:
for i in range(0 , len(item)):
temp_item = copy.deepcopy(item)
temp_item[i].append(last_item)
new_from_one_step_before.append(temp_item)
new_from_two_step_before = []
for i in range(0 , len(temp_array)):
new_arr = temp_array[:i] + temp_array[i+1 :]
recursive_builder_two_step_before = recursive_builder(new_arr)
new_from_two_step_before_inner = []
for item in recursive_builder_two_step_before:
new_item = item + [[temp_array[i] , last_item]]
new_from_two_step_before_inner.append(new_item)
new_from_two_step_before = new_from_two_step_before + new_from_two_step_before_inner
if(arr == list(range(0 , len(arr)))):
memotization_dict[len(arr)] = new_from_two_step_before + new_from_one_step_before
return new_from_two_step_before + new_from_one_step_before

Creating a list from different sublists repeated a different number of times

Please could I have help with the following query in Python 3.9.
I have the following sublists:
[0, 1]
[1, 3]
[2, 5]
I would like to make a new list with each of these sublists repeated a different number of times. Required output:
[[0,1],[0,1],[0,1],[1,3],[1,3],[2,5],[2,5],[2,5],[2,5]]
I have tried doing the following:
[[[0,1]]*3,[[1,3]]*2,[[2,5]]*4]
However I get this:
[[[0,1],[0,1],[0,1]],[[1,3],[1,3]],[[2,5],[2,5],[2,5],[2,5]]]
How do I get my desired output? Or alternatively, how do I just flatten it by one level? Thank you
You can just unpack the sublists:
[*[[0,1]]*3, *[[1,3]]*2, *[[2,5]]*4]
# [[0, 1], [0, 1], [0, 1], [1, 3], [1, 3], [2, 5], [2, 5], [2, 5], [2, 5]]
Note however, that the resulting sublists are not independent, but references to the same list objects (changes made to one sublist will be reflected in all the equal others)! Better use generators/comprehensions:
[*([0,1] for _ in range(3)),
*([1,3] for _ in range(2)),
*([2,5] for _ in range(4))]
# [[0, 1], [0, 1], [0, 1], [1, 3], [1, 3], [2, 5], [2, 5], [2, 5], [2, 5]]
The more general question of 1-level flattening has been asked and answered multiple times, but the main options are the nested comprehension:
[x for sub in lst for x in sub]
or itertools.chain:
[*chain(lst)]
You can use list loop:
r1 = [0, 1]
r2 = [1, 3]
r3 = [2, 5]
h = [*(r1 for x in range(3)),
*(r2 for x in range(2)),
*(r3 for x in range(4))]
print(h)

Is there a way to get "groups of combinations" of lists that don't overlap, and is exhaustive, using itertools on Python?

Here's what I mean: if you found all possible 2-element combinations of [1,2,3,4], you would get [1,2], [1,3],[1,4],[2,3],[2,4] and [3,4]
What I want is groups of combinations that don't overlap and include all elements. So for example [[1,2],[3,4]] is an example of one "group", because the elements in both combinations do not overlap, and all possible elements are used. [[1,3],[2,4]] is an example of another "group"
By the way, I'm aware that itertools will allow me to print combinations themselves. So for example, the following code:
combinations = itertools.combinations([1,2,3,4], 2)
for c in combinations:
print(c)
will output:
(1, 2)
(1, 3)
(1, 4)
(2, 3)
(2, 4)
(3, 4)
But again, that's just giving me combinations. I want GROUPS of combinations that are mutually exclusive and exhaustive with the elements.
Also, I'm sure I'm not using the proper vocabulary. If there is a formal term for anything I'm describing, I would appreciate learning it.
Thanks in advance!
These “groups of combinations” might be called set partitions into parts of size k.
I assume that k is divisor of n, so there are p=n/k parts.
Now we can recursively distribute items over parts. To avoid repeated generation of the same partition (like 01 23 45 and 01 45 23), we should restrict places for leading (the smallest) element of every group.
Here I used lastfilled parameter for index of the rightmost filled part, so item 0 always belongs to the 0-th part, item 1 might fall into parts 0 or 1 but not into part 2 and so on. Having intermediate result 01 __ __ we can make only 01 2_ __ at the next level, not 01 __ 2_.
Note that number of such partitions is
NPK(n,k) = n! / ((k!)^p * p!)
and grows fast (280 for n=9,k=3, 1401400 for 15/3). (Found OEIS sequence A060540)
Python code. I used global lists for parts contents and counts of occupied places in them to save memory, so I have to reset counts to previous state after recursive call.
n = 6
k = 2
p = n // k
parts = [[0]*k for _ in range(p)]
cnts = [0]*p
def genparts(m, lastfilled):
if m == n:
print(parts)
return
for i in range(min(p, lastfilled + 2)):
if cnts[i] < k:
parts[i][cnts[i]] = m
cnts[i] += 1
genparts(m+1, max(i, lastfilled))
cnts[i] -= 1
genparts(0, -1)
[[0, 1], [2, 3], [4, 5]]
[[0, 1], [2, 4], [3, 5]]
[[0, 1], [2, 5], [3, 4]]
[[0, 2], [1, 3], [4, 5]]
[[0, 2], [1, 4], [3, 5]]
[[0, 2], [1, 5], [3, 4]]
[[0, 3], [1, 2], [4, 5]]
[[0, 4], [1, 2], [3, 5]]
[[0, 5], [1, 2], [3, 4]]
[[0, 3], [1, 4], [2, 5]]
[[0, 3], [1, 5], [2, 4]]
[[0, 4], [1, 3], [2, 5]]
[[0, 5], [1, 3], [2, 4]]
[[0, 4], [1, 5], [2, 3]]
[[0, 5], [1, 4], [2, 3]]

How to determine if a nested list structure is the same as another, but with elements exchanged for new ones

Let's say we have two nested lists: L1 = [[0, 1], [0, 2]] and L2 = [[1, 2], [1, 3]]
The question is, does there exist a bijection between the integers in one list and the integers in the other list which transforms L1 into L2? For L1 and L2 given above, the answer is yes.
BIJECTION:
old 0 becomes new 1
old 1 becomes new 2
old 2becomes new 3
Recall our nested list L1 = [[0, 1], [0, 2]]. If we apply the mapping described above, then we get L2 = [[1, 2], [1, 3]] Therefore, foo(L1, L2) should return True. foo is the name of the equality operator we are trying to implement.
Also, order does not matter. Each list should be treated as a mathematical "set."
Some examples are shown below:
Left list: [[2, 1], [3, 1]]
Right list: [[1, 2], [1, 3]] : True
foo(left,right) returns True
why?
order doesn't matter
Left list: [[2, 1], [3, 1]]
Right list: [[1, 2], [3, 4]]
foo(left,right) returns False
why?
Two integers inside of the left list are the same, but all integers inside of the right list are different from each-other.
left= [[2, 1], [3, 1]]
right = [[0, 1], [0, 1]]
foo(left, right) returns False
why?
the right list contains only 2 distinct integers (0 and 1). The left list contains 3 distinct integers (1, 2, 3)
Some longer examples are shown below:
Original list: [[0, 1], [0, 2], [1, 2], [1, 3], [0, 1, 2]]
A1: [[4, 1], [4, 0], [1, 0], [1, 3], [4, 1, 0]] : True
A2: [[4, 1], [4, 0], [1, 3], [1, 0], [4, 0, 1]] : True
B: [[1, 2], [3, 1], [2, 4], [1, 4], [2, 4, 1]] : True
C: [[3, 2], [5, 2], [5, 0], [0, 2], [5, 0, 2]] : True
D: [[5, 2], [5, 2], [3, 0], [0, 2], [5, 0, 2]] : False
E: [[3, 0], [0, 3], [5, 0], [0, 2], [5, 0, 2]] : False
Bijection for Example A1:
ORIGINAL A
0 4
1 1
2 0
3 3
A2 is simply a reordering of A1
In example B, 2 and 4 are playing the same role as 0 and 2 in the original list. 1 is in the same role in both lists, as is 3.
In example C, 0 and 5 are playing the same role as 0 and 2 in the original list, 2 is playing the same role as 1 in the original list, and 3 is playing the same role in both lists.
In example D, there are two sub-lists which are the same ([5, 2]), while the original list has no repeating sub-lists.
In example E, 0 is in all four length-2 sub-lists, while in the original list, no number is in all four length-2 sub-lists.
Here is the code I have from my attempt, however it does not work when a low number (like 0) is exchanged for one of the largest numbers in the lists (like say 4). When it does the sorting, it fails to recognize that the 4 is playing the same role as the 0. Since low numbers can be exchanged for high numbers, sorting will not work.
def CheckUnique(configs, newconfig):
sortednewconfig = sorted([sorted(i) for i in newconfig])
presentnumbers = []
canonicalnewconfig = []
for sub in sortednewconfig:
for i in sub:
if i not in presentnumbers:
presentnumbers.append(i)
for sub in sortednewconfig:
cansub = []
for i in sub:
cansub.append(presentnumbers.index(i))
canonicalnewconfig.append(cansub)
if canonicalnewconfig not in configs:
return True
else:
return False
Use a all and any with a zip with it:
>>> l = [[0, 1], [0, 2], [1, 2], [1, 3], [0, 1, 2]]
>>> l2 = [[4, 1], [4, 0], [1, 3], [1, 0], [4, 0, 1]]
>>> all([any(i in x for i in y) for x, y in zip(l, l2)])
True
>>> l3 = [[5, 2], [5, 2], [3, 0], [0, 2], [5, 0, 2]]
>>> all([any(i in x for i in y) for x, y in zip(l, l3)])
False
>>>
You are trying to solve a modified form of what is known as the "graph isomorphism problem." There are existing algorithms which will determine if two graphs are isomorphic, but existing algorithms are all very slow, particularly for large graphs.
"Graphs" are diagrams with dots and lines.
Suppose we have two nested lists:
L1 = [[0, 1], [0, 2], [1, 2], [1, 3], [0, 1, 2]]
L2 = [[4, 1], [4, 0], [1, 3], [1, 0], [4, 0, 1]]
Draw a picture L1 by from the following instructions:
For each element of a sublist, draw a dot. For example consider the sublist
[0, 1]. It will get two dots, one dot for 0 and one dot for 1.
Draw a circle around a cluster of dots if they are in the same
sub-list.
Draw a line between two dots if the two dots represent the same integer.
After that, condense each group of dots (sublist) into a single dot.
You can draw a similar diagram for nested list L2 The question is, after you remove the all of the numbers, do the two diagrams for L1 and L2 look the same? You might have to swap the colors around (blue edges become red, red become, blue, etc...) Also the dots might have to be moved around until it looks the same.
The traditional graph isomorphism problem has the lines connecting dots all of the same color. Your problem is slightly different from traditional in that your edges are colored.
I think that you can get rid of the separate colors and simply number each edge with the number of colors that used to be there. It then becomes an "edge-weighted graph"
Do a google search for "graph isomorphism for edge-weighted graphs."
What you are working on is extremely difficult. I recommend looking at local university math department websites for people you can contact. Looking for email address of professors whose job title is "graph theorist." Contact them, and ask for their advice.
Like I said, what you are working on is extremely difficult.
I think that you can solve it as follows:
Build the edge-weighted graph for the left list
Build the edge-weighted graph for the right list
Determine if the two graphs are isomorphic or not.

Categories