Compare commits

...

3 Commits

Author SHA1 Message Date
c6e671c9c5 better docs 2021-01-07 22:12:30 +01:00
0cdf63bec1 docs 2021-01-07 14:56:58 +01:00
a26c4fc460 comments 2021-01-02 17:39:22 +01:00
3 changed files with 104 additions and 40 deletions

View File

@ -6,9 +6,12 @@ import de.redstoneunion.git.MrGeorgen.timsort.timsort;
public class test {
public static void main(String[] args) {
int[] array = {0, 3, 4, 8, -6, -6, 10, 10, 789, 456, 0, -1, -2, 8, 10, 1, 100, -100, 700, 800, 5};
int[] output = timsort.insertionsort(array);
System.out.println("Insertionsort test: " + Arrays.toString(output));
System.out.println("End result: " + Arrays.toString(timsort.timsort(array)));
long startTime = System.nanoTime();
int[] test = timsort.timsort(new int[]{20, 1, 573, 34, 281, 201, 0});
long endTime = System.nanoTime();
System.out.println(Arrays.toString(test));
long duration = (endTime - startTime);
System.out.println("Program executed in " + duration + " nanoseconds.");
}
}

View File

@ -4,21 +4,21 @@ import java.util.ArrayList;
import java.util.Arrays;
public class timsort {
public static int[] insertionsort(int[] unsorted) {
private static int[] insertionsort(int[] unsorted) {
int[] output = new int[unsorted.length];
output[0] = unsorted[0];
output[0] = unsorted[0]; // copies the first element
for(int i = 1; i < unsorted.length; ++i) {
int foundIndex = binarySearch(output, unsorted[i], 0, i);
for(int j = i; j > foundIndex; --j) {
int foundIndex = binarySearch(output, unsorted[i], 0, i); // searches the right index using binary search
for(int j = i; j > foundIndex; --j) { // shift each element after the found index one to the right
output[j] = output[j - 1];
}
output[foundIndex] = unsorted[i];
output[foundIndex] = unsorted[i]; // insert the element
}
return output;
}
private static int binarySearch(int[] array, int search, int minIndex, int maxIndex) {
for(int j = maxIndex - 1; minIndex != maxIndex; j = minIndex + (maxIndex - minIndex) / 2) {
if(search >= array[j]) {
for(int j = maxIndex - 1; minIndex != maxIndex; j = minIndex + (maxIndex - minIndex) / 2 /* set j to middle between min and max Index */) {
if(search >= array[j]) {// check if the element is larger and set min or max Index accordingly
minIndex = j + 1;
}
else {
@ -28,6 +28,7 @@ public class timsort {
return minIndex;
}
public static int[] timsort(int[] unsorted) {
// calculate minrun
int r = 0;
// r becomes 1 any 1 bits are shifted off
int n = unsorted.length;
@ -36,12 +37,14 @@ public class timsort {
n >>= 1;
}
int minRun = n + r;
// split array into runs
ArrayList<int[]> runs = new ArrayList<int[]>();
for(int i = 0; i < unsorted.length - 1;) {
int j = i;
boolean ascending = true;
boolean descending = true;
boolean ascendingCache = false;
boolean ascendingCache = false; // cache variables are needed to know if it is ascending or descending, both ascending and descending are false after the loop
boolean descendingCache = false;
for(; i < unsorted.length - 1 && (ascending || descending); ++i) {
ascendingCache = ascending;
@ -49,11 +52,11 @@ public class timsort {
ascending = ascending && unsorted[i] <= unsorted[i + 1];
descending = descending && unsorted[i] >= unsorted[i + 1];
}
if(i - j < minRun) i = j + minRun;
boolean minRunNotMet = i >= unsorted.length - 1;
if(minRunNotMet) i = unsorted.length;
int[] temp1 = Arrays.copyOfRange(unsorted, j, i);
if(!ascendingCache && descendingCache && !minRunNotMet) {
boolean minRunNotMet = i - j < minRun;
if(minRunNotMet) i = j + minRun; // if minrun is not met enlarge the array range
if(i > unsorted.length) i = unsorted.length; // if the Index is out of Bounds set to the maxium.
int[] temp1 = Arrays.copyOfRange(unsorted, j, i); // the index same as the length is not out of Bounds because copyOfRange is excluding the last index
if(!ascendingCache && descendingCache && !minRunNotMet) { // if the array is descending and not ascending (if it is both all values are equal) and minrun is met (if it is not met the run is not ascending nor descending because of the enlargment of the run
// reverses the array
int l, temp;
for(l = 0; l < temp1.length / 2; l++) {
@ -64,43 +67,45 @@ public class timsort {
}
runs.add(temp1);
}
int[][] notMerged = new int[runs.size()][];
for(int i = 0; i < runs.size(); ++i) {
for(int i = 0; i < runs.size(); ++i) { // sort each runs with insertionsort
notMerged[i] = insertionsort(runs.get(i));
}
return merge(notMerged)[0];
}
private static int[][] merge(int[][] notMerged) {
int notEvenLen = notMerged.length % 2;
int[][] merged = new int[notMerged.length / 2 + notEvenLen][];
for(int i = 0; i < notMerged.length - 1; i += 2) {
int[] temp = new int[notMerged[i].length + notMerged[i + 1].length];
int[][] merged = new int[notMerged.length / 2 + notEvenLen][]; // the merged array is half as the unmerged. if the length of notMerged is not even we have to add one because the one remaining arary will just be copied
for(int i = 0; i < notMerged.length - 1; /* -1 becasue index of i + 1 is used later */ i += 2) {
int[] temp = new int[notMerged[i].length + notMerged[i + 1].length]; // length of the merged is the sum of the 2 not merged arrays
merged[i / 2] = temp;
int[] j = new int[2];
int w = 0;
int[] j = new int[2]; // index variable: [0]: notMerged[i], [1]: notMerged[i + 1]
int relativeIndexOfSmallerArray = 0;
int l = 0;
for(int gallopingCounter = 0; j[w] < notMerged[i + w].length; ++l) {
if(gallopingCounter == 7) {
int b = w != 0 ? 0 : 1;
int found = binarySearch(notMerged[i + w], notMerged[i + b][j[b]], j[w], notMerged[i + w].length - 1);
for(; j[w] < found; ++j[w], ++l) {
temp[l] = notMerged[i + w][j[w]];
for(int gallopingCounter = 0; j[relativeIndexOfSmallerArray] < notMerged[i + relativeIndexOfSmallerArray].length; ++l) {
if(gallopingCounter == 7) { // do galloping if 7 elements in a row came from one array
int relativeIndexOfLargerArray = relativeIndexOfSmallerArray != 0 ? 0 : 1;
int found = binarySearch(notMerged[i + relativeIndexOfSmallerArray], notMerged[i + relativeIndexOfLargerArray][j[relativeIndexOfLargerArray]], j[relativeIndexOfSmallerArray], notMerged[i + relativeIndexOfSmallerArray].length - 1); // use binray search to out how many elements can be copied from the same array
for(; j[relativeIndexOfSmallerArray] < found; ++j[relativeIndexOfSmallerArray], ++l) { // copy elements
temp[l] = notMerged[i + relativeIndexOfSmallerArray][j[relativeIndexOfSmallerArray]];
}
}
int lastW = w;
w = notMerged[i][j[0]] > notMerged[i + 1][j[1]] ? 1 : 0;
temp[l] = notMerged[i + w][j[w]];
++j[w];
if(w == lastW) ++gallopingCounter;
int lastRelativeIndexOfSmallerArray = relativeIndexOfSmallerArray;
relativeIndexOfSmallerArray = notMerged[i][j[0]] > notMerged[i + 1][j[1]] ? 1 : 0;
temp[l] = notMerged[i + relativeIndexOfSmallerArray][j[relativeIndexOfSmallerArray]]; // copy the element to the output array
++j[relativeIndexOfSmallerArray];
if(relativeIndexOfSmallerArray == lastRelativeIndexOfSmallerArray ) ++gallopingCounter;
else gallopingCounter = 0;
}
int b = w != 0 ? 0 : 1;
for(int k = j[b]; l < temp.length; ++k, ++l) {
temp[l] = notMerged[i + b][k];
// after one of the not merged arrays is empty the remaining elements from the other arrays have to be copied
int relativeIndexOfLargerArray = relativeIndexOfSmallerArray != 0 ? 0 : 1;
for(int k = j[relativeIndexOfLargerArray]; l < temp.length; ++k, ++l) {
temp[l] = notMerged[i + relativeIndexOfLargerArray][k];
}
}
if(notEvenLen == 1) merged[merged.length - 1] = notMerged[notMerged.length - 1];
if(merged.length == 1) return merged;
return merge(merged);
if(notEvenLen == 1) merged[merged.length - 1] = notMerged[notMerged.length - 1]; // copy the last array if input array length is uneven so array could not be merged
if(merged.length == 1) return merged; // sorting is finnished
return merge(merged); // do merging again
}
}

56
timsort.md Normal file
View File

@ -0,0 +1,56 @@
# Timsort
- Hybrid aus Insertion und Mergesort
- weniger als 64 Elemente: Insertionsort
## Insertionsort
- erste Element des unsortierten Arrays in das sortierte Array kopiert
- Danach wird geschaut, ob das zweite Element größer als das letzte Element im sortierten Array ist. Dann wird es rechts davon einsortiert
- Ansonsten wird der Index vom zu überprüfenden Element halbiert und von vorne begonnen
- In der Mitte des eingegrenzten Bereiches wird überprüft, ob das Element größer oder kleiner ist, so wird der mögliche Bereich mit jedem Mal halbiert
- Nachdem die richtige Stelle gefunden wurde, wird die Zahl dort eingeschoben
## Runs
- mehr als 64 Elemente
- Das unsortierte Array wird in Runs aufgeteilt
- jeder Runs hat eine Mindestgröße, den minrun, normlerweise zwischen 32 und 64
- minrun wird so audgewählt, dass die Anzahl der Runs etwas weniger als eine 2er-Potenz
- viele Daten enthalten bereits sortierte Teile
- Aufsteigende und aufsteigende Reihe werden erkannt
- absteigende Reihen werden umgekehrt
- jeder Run wird mit Insertionsort sortiert
## Merge
- jeweiles zwei Arrays werden gemerged
- Überprüfung am Anfang welches Arrays die Zahl kleiner ist
- kopieren der Zahl in das sortierte Array und lösche der Zahl im Ursprungsarray
- nach einem Durchgang nur noch die Hälfte der Arrays
- wiederholen bis alle Zahlen im einen Array sind
### Galloping
- wenn 7 mal in Folge die kleinere Zahl im selben Array ist
- wird nach der Zahl des anderen Arrays, welche als nächstes einsortiert wird, im ersten Array gesucht
- alles von der aktuellen Position bis zur gefundenen Position des ersten Arrays wird in das sortierte Array kopiert
## Quellen
https://www.youtube.com/watch?v=_dlzWEJoU7I
https://en.wikipedia.org/wiki/Timsort
https://en.wikipedia.org/wiki/Exponential_search
https://www.geeksforgeeks.org/timsort/
### Bilder
https://media.geeksforgeeks.org/wp-content/uploads/insertionsort.png
https://upload.wikimedia.org/wikipedia/commons/6/63/Selection_of_minrun_by_timsort.png
https://upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Merge_sort_algorithm_diagram.svg/1064px-Merge_sort_algorithm_diagram.svg.png
https://i.ytimg.com/vi/ZVzd_Y1Gdbg/maxresdefault.jpg
https://upload.wikimedia.org/wikipedia/commons/2/2d/Galloping_mode_timsort.png
## Code
https://git.redstoneunion.de/MrGeorgen/timsort