dmbcalado / PushSwap

Own algorithm that with a restrict set of rules arranges any stack of numbers by order in the fewer steps possible. Good results.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Push Swap (grade : 90)


https://user-images.githubusercontent.com/76601369/110706242-77158d00-81ef-11eb-8085-5da6f0988553.jpg

This project has the goal of creating an algorithm that is step-efficcient into sorting a stack of integers (a) by using another stack (b) with a restrict set of rules on how to move the numbers between them.

So i'll start by explaining the rules and what they mean, then i'll explain the logical construction that driven my algorithm to reach sucess in doing so, and with a reasonable average number of steps.

1 - RULES :

So at the start, we will receive the numbers as arguments passed as the following:

./push_swap -43 23 1 0 54 43 (...)

And the goal is to convert them to integers, put all in stack a and with an empty stack b, sort in ascending order the numbers into stack a. To do so, we have the following operations at our disposal:
  • sa (swap a): Swap the first 2 elements at the top of stack a.
  • sb (swap b): Swap the first 2 elements at the top of stack b.
  • ss : sa and sb at the same time.
  • pa (push a): Take the first element at the top of b and put it at the top of a.
  • pb (push b): Take the first element at the top of a and put it at the top of b.
  • ra (rotate a): Shift up all elements of stack a by 1. The first element becomes the last one.
  • rb (rotate b): Shift up all elements of stack b by 1. The first element becomes the last one.
  • rr : ra and rb at the same time.
  • rra (reverse rotate a): Shift down all elements of stack a by 1. The last element becomes the first one.
  • rrb (reverse rotate b): Shift down all elements of stack b by 1. The last element becomes the first one.
  • rrr : rra and rrb at the same time.
We can see a draw I made representing the possible operations on stack a (the same operations are the ones allowed on stack b) to grasp the rules even better:

https://user-images.githubusercontent.com/76601369/110706242-77158d00-81ef-11eb-8085-5da6f0988553.jpg

2 - LOGIC CONSTRUCTION :

At the beginning, I must confess I felt kind of overwelmed. The first questions that come to your mind is how with this set of rules would i even sort the stack a.

Since I'm not a genius (at least at sorting algorithms (yet)) i thought "well, what is the nicest way I can brute force the best outcome for sorting X numbers. What is even X, how many numbers is it doable to brute force and not have 30 files of 5 functions (remember i have the Norm of 42 to respect). The number I came across was X = 4 since 5 would have been way to much possible solutions and 4 it is still doable. However, i had to understand that with this rules, the best way to sort 4 is different then the best way to sort 4 that are on top of a stack. And the solution to the best way to sort 4 could give a wrong outcome on 4 that were on top of a stack. Thats when i built the base of my sorting algorithm, a set of functions that would:

  • Sort for 2 elements at the stack a.
  • Sort for 3 elements at the stack a.
  • Sort for 4 elements at the stack a.
  • Sort for 4 elements at the top of the stack a.
  • Sort for 5 elements at the stack a.

Now i had all the brute force i needed to construct my algorithm. So to begin, i new this would be a hard task so i had to come across a nice way of solving this by only having chuncks of 4 to ultimatly sort. I cannot recall how many times i thought of a different solution. Finally, i thought long enough to agree on one solution, that would evolve along the time since new thoughts of making it better would appear, and the need of doing so had arrived at some point.

The idea is the following: firstly i work with temporary stacks, that i call pos_a and pos_b stack. This stacks take the final position of each number, so the list had to be ordered from 1 to N numbers. Now, if I'm good at sending the right numbers at a given order to the stack b, then when it will require me phew steps to send each chunck of 4 (unsorted) to a, and sort it. So what i realized is that it would be efficient for me to pick 5 numbers that would be at the middle of the pos_a stack at the end, and add both the 4-stack from the top and from the bottom. So what i had to do was send the last 4-stacks (from top and from bottom) then the next ones, etc... till i send the ones that would be just at the bottom or at the top of 5 numbers at the pos_a stack. Of course this may raise the question but what if by subtracting 5 numbers from the total ones, what lays at the bottom and what is a the top are not multiples of 4. And for that, i created what i called the outliers, that is, if its not a multiple of 4 then exists 1, 2 or 3 outliers at the bottom or at the top. Once an outlier was spoted, he would also be sent to the stack pos_b.

So what we end up is something like the next picture:


https://user-images.githubusercontent.com/76601369/110706242-77158d00-81ef-11eb-8085-5da6f0988553.jpg

Where we can see the stack a with the 5 middle numbers and the b has the stacks ordered and the outliers randomly distributed inside the stacks (the orange lines).

So now what we have to do is rotate b till all of the selected 4-stack (its selected the top or bottom dependingly on wich takes less steps to send to a) is sent to a, then rotate it back. Proceed with this method till we end up with just the outliers and solve it for them. Pretty simple right?

However, since we had to sort 500 numbers being the most step-efficient, there were some tunes that would had to be made to make this logic scalable.

One easy way to do so, is to send to b not only the 4-stacks but a multiple, that is, 2 top and 2 bottom stacks for instance. This was just a tune on sending to b, so i could tune to find the optimal number, since too much stacks would make pos_b rotate back and forth too much, and too phew the stack a would have to rotate too many times to get the stacks to b, so a balance should be find. I found out the optimal numbers would be to send to pos_b 2 4-stacks each time for 100 numbers and 4 4-stacks each time for 500 numbers.

This is my algorithm basically. There were some future tunes that could be possible:
  • The first one was to not rotate backwards the outliers once they would be sent to the bottom of the pos_b. This would save some rotations.
  • Sorting the top 4-stack on the top of pos_b along side with sorting the top 4-stack on the pos_a. This would create more situations of ra + rb = rr.

So the only thing missing is to explain how to pass the steps made on the position stacks to the real a and b stacks. I do a list of indexs associated with every step:

  • pb = -2
  • pa = -1
  • ra = 1
  • rb = 2
  • rra = 3
  • rrb = 4
  • sa = 5
  • sb = 6

And every time i perform a new operation, i recreate the list of integers with 1 more int slot available to add the corresponding index of the operation.

Now while running, i can find this patterns and reduce them to smaller cases:

  • ra + rb or rb + ra = rr
  • rra + rrb or rrb + rra = rrr
  • ra + sb + rb = sb + rr
  • rb + sa + ra = sa + rr
  • rra + sb + rrb = sb + rrr
  • rrb + sa + rra = sa + rrr

By doing so, i end up with my stack a sorted with not too much steps. I got aproximatly 830 steps for the 100 numbers and aproximatly 64000 steps for the 500 numbers, wich gives me the grade of 4 out of 5 for both. The project doesn't have any leak or norm error also, the parsing is done accordingly also, so i got the grade of 90 out of 100 in this project.


About

Own algorithm that with a restrict set of rules arranges any stack of numbers by order in the fewer steps possible. Good results.


Languages

Language:C 99.2%Language:Makefile 0.8%