Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
morgan3d committed Aug 31, 2016
1 parent 5e70e6a commit 6bdb6e5
Show file tree
Hide file tree
Showing 3 changed files with 327 additions and 4 deletions.
8 changes: 4 additions & 4 deletions README.md
Expand Up @@ -19,6 +19,10 @@ Graphics
- oculusSample - Oculus SDK OpenGL Sample. Windows Visual Studio project for initializing and rendering to a DK2 or later HMD using OpenGL. June 2015.
- terrain - Continuous detail GPU Terrain, with multi-scale texturing and a hardware optimized variant of the geo clipmap. April 2014 http://casual-effects.blogspot.com/2014/04/fast-terrain-rendering-with-continuous.html

C++
==================================================================
- sort - Elegant C++ reference implementations of heapSort, insertionSort, mergeSort, and quickSort. (now maintained in [The Graphics Codex](http://graphicscodex.com)). December 2012

<!--
- tachyonVR - Unity 5.4 VR Starter Project. August 2016.
<li><a href="python/python-oit.zip">Python implementation of Blended Order Independent Transparency</a> contributed by Nicolas P. Rougier, based on the paper by Louis Bavoil and Morgan McGuire</li>
Expand Down Expand Up @@ -68,10 +72,6 @@ Scheme
C/C++
===========================================================================
<li><a href="C++/indent++.zip">indent++</a> Platform-independent command-line program to auto-indent C++ and JavaScript code (similar to the Unix indent program for C), with precompiled OS X binary.</li>
<li><a href="C++/sort.cpp">sort.cpp</a> Elegant reference implementations of some common sorting algorithms: heapSort,
insertionSort,
mergeSort, and
quickSort.</li>
<li><a href="C++/getip.cpp">getip.cpp</a> Get IP address, subnet mask, broadcast address, and MAC address of the network adapter(s) on Unix-based systems.</li>
<li><a href="C++/kbhit.cpp">_kbhit.cpp</a> _kbhit for Linux/POSIX without using Curses (see also my <a href="http://www.flipcode.com/archives/_kbhit_for_Linux.shtml">flipcode article</a>)
<li><a href="C++/safesprintf.cpp">safesprintf.cpp</a> - An sprintf implementation that prevents buffer overflows (a newer version appears as the "format" function in the G3D library. See also my <a href="http://www.flipcode.com/archives/Safe_sprintf.shtml">flipcode article</a>)
Expand Down
1 change: 1 addition & 0 deletions sort/README.md
@@ -0,0 +1 @@
Elegant reference implementations of some common sorting algorithms in C++. By Morgan McGuire, December 2012.
322 changes: 322 additions & 0 deletions sort/sort.cpp
@@ -0,0 +1,322 @@
/**
\file sort.cpp
Elegant reference implementations of some common sorting
algorithms:
- heapSort
- insertionSort
- mergeSort
- quickSort
Written in C++ with an eye towards portability to other languages
such as C, Java, GLSL, CUDA, JavaScript, and Python.
\author Morgan McGuire, morgan@cs.williams.edu.
http://graphics.cs.williams.edu
http://cs.williams.edu/~morgan/code/
License: BSD (see download page)
Last modified 2012-12-26
*/

#include <stdlib.h>

typedef float Key;
class Element {
public:
/** Sort key */
Key key;

/** Arbitrary data here ... */
int data;

Element(Key key = 0.0f) : key(key), data(0) {}
};


/** Sorts the array of n elements in place by Element::key from least
to greatest. O(n log n) time, O(1) additional space.
This has the best performance of the methods that I profiled,
beating insertionSort for N > 16.
*/
void heapSort(Element* element, const int N);

/** Sorts the array of n elements in place by Element::key from least
to greatest. O(n^2) time, O(1) additional space.
This was the fastest sort for N <= 16 in practice, but was beaten
significantly by heapSort for large N when the asymptotic behavior
emerged.
*/
void insertionSort(Element* element, const int N);

/** Sorts the array of n elements in place by Element::key from least
to greatest. O(n log n) time, O(n) additional space. Takes advantage
of pointer arithmetic in C.*/
void quickSort(Element* element, const int N);

/** Sorts the array of n elements in place by Element::key from least
to greatest. O(n log n) time, O(n) additional space. Does not use
pointer arithmetic. Better base for porting.*/
void quickSort2(Element* element, const int N);

/** Sorts the array of n elements in place by Element::key from least
to greatest. O(n log n) time, O(n) additional space.
This has the worst performance of the methods that I profiled,
running in 3x the time of heapSort and 2x the time of quickSort.
*/
void mergeSort(Element* element, const int N);


///////////////////////////////////////////////////////////////////////////////

#define SWAP(a, b) {Element temp = element[a]; element[a] = element[b]; element[b] = temp; }

void insertionSort(Element* element, const int N) {

// Treat elements [0, numSorted) as sorted and the rest to be a
// bucket of values awaiting sorting.
for (int numSorted = 1; numSorted < N; ++numSorted) {
// Get the next element to be inserted
const Element temp = element[numSorted];

// Drag temp backwards
int i;
for (i = numSorted; (i > 0) && (temp.key < element[i - 1].key); --i) {
element[i] = element[i - 1];
}

// Re-insert temp (possibly at the location where it started!)
element[i] = temp;
}
}

///////////////////////////////////////////////////////////////////////////////

/** Merge [low, mid] with [mid + 1, high] in place */
void merge(Element* element, int low, int mid, int high, Element* temp) {
int i = low;
int j = mid + 1;

// Index into temp
int t = low;

// Merge same-length portions
while ((i <= mid) && (j <= high))
// Choose the smaller element
if (element[i].key < element[j].key)
temp[t++] = element[i++];
else
temp[t++] = element[j++];

// Copy remaining elements from the low side
while (i <= mid) temp[t++] = element[i++];

// Copy remaining elements from the high side
while (j <= high) temp[t++] = element[j++];

// Copy everything back from temp to element
for (t = t - 1; t >= low; --t)
element[t] = temp[t];
}

/** High is inclusive */
void mergeSort2(Element* element, int low, int high, Element* temp) {
if (low < high) {
int mid = (low + high) / 2;
mergeSort2(element, low, mid, temp);
mergeSort2(element, mid + 1, high, temp);
merge(element, low, mid, high, temp);
}
}


void mergeSort(Element* element, const int N) {
Element* temp = (Element*)malloc(sizeof(Element) * N);
mergeSort2(element, 0, N - 1, temp);
free(temp);
}

/////////////////////////////////////////////////////////////////////////////


void quickSort(Element* element, const int N) {
if (N > 0) {
// Partition elements
const Element pivot = element[0]; // ...or choose randomly
int i = 0;
int j = N;

while (true) {
while ((element[++i].key < pivot.key) && (i < N));
while (element[--j].key > pivot.key);

if (i >= j) break;

SWAP(i, j);
}

SWAP(i - 1, 0);

quickSort(element, i - 1);
quickSort(element + i, N - i);
}
}

/////////////////////////////////////////////////////////////////////////////

void quickSort2Rec(Element* element, const int start, const int N) {
if (N > 0) {
// Partition elements
const Element pivot = element[start]; // ...or choose randomly
int i = start;
int j = N + start;

while (true) {
while ((element[++i].key < pivot.key) && (i < N + start));
while (element[--j].key > pivot.key);

if (i >= j) break;

SWAP(i, j);
}

SWAP(i - 1, start);

quickSort2Rec(element, start, (i - start) - 1);
quickSort2Rec(element, i, N - (i - start));
}
}

void quickSort2(Element* element, const int N) {
quickSort2Rec(element, 0, N);
}

/////////////////////////////////////////////////////////////////////////////

/** Sift element[parent] down the tree */
void siftDown(Element* element, int parent, int end) {

const Element value = element[parent];

int maxChild = parent * 2 + 1;
while (maxChild <= end) {

// See if the other child's index is larger
if (maxChild < end) {
int otherChild = maxChild + 1;
maxChild = (element[otherChild].key > element[maxChild].key) ? otherChild : maxChild;
}

// Stop when the parent is larger than the max child
if (value.key >= element[maxChild].key) break;

element[parent] = element[maxChild];

parent = maxChild;
maxChild = parent * 2 + 1;
}

element[parent] = value;
}


void heapSort(Element* element, const int N) {
// Form a max heap

for (int i = (N / 2); i >= 0; --i)
siftDown(element, i, N - 1);

for (int i = N - 1; i >= 1; --i) {
SWAP(0, i);
siftDown(element, 0, i - 1);
}
}


#undef SWAP


#if 0 // Sample profiling routines

#include <G3D/G3DAll.h>

/** For timing */
void noSort(Element* element, const int N) {}

void printArray(const Array<Element>& a) {
for (int i = 0; i < a.size(); ++i) {
printf("%g ", a[i].key);
}
printf("\n");
}


float timeRoutine(void (*sort)(Element*, const int), const int N) {
static const int iterations = 1000000;

Array<Element> data;
Array<Element> src;
Random rnd;

for (int i = 0; i < N; ++i) {
src.append(rnd.integer(0, 20));
}
data.resize(src.size());

RealTime t0 = System::time();
for (int i = 0; i < iterations; ++i) {
// Restore
System::memcpy(data.getCArray(), src.getCArray(), sizeof(Element) * src.size());
sort(data.getCArray(), data.size());
}

return (System::time() - t0) / iterations;
}


int main(const int argc, const char* argv[]) {

static const int N = 40;
Array<Element> data;
Array<Element> src;
Random rnd;

for (int i = 0; i < N; ++i) {
data.append(rnd.integer(0, 30));
}

printf("Before: ");
printArray(data);

// Test:
// insertionSort(data.getCArray(), data.size());
// heapSort(data.getCArray(), data.size());
// mergeSort(data.getCArray(), data.size());
// quickSort(data.getCArray(), data.size());
quickSort2(data.getCArray(), data.size());

printf("After: ");
printArray(data);

// Time test on small arrays
const RealTime nt = timeRoutine(noSort, N);
const RealTime mt = timeRoutine(mergeSort, N) - nt;
const RealTime qt = timeRoutine(quickSort, N) - nt;
const RealTime ht = timeRoutine(heapSort, N) - nt;
const RealTime it = timeRoutine(insertionSort, N) - nt;

printf("Time for %d elements:\n", N);
printf(" insertionSort: %d ns\n", iRound(it / units::nanoseconds()));
printf(" heapSort: %d ns\n", iRound(ht / units::nanoseconds()));
printf(" quickSort: %d ns\n", iRound(qt / units::nanoseconds()));
printf(" mergeSort: %d ns\n", iRound(mt / units::nanoseconds()));

return 0;
}
#endif

0 comments on commit 6bdb6e5

Please sign in to comment.