-
Notifications
You must be signed in to change notification settings - Fork 109
/
FactorialAdvanced.kt
137 lines (122 loc) · 5.05 KB
/
FactorialAdvanced.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package other
import java.math.BigInteger
/**
*
* This algorithm is taken from Google Guava library
*
*/
class FactorialAdvanced {
// precomputed factorials
private val factorials = longArrayOf(
1L,
1L,
1L * 2,
1L * 2 * 3,
1L * 2 * 3 * 4,
1L * 2 * 3 * 4 * 5,
1L * 2 * 3 * 4 * 5 * 6,
1L * 2 * 3 * 4 * 5 * 6 * 7,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19,
1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19 * 20
)
fun compute(n: Int): BigInteger {
if (n <= 0) {
return BigInteger.ZERO
}
if (n < factorials.size) {
return BigInteger.valueOf(factorials[n])
}
// pre-allocate space for our list of intermediate BigIntegers.
val approxSize = divide(n * log2Celling(n), Long.SIZE_BITS)
val bigNumbers = ArrayList<BigInteger>(approxSize)
// start from the pre-computed maximum long factorial.
val startingNumber = factorials.size
var number = factorials[startingNumber - 1]
// strip off 2s from this value.
var shift = number.countTrailingZeroBits()
number = number shr shift
// use floor(log2(num)) + 1 to prevent overflow of multiplication.
var numberBits = log2Floor(number) + 1
var bits = log2Floor(startingNumber.toLong()) + 1
// check for the next power of two boundary, to save us a CLZ operation.
var nextPowerOfTwo = 1 shl bits - 1
// iteratively multiply the longs as big as they can go.
for (num in startingNumber..n) {
// check to see if the floor(log2(num)) + 1 has changed.
if ((num and nextPowerOfTwo) != 0) {
nextPowerOfTwo = nextPowerOfTwo shl 1
bits++
}
// get rid of the 2s in num.
val tz = num.toLong().countTrailingZeroBits()
val normalizedNum = (num shr tz).toLong()
shift += tz
// adjust floor(log2(num)) + 1.
val normalizedBits = bits - tz
// if it won't fit in a long, then we store off the intermediate product.
if (normalizedBits + numberBits >= Long.SIZE_BITS) {
bigNumbers.add(BigInteger.valueOf(number))
number = 1
numberBits = 0
}
number *= normalizedNum
numberBits = log2Floor(number) + 1
}
// check for leftovers.
if (number > 1) {
bigNumbers.add(BigInteger.valueOf(number))
}
// efficiently multiply all the intermediate products together.
return listNumbers(bigNumbers).shiftLeft(shift)
}
/**
* Returns the result of dividing p by q, rounding using the celling
*
* @throws ArithmeticException if q == 0
*/
private fun divide(number: Int, divider: Int): Int {
if (divider == 0) {
throw ArithmeticException("/ by zero") // for GWT
}
val div = number / divider
val rem = number - divider * div // equal to number % divider
if (rem == 0) {
return div
}
val signedNumber = 1 or (number xor divider shr Int.SIZE_BITS - 1)
val increment = signedNumber > 0
return if (increment) div + signedNumber else div
}
private fun listNumbers(numbers: List<BigInteger>, start: Int = 0, end: Int = numbers.size): BigInteger {
return when (end - start) {
0 -> BigInteger.ONE
1 -> numbers[start]
2 -> numbers[start].multiply(numbers[start + 1])
3 -> numbers[start].multiply(numbers[start + 1]).multiply(numbers[start + 2])
else -> {
// otherwise, split the list in half and recursively do this.
val m = end + start ushr 1
listNumbers(numbers, start, m).multiply(listNumbers(numbers, m, end))
}
}
}
// returns the base-2 logarithm of number, rounded according to the celling.
private fun log2Celling(number: Int): Int {
return Int.SIZE_BITS - (number - 1).countLeadingZeroBits()
}
// returns the base-2 logarithm of number rounded according to the floor.
private fun log2Floor(number: Long): Int {
return Long.SIZE_BITS - 1 - number.countLeadingZeroBits()
}
}