42 push_swap group project
0

Configure Feed

Select the types of activity you want to include in your feed.

C 97.9%
Makefile 2.1%
222 1 0

Clone this repository

https://tangled.org/jaykrishnan97.tngl.sh/push_swap https://tangled.org/did:plc:o3yyamaqhy3tfucmb4sek2kb
git@tangled.org:jaykrishnan97.tngl.sh/push_swap git@tangled.org:did:plc:o3yyamaqhy3tfucmb4sek2kb

For self-hosted knots, clone URLs may differ based on your setup.



README.md

This project has been created as part of the 42 curriculum by gvoelkne, jkrishna.

push_swap#

Description#

push_swap sorts a sequence of integers using exactly two stacks (a and b) and a fixed set of operations, printing the shortest operation sequence it can find to stdout. The program implements four distinct sorting strategies — Simple, Medium, Complex, and Adaptive — and selects among them based on a runtime flag or, by default, by measuring how disordered the input is.

The project was completed as a group by:

Login Contributions
gvoelkne (gabriel) Parser, input validation, arg handling, benchmark infrastructure, disorder metric
jkrishna (jay) Stack data structure, all operators, sorting algorithms

Instructions#

Build

make        # produces ./push_swap
make re     # clean rebuild

Run

# default (adaptive strategy)
./push_swap 5 4 3 2 1

# force a specific strategy
./push_swap --simple  5 4 3 2 1
./push_swap --medium  5 4 3 2 1
./push_swap --complex 5 4 3 2 1

# verify correctness with the provided checker
ARG="4 67 3 87 23"; ./push_swap --complex $ARG | ./checker_linux $ARG

# benchmark output (disorder %, strategy name, op count and breakdown) on stderr
./push_swap --bench 5 4 3 2 1

Numbers may be passed as separate arguments or as a single quoted string ("5 4 3 2 1"). Passing no numbers prints nothing. Invalid input (non-integers, out-of-range values, duplicates) prints Error to stderr and exits with status 1.

Algorithms#

Disorder metric#

Before any sorting begins the program computes a disorder value in [0, 1]: the fraction of pairs (i, j) with i < j where a[i] > a[j] (i.e. the normalised inversion count). A disorder of 0 means the stack is already sorted; 1 means it is in reverse order.

Simple — Insertion Sort · O(n²)#

The minimum element of stack a is located, the stack is rotated so that element sits on top (using ra or rra, whichever is shorter), and it is pushed to b. This repeats until two elements remain in a, which are swapped if needed with sa. All of b is then pushed back to a with pa.

Each "find-and-push" pass costs at most n/2 rotations; doing this n times gives O(n²) operations.

Why use it for low disorder (< 0.2)? A nearly-sorted stack has very few inversions. Insertion sort degrades gracefully: when the minimum is already near the top, the rotation cost is small, so the real operation count is much closer to O(n) than the theoretical worst case.

Medium — Chunk Sort · O(n√n)#

Elements are assigned a rank from 0 (smallest) to n−1 (largest). The rank space is split into chunks of size ≈ 5√n. For each chunk, stack a is scanned: any element whose rank falls in the current range is pushed to b immediately; elements outside the range are rotated past (choosing ra or rra to minimise moves). Once all chunks are pushed, stack b holds elements in roughly descending rank order.

b is then drained back to a greedily: on each step, every element in b is considered as a candidate, its combined rotation cost to become the next element inserted into a at the correct position is computed, and the cheapest candidate is moved.

There are n/chunk_size ≈ √n chunks. Within each chunk, each element requires at most O(√n) rotations on average to reach the top (elements are spread uniformly, so expected distance ≈ chunk_size/2 ≈ √n/2). Total push phase: n × O(√n) = O(n√n). The greedy pull-back phase is also empirically O(n√n).

Why use it for medium disorder (0.2–0.5)? With a moderately shuffled stack, chunk sort's chunk-based locality keeps rotation costs low without needing the full overhead of a bit-by-bit radix pass.

Complex — LSD Radix Sort · O(n log n)#

Elements are assigned ranks 0–(n−1). The sort processes the binary representation of each rank from the least-significant bit upward. In each pass, every element in a is inspected: if the current bit is 0 the element is pushed to b; if it is 1 it is rotated to the back of a. After all n elements are processed, everything in b is pushed back to a. After log₂(n) passes the ranks are in ascending order in a.

Each of the log₂(n) passes costs exactly 2n operations (n push/rotate + n push-back), giving O(n log n) total.

Why use it for high disorder (≥ 0.5)? Highly randomised input invalidates any locality assumption. Radix sort's cost depends only on n and the number of bits — it is immune to disorder and scales reliably.

Adaptive — disorder-based dispatch#

Disorder Strategy used
< 0.2 Simple (O(n²))
0.2–0.5 Medium (O(n√n))
≥ 0.5 Complex (O(n log n))

Threshold rationale. 0.2 is the point above which a nearly-sorted assumption no longer holds and the O(n²) constant becomes dominant. 0.5 marks the midpoint of disorder where chunk locality degrades enough that the guaranteed O(n log n) of radix sort becomes cheaper in practice. Both thresholds were validated empirically against the 100- and 500-element benchmarks.

Space complexity. All strategies use stack b as auxiliary storage (O(n) nodes) and O(1) additional variables. No extra arrays are allocated.

Resources#

AI usage. Claude Code (Anthropic) was used throughout the project:

  • Explaining Big-O analysis for the chunk sort and radix sort variants in the push_swap operation model (i.e., counting operations rather than comparisons).
  • Drafting documentation.

All AI-generated content was reviewed, tested, and understood by both group members before being accepted into the project.