mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-18 03:12:10 +00:00
Merge pull request #7349 from TheRadischen/merge_sort
improved stable sort | from insertion_sort to merge_rotate
This commit is contained in:
@@ -25,15 +25,104 @@ _stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (O
|
||||
#panic("unhandled Sort_Kind")
|
||||
}
|
||||
}
|
||||
|
||||
// insertion sort
|
||||
// TODO(bill): use a different algorithm as insertion sort is O(n^2)
|
||||
n := len(data)
|
||||
for i in 1..<n {
|
||||
for j := i; j > 0 && less(data[j], data[j-1], call); j -= 1 {
|
||||
swap(data, j, j-1)
|
||||
|
||||
merge_rotate(data, call)
|
||||
|
||||
insertion_sort :: proc(data: T, call: P) {
|
||||
for i in 1..<len(data) {
|
||||
j := i
|
||||
temp := data[j]
|
||||
for ; j > 0 && less(temp, data[j - 1], call); j -= 1 {
|
||||
data[j] = data[j - 1]
|
||||
}
|
||||
data[j] = temp
|
||||
}
|
||||
}
|
||||
|
||||
merge_rotate :: proc(data: T, call: P) {
|
||||
if len(data) <= 200 {
|
||||
insertion_sort(data, call)
|
||||
return
|
||||
}
|
||||
|
||||
mid := len(data) / 2
|
||||
merge_rotate(data[:mid], call)
|
||||
merge_rotate(data[mid:], call)
|
||||
|
||||
merge(data, mid, len(data) - mid, call)
|
||||
}
|
||||
|
||||
bin_search_left :: proc(data: T, value: E, call: P) -> (from: int) {
|
||||
n := len(data)
|
||||
|
||||
for n > 0 {
|
||||
half := n / 2
|
||||
mid := from + half
|
||||
|
||||
if less(data[mid], value, call) {
|
||||
from = mid + 1
|
||||
n -= half + 1
|
||||
} else {
|
||||
n = half
|
||||
}
|
||||
}
|
||||
|
||||
return from
|
||||
}
|
||||
|
||||
bin_search_right :: proc(data: T, value: E, call: P) -> (from: int) {
|
||||
n := len(data)
|
||||
|
||||
for n > 0 {
|
||||
half := n / 2
|
||||
mid := from + half
|
||||
|
||||
if less(value, data[mid], call) {
|
||||
n = half
|
||||
} else {
|
||||
from = mid + 1
|
||||
n -= half + 1
|
||||
}
|
||||
}
|
||||
|
||||
return from
|
||||
}
|
||||
|
||||
merge :: proc(data: T, left, right: int, call: P) {
|
||||
if left == 0 || right == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if left + right == 2 {
|
||||
if less(data[1], data[0], call) {
|
||||
data[1], data[0] = data[0], data[1]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
first_cut, second_cut: int
|
||||
left2, right2: int
|
||||
|
||||
if left > right {
|
||||
left2 = left / 2
|
||||
first_cut = left2
|
||||
|
||||
second_cut = left + bin_search_left(data[left:], data[first_cut], call)
|
||||
right2 = second_cut - left
|
||||
} else {
|
||||
right2 = right / 2
|
||||
second_cut = left + right2
|
||||
|
||||
first_cut = bin_search_right(data[:left], data[second_cut], call)
|
||||
left2 = first_cut
|
||||
}
|
||||
|
||||
rotate_left(data[first_cut:second_cut], left - first_cut)
|
||||
new_mid := first_cut + right2
|
||||
|
||||
merge(data[:new_mid], left2 , right2 , call)
|
||||
merge(data[new_mid:], left - left2, right - right2, call)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
@@ -140,6 +140,52 @@ test_sort_by_indices :: proc(t: ^testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
@test
|
||||
test_sort_stability :: proc(t: ^testing.T) {
|
||||
// Test sizes are all prime.
|
||||
test_sizes :: []int{7, 13, 347, 1031, 10111, 100003}
|
||||
Data :: struct {
|
||||
rand: int,
|
||||
index: int,
|
||||
}
|
||||
|
||||
for test_size in test_sizes {
|
||||
rand.reset(t.seed)
|
||||
|
||||
vals := make([]Data, test_size)
|
||||
defer delete(vals)
|
||||
|
||||
// Set up test values
|
||||
for &val, i in vals {
|
||||
val = {rand.int_max(10), i}
|
||||
}
|
||||
|
||||
// Sort
|
||||
slice.stable_sort_by(vals, proc(l, r: Data) -> bool {return l.rand < r.rand})
|
||||
|
||||
// Verify sorted test values
|
||||
rand.reset(t.seed)
|
||||
|
||||
sum := vals[0].index
|
||||
for i in 1..<len(vals) {
|
||||
sum += vals[i].index
|
||||
if vals[i - 1].rand > vals[i].rand {
|
||||
testing.expect(t, false, "Expected slice to be sorted")
|
||||
}
|
||||
if vals[i - 1].rand < vals[i].rand {
|
||||
continue
|
||||
}
|
||||
if vals[i - 1].index > vals[i].index {
|
||||
testing.expect(t, false, "Expected slice to be stable")
|
||||
}
|
||||
}
|
||||
|
||||
testing.expect(t, sum == test_size * (test_size - 1) / 2, "Expected slice to have all indecies")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@test
|
||||
test_binary_search :: proc(t: ^testing.T) {
|
||||
index: int
|
||||
|
||||
Reference in New Issue
Block a user