Skip to content

Commit 1a6ca5b

Browse files
committed
added Shellsort
1 parent b4acb93 commit 1a6ca5b

25 files changed

+912
-5
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package sequential.math
2+
3+
import kotlin.math.ln
4+
import kotlin.math.min
5+
import kotlin.math.round
6+
7+
8+
/**
9+
* Calculates number of 3-smooth numbers till [N].
10+
*
11+
* An alternative formula is also applicable:
12+
* (ln(N) / ln(2.0) + 1) * (ln(N) / ln(3.0) + 1) * 0.5
13+
*
14+
* Note: the result of the function is approximate and may differ from the correct one by +-1 (or +-2 in rear cases).
15+
*
16+
* @see [Ramanujan's First Letter to Hardy and the Number of 3-Smooth Integers](https://math.stackexchange.com/questions/15966/ramanujans-first-letter-to-hardy-and-the-number-of-3-smooth-integers)
17+
* @see [3-Smooth Representations of Integers](https://www.jstor.org/stable/2589404)
18+
*/
19+
fun count3SmoothNumbers(N: Int) =
20+
(ln(2.0 * N) * ln(3.0 * N)) / (2 * ln(2.0) * ln(3.0))
21+
22+
23+
fun list3SmoothNumbers(N: Int): IntArray {
24+
if (N <= 0) return IntArray(size = 0)
25+
26+
val sequenceSize = round(count3SmoothNumbers(N)).toInt() + 1
27+
28+
var numbers = IntArray(sequenceSize)
29+
numbers[0] = 1
30+
var i2 = 0
31+
var i3 = 0
32+
var i = 1
33+
34+
var n2: Int
35+
var n3: Int
36+
var number: Int
37+
while (true) {
38+
n2 = 2 * numbers[i2]
39+
n3 = 3 * numbers[i3]
40+
number = min(n2, n3)
41+
42+
if (number >= N) break
43+
numbers[i++] = number
44+
45+
if (n2 <= n3) i2++
46+
if (n2 >= n3) i3++
47+
}
48+
49+
if (numbers.last() == 0) numbers = numbers.removeTrailingZeros()
50+
51+
return numbers
52+
}
53+
54+
55+
/**
56+
* Utility function that removes trailing zeros from the gap sequence array.
57+
*
58+
* @return copy of the original array without trailing zeros.
59+
*/
60+
private fun IntArray.removeTrailingZeros(): IntArray {
61+
if (last() != 0) return this
62+
63+
var zeroId = lastIndex
64+
while (this[lastIndex - 1] == 0) zeroId--
65+
66+
return copyOfRange(0, zeroId)
67+
}

src/kotlin/sequential/math/BUILD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
exports_files(
2+
["3SmoothNumbers.kt"],
3+
visibility = ["//:__subpackages__"]
4+
)

src/kotlin/sequential/sorting/insertionsort/RangedInsertionSort.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package sequential.sorting.insertionsort
1+
package sequential.sorting.insertionsort.ranged
22

33
import _util.*
44

src/kotlin/sequential/sorting/quicksort/FunctionalQuickSort.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package sequential.sorting.quicksort
1+
package sequential.sorting.quicksort.functional
22

33
import _util.*
44

src/kotlin/sequential/sorting/quicksort/QuickSort.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package sequential.sorting.quicksort
22

3-
import sequential.sorting.insertionsort.insertionSort
4-
import sequential.sorting.quicksort.partition.lomutoPartition
3+
import _util.isSorted
4+
import _util.print
5+
import _util.randomIntArray
6+
import _util.shuffle
7+
import sequential.sorting.insertionsort.ranged.insertionSort
58
import sequential.sorting.quicksort.partition.hoarePartition
6-
import _util.*
9+
import sequential.sorting.quicksort.partition.lomutoPartition
710

811

912
/**

src/kotlin/sequential/sorting/selectionsort/SelectionSort.kt

Whitespace-only changes.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
2+
3+
kt_jvm_library(
4+
name = "shellsort_lib",
5+
srcs = glob([
6+
"*.kt",
7+
"gapsequence/*.kt",
8+
"_measurement/MeasuredShellSort.kt"
9+
]) + [
10+
"//sequential/math:3SmoothNumbers.kt"
11+
],
12+
deps = [
13+
"//sequential/sorting/_measurement:measurement",
14+
"//:utils",
15+
]
16+
)
17+
18+
java_binary(
19+
name = "shellsort",
20+
main_class = "sequential.sorting.shellsort.ShellSortKt",
21+
runtime_deps = [":shellsort_lib"],
22+
)
23+
24+
java_binary(
25+
name = "shellsort_measured",
26+
main_class = "sequential.sorting.shellsort._measurement.MeasuredShellSortKt",
27+
runtime_deps = [":shellsort_lib"],
28+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Shellsort
2+
...
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package sequential.sorting.shellsort
2+
3+
import _util.isSorted
4+
import _util.print
5+
import _util.randomIntArray
6+
import sequential.sorting.shellsort.gapsequence.ShellGapSequence
7+
8+
9+
/**
10+
* Sorts given array using Shellsort.
11+
*/
12+
fun IntArray.shellSort() {
13+
shellSort(ShellGapSequence.compute(size))
14+
}
15+
16+
/**
17+
* Sorts given array with Shellsort using provided gap sequence.
18+
*/
19+
fun IntArray.shellSort(gaps: IntArray) {
20+
for (gap in gaps) {
21+
for (i in gap..lastIndex) {
22+
var j = i
23+
val arri = this[i]
24+
while (j >= gap && arri < this[j - gap]) {
25+
this[j] = this[j - gap]
26+
j -= gap
27+
}
28+
this[j] = arri
29+
}
30+
}
31+
}
32+
33+
34+
fun main() {
35+
val array = randomIntArray(size = 20)
36+
array.print()
37+
38+
array.shellSort()
39+
40+
println("Sorting is successful: ${array.isSorted()}")
41+
array.print()
42+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package sequential.sorting.shellsort._measurement
2+
3+
import _util.isSorted
4+
import _util.print
5+
import _util.randomIntArray
6+
import sequential.sorting._measurement.measured
7+
import sequential.sorting.shellsort.gapsequence.ShellGapSequence
8+
9+
10+
/**
11+
* Sorts given array with Shellsort using provided gap sequence.
12+
*/
13+
fun IntArray.shellSort(gaps: IntArray) = measured {
14+
for (gap in gaps) {
15+
for (i in gap..lastIndex) {
16+
var j = i
17+
val arri = this[i]
18+
while (j >= gap && arri < this[j - gap]) {
19+
this[j] = this[j - gap]
20+
j -= gap
21+
}
22+
this[j] = arri
23+
}
24+
}
25+
}
26+
27+
28+
fun main() {
29+
val array = randomIntArray(size = 20)
30+
array.print()
31+
32+
val gapSequence = ShellGapSequence.compute(array.size)
33+
val measurement = array.shellSort(gapSequence)
34+
35+
println("Sorting is successful: ${array.isSorted()}")
36+
array.print()
37+
38+
println("$measurement")
39+
}

src/kotlin/sequential/sorting/shellsort/_test/ShellSortTest.kt

Whitespace-only changes.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package sequential.sorting.shellsort.gapsequence
2+
3+
import kotlin.math.ceil
4+
import kotlin.math.pow
5+
6+
7+
/**
8+
* @see [Shellsort, 2.48^(k-1) vs Tokuda's sequence](https://stackoverflow.com/questions/21508595/shellsort-2-48k-1-vs-tokudas-sequence)
9+
* @see TokudaGapSequence
10+
*/
11+
object GapSequence248 {
12+
13+
/**
14+
* Precomputed gap sequence for arrays of integer range.
15+
*/
16+
val get = intArrayOf(
17+
1181377402, 476361856, 192081394, 77452175, 31230716, 12593031, 5077835, 2047515,
18+
825611, 332908, 134237, 54128, 21826, 8801, 3549, 1431, 577, 233, 94, 38, 16, 7, 3, 1
19+
)
20+
21+
/**
22+
* Computes gap sequence for the given array size.
23+
*/
24+
fun compute(arraySize: Int): IntArray {
25+
if (arraySize <= 1) return intArrayOf(1)
26+
27+
var k = 1
28+
var gap = 1
29+
while (gap < arraySize) {
30+
gap = computeTerm(k + 1)
31+
k++
32+
}
33+
34+
val gaps = IntArray(--k)
35+
for (i in 0..gaps.lastIndex) {
36+
gaps[i] = computeTerm(k--)
37+
}
38+
39+
return gaps
40+
}
41+
42+
/**
43+
* General term of the gap sequence.
44+
*
45+
* @param k sequence term number (starting from 1).
46+
* @return k-th term of the sequence.
47+
*/
48+
private fun computeTerm(k: Int) =
49+
ceil(
50+
2.48.pow(k - 1)
51+
).toInt()
52+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package sequential.sorting.shellsort.gapsequence
2+
3+
import kotlin.math.ceil
4+
import kotlin.math.floor
5+
6+
7+
/**
8+
* @see CiuraGapSequence
9+
* @see TokudaGapSequence
10+
*/
11+
object CiuraExtendedGapSequence {
12+
13+
/**
14+
* Precomputed gap sequence for arrays of integer range.
15+
*/
16+
val get = intArrayOf(
17+
954437177, 424194301, 188530801, 83791468, 37240653, 16551402, 7356179, 3269413,
18+
1453073, 645811, 287028, 127568, 56697, 25199, 11200, 4978, 2213, 1750, 701, 301, 132,
19+
57, 23, 10, 4, 1
20+
)
21+
22+
/**
23+
* Computes gap sequence for the given array size.
24+
*/
25+
fun compute(arraySize: Int): IntArray {
26+
if (arraySize <= 1) return intArrayOf(1)
27+
28+
val base = CiuraGapSequence.get
29+
if (arraySize <= base[0]) return base
30+
31+
var k = base.size
32+
var gap = base[0]
33+
do {
34+
gap = computeTerm(gap)
35+
k++
36+
} while (gap < arraySize)
37+
38+
val gaps = IntArray(--k)
39+
val extensionSize = gaps.size - base.size
40+
41+
for (i in 0..gaps.lastIndex) {
42+
gaps[i] = if (i < extensionSize) {
43+
gap = computeReverseTerm(gap)
44+
gap
45+
} else base[i - extensionSize]
46+
}
47+
48+
return gaps
49+
}
50+
51+
52+
/**
53+
* General term of the gap sequence.
54+
*
55+
* @param h previous sequence term.
56+
* @return next sequence term.
57+
*/
58+
private fun computeTerm(h: Int) =
59+
floor(h * 2.25).toInt()
60+
61+
/**
62+
* Reverse operation to [computeTerm].
63+
*/
64+
private fun computeReverseTerm(h: Int) =
65+
ceil(h / 2.25).toInt()
66+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package sequential.sorting.shellsort.gapsequence
2+
3+
4+
/**
5+
* Experimentally derived gap sequence by Marcin Ciura (2001).
6+
* Originally the sequence consisted of the first 8 elements but was extended by Roman Dovgopolov
7+
* in 2011 by adding 1750.
8+
*
9+
* @see [Best Increments for the Average Case of Shellsort](https://doi.org/10.1007/3-540-44669-9_12)
10+
*/
11+
object CiuraGapSequence {
12+
13+
/**
14+
* Experimentally derived gap sequence.
15+
*/
16+
val get = intArrayOf(
17+
1750, 701, 301, 132, 57, 23, 10, 4, 1
18+
)
19+
}

0 commit comments

Comments
 (0)