Bubblesort, rocksort, and cocktail-shaker sort(quuxplusone.github.io)
quuxplusone.github.io
Bubblesort, rocksort, and cocktail-shaker sort
https://quuxplusone.github.io/blog/2020/12/13/bubblesort-rocksort-shakersort/
7 comments
Also you you need "real" (in the quantum sense) random generator for the shuffling. If you use a PRNG then you risk destroying all the universes.
You could always just use a pseudorandom number generator and rely on bit errors to generate the different potential sort orders. ;)
If you add bit errors to the picture then the check after shuffle spuriously passing has a significant, potentially much higher probability than a bit error introducing new, previously impossible random shuffling and producing a sorted sequence. So in a significant portion (maybe most) of the non-destroyed universes quantum bogosort returns an unsorted sequence due to bit errors.
Repeat the check an arbitrary number of times then? We have infinite universes, remember. :)
If you're using a cloud host that doesn't charge you anything for instances with a very low runtime, you can implement quantum bogosort for free - just have every unsorted instance self-destruct promptly.
For any given input, there is no advantage to running more than one instance in the same universe.
This is creating many universes though.
You can trivially parallelize this algorithm to be O(1): for each permutation you create universes to check all finite lengths of the list in parallel and destroy the universes that check either less elements than are in the list or that run off the end of the list.
I'm not sure if this can extend to infinite size lists. Sorting infinite lists in finite time makes my head hurt.
I'm not sure if this can extend to infinite size lists. Sorting infinite lists in finite time makes my head hurt.
The sleep sort only requires n operations.
for x in "$@"; do (sleep $x; echo $x) &; done
for x in "$@"; do (sleep $x; echo $x) &; done
Computationally, this (ab)uses the scheduler to do your sorting for you. So really the performance becomes whatever your scheduler’s performance is, probably heapsort time (O(n log n)).
Although if your computer is slow compared to the size of the list this will not work due to race conditions. Still cleaver.
That was a great article! The last paragraph, however:
"By the way, Knuth is interested in these algorithms mainly for the interesting mathematical problems (...) not because they’re in any way efficient algorithms! "
Made me wonder, these kind of algorithms, where there is a lot of comparing and swapping of adjacent records might be more efficient on a tape-based architecture. Or an architecture with a lot of prefetch caching.
"By the way, Knuth is interested in these algorithms mainly for the interesting mathematical problems (...) not because they’re in any way efficient algorithms! "
Made me wonder, these kind of algorithms, where there is a lot of comparing and swapping of adjacent records might be more efficient on a tape-based architecture. Or an architecture with a lot of prefetch caching.
Insertion sort is known to be the fastest option for small ranges, which is why it often is the sorting option hybrid sorts switch to for that case (like when the partitions in quicksort or merge sort are small enough).
AFAIK, this is mainly because even though it is expected O(n²), it has a really tiny overhead because insertion sort is incredibly simple, so for small enough n it wins. However, being very cache-friendly is also part of it (it's a linear search over the array).
IIRC some people found similar results for bubble sort.
AFAIK, this is mainly because even though it is expected O(n²), it has a really tiny overhead because insertion sort is incredibly simple, so for small enough n it wins. However, being very cache-friendly is also part of it (it's a linear search over the array).
IIRC some people found similar results for bubble sort.
It's both cache-friendly and its branches are easy to predict.
just want to quickly share an IMHO interesting sorting algorithm visualization video: https://youtu.be/kPRA0W1kECg
"Visualization and "audibilization" of 15 Sorting Algorithms in 6 Minutes. Sorts random shuffles of integers, with both speed and the number of items adapted to each algorithm's complexity. The algorithms are: selection sort, insertion sort, quick sort, merge sort, heap sort, radix sort (LSD), radix sort (MSD), std::sort (intro sort), std::stable_sort (adaptive merge sort), shell sort, bubble sort, cocktail shaker sort, gnome sort, bitonic sort and bogo sort (30 seconds of it)."
"Visualization and "audibilization" of 15 Sorting Algorithms in 6 Minutes. Sorts random shuffles of integers, with both speed and the number of items adapted to each algorithm's complexity. The algorithms are: selection sort, insertion sort, quick sort, merge sort, heap sort, radix sort (LSD), radix sort (MSD), std::sort (intro sort), std::stable_sort (adaptive merge sort), shell sort, bubble sort, cocktail shaker sort, gnome sort, bitonic sort and bogo sort (30 seconds of it)."
Bubblesort appears to be inferior to insertion sort in every single way. It's more complicated, it's slower in every situation. I don't know why you'd ever use it. What's the point of it?
The "point" is that it is one of the three intuitive sorting algorithms that someone unfamiliar with algorithmic complexity will write when asked to write a computer sorting algorithm (and happened to be the first sort I wrote when tasked with just that in college long ago). This makes it a useful thing to discuss because it's so clearly inferior to the other two despite being just as intuitive.
Insertion sort seems more intuitive. Just take the next item and file it at its correct position.
I think the whole "swap neighbors and scan many times" thing isn't something a novice would come up with.
I think the whole "swap neighbors and scan many times" thing isn't something a novice would come up with.
My anecdotal experience as a teacher is that when tasked with implementing sort, students go for selection sort.
However when asked to do it in real life on a bundle of graded exam sheets, if I ask them to sort alphabetically, they will do insertion sort, if I demand to sort by grade, they will bubble sort. The number of sheets may also change their method.
However when asked to do it in real life on a bundle of graded exam sheets, if I ask them to sort alphabetically, they will do insertion sort, if I demand to sort by grade, they will bubble sort. The number of sheets may also change their method.
If you need an additional datapoint, bubble sort is also the first sorting algorithm I came up with. I find it super intuitive - the only slightly-less intuitive part maybe is that you need to do the outer loop at most N times (in my implementation, the outer loop was `while(changed)` where `changed` would be set to `true` if any swap was performed in the inner loop ).
Even though I know the algorithm, I still find that I need to think a nontrivial amount to see why it will not miss anything. I mean, sure, you are strictly improving the "sortedness" by a discrete amount in each pass and crucually, you don't destroy any progress you made before (not totally trivial), and there's only a finite amount of "sortedness" to attain, so it will converge to the sorted state. But how exactly and, as you say, after how many iterations, is not so clear. In more complex algorithms, you may get stuck in a local optimum with such a greedy approach though.
But contrast that with insertion sort. I have a sorted pile that grows after each insertion. I find it trivially clear that it works and when I will be done.
It's also something that people will intuitively do in the physical world to sort paper documents I think: just grow a pile of sorted documents by adding the next one in the right place.
But contrast that with insertion sort. I have a sorted pile that grows after each insertion. I find it trivially clear that it works and when I will be done.
It's also something that people will intuitively do in the physical world to sort paper documents I think: just grow a pile of sorted documents by adding the next one in the right place.
The refutation of that is in the comment you're replying to:
> [Bubble sort] happened to be the first sort I wrote when tasked with just that in college long ago
> [Bubble sort] happened to be the first sort I wrote when tasked with just that in college long ago
I think this is a bit dangerous because people take away the “I understand and can implement bubble sort” part, but forget the “it’s very inefficient, don’t use it” part. So people end up using bubble sort in production! It doesn’t help that it has such a cute and catchy name.
Probably no real fix for that. In algorithms if you only taught the best algo for a particular problem, you'd lose everyone on the first day, and miss out on a lot of analysis/teaching opportunities. Remember that algorithms study isn't just about speed, it's actually about _correctness_ first.
Besides, it's pretty rare for someone to need to write their own sorting algorithm anyway, in production. And if they do but they're so addled they do it from memory and randomly pick bubblesort, hey at least it's fairly easy to be correct.
Besides, it's pretty rare for someone to need to write their own sorting algorithm anyway, in production. And if they do but they're so addled they do it from memory and randomly pick bubblesort, hey at least it's fairly easy to be correct.
I wonder how many people actually write a sort algorithm in production code anymore. I haven't personally seen this, but I guess that doesn't really mean it doesn't exist.
What I have seen is pretty horrific abuse of Linq (i.e. repeated lookup of `things.Where(t => t.Id == query.Id).First()` as if it was a dictionary) which might stem from the same root cause - not thinking about what your code is actually doing and why it might be inefficient. It doesn't help that there's that there's those oft-quoted Bentley's Rules of Optimization which gives people an excuse to not care :-/
What I have seen is pretty horrific abuse of Linq (i.e. repeated lookup of `things.Where(t => t.Id == query.Id).First()` as if it was a dictionary) which might stem from the same root cause - not thinking about what your code is actually doing and why it might be inefficient. It doesn't help that there's that there's those oft-quoted Bentley's Rules of Optimization which gives people an excuse to not care :-/
Huh, like when I learned this (back when I was in elementary school and the class was taught using unstructured BASIC, so the more natural lesson with most things more modern of “rely on your standard library for sorting, unless you have a really good cause to roll your own and then do some research” wasn't an option) the takeaway was “look up the implementation of a better algorithm if you are sorting nontrivial data”. (Plus, “its worth thinking about how much work your code is going to take to do what it needs to do”, as it was also my first introduction to algorithmic complexity.)
I'm trying to grasp the pedagogical failure that would result in people taking the lesson “implementing bubble sort is a reasonable thing to ever do for non-toy problems”.
I'm trying to grasp the pedagogical failure that would result in people taking the lesson “implementing bubble sort is a reasonable thing to ever do for non-toy problems”.
I don't think I have ever implemented a sort. What modern tooling doesn't provide a sort? Even C has qsort.
Why even bother teaching it, then?
(Semi-serious question, as you're quite right that hardly anyone ever needs to write a sort routine by hand these days.)
(Semi-serious question, as you're quite right that hardly anyone ever needs to write a sort routine by hand these days.)
Maybe it's the things you learn along the way, such as:
- Recursion and induction (mergesort, quicksort)
- Lower bounds on running time (O(n log n))
- Data structures (heaps in the context of heapsort, binary search trees in the context of treesort, tries in the context of radix sort, etc.)
- Difference between worst case and average case (quicksort)
- The role of the hidden coefficient in O notation (insertion sort is fast for small n even though its running time is O(n^2))
Etc.RISC V Assembler :)
It's good when there are a lot of elements off by a short distance in a mostly sorted list. The go-to use case that I've seen is depth sorting for 3D rendering on really low end hardware. Polygons usually don't move much in the Z-order each frame, so you run a few passes of bubblesort per frame for a good-enough linear-time sort.
Also, two passes of cocktail-shaker sort (back and forth bubble sort) would guarantee that the top and bottom z-order would be correct, which would probably be good enough to keep really obviously bad z-ordering at bay.
Like you say, when it's already partially sorted and you're just having to maintain a slowly-updated list, a single partial-sort pass per frame could be quite enough to maintain a good-enough ordering of a list.
Like you say, when it's already partially sorted and you're just having to maintain a slowly-updated list, a single partial-sort pass per frame could be quite enough to maintain a good-enough ordering of a list.
Do you know how it compares to insertion sort, which is also fast on nearly sorted lists?
I Germany we have a saying that goes "Nobody is completely useless - you can still serve as a bad example."
It's easier to teach and visualize, that's why it's usually taught as the first algorithm to computer science students.
Like I clearly said, not when compared to insertion sort.
The differences between bubblesort and insertion sort are quite subtle and learning that it an of itself can be instructive IMO in terms of teaching rigour.
Radix sort ftw
I feel that if you have to go to Stackoverflow after reading Knuth, maybe you didn't actually read the Knuth? Just skimmed it?<p>Not trying to piss on the OP here, but the idea with the Knuth books is that you spend time with them. Not just reading the table of contents and then going to Stackoverflow.<p>In fact, the reason for having books by authorities in the field like Knuth is so you become less dependent on places like Stackoverflow that replace factual validation by popularity validation. If you really did read Knuth you should be able to produce content on Stackoverflow instead of consuming it.
OP being in a book club - and then even writing a blog post about their takeaways - suggest to me that they did read it carefully.
No book is perfect and everything has a different level of knowledge that they have to integrate the new information from the book with. It seems reasonable to me to go to Stackoverflow to clear up questions, especially if you have such a well-known topic.
> If you really did read Knuth you should be able to produce content on Stackoverflow instead of consuming it.
Nope. Reading is just the first step. Then you have to find out whether or not you actually understood things correctly by discussing what you read and/or getting your hands dirty. After you've gained some experience, then you can start answering questions from others.
No book is perfect and everything has a different level of knowledge that they have to integrate the new information from the book with. It seems reasonable to me to go to Stackoverflow to clear up questions, especially if you have such a well-known topic.
> If you really did read Knuth you should be able to produce content on Stackoverflow instead of consuming it.
Nope. Reading is just the first step. Then you have to find out whether or not you actually understood things correctly by discussing what you read and/or getting your hands dirty. After you've gained some experience, then you can start answering questions from others.
[0] https://en.wikipedia.org/wiki/Bogosort#Quantum_bogosort