-
Notifications
You must be signed in to change notification settings - Fork 0
/
Factorization.rb
executable file
·241 lines (216 loc) · 5.69 KB
/
Factorization.rb
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# ================================================================
# Please see LICENSE.txt in the same directory as this file.
# John Kerl
# kerl.john.r@gmail.com
# Copyright (c) 2004
# Ported to Ruby 2011-02-10
# ================================================================
class Factorization
# ----------------------------------------------------------------
def initialize
@factors_and_multiplicities = []
@trivial_factor = nil # E.g. -1, 0, 1.
end
attr_reader :trivial_factor
attr_reader :factors_and_multiplicities
def num_distinct_factors # Don't count trivial factors.
@factors_and_multiplicities.length
end
def num_factors # Don't count trivial factors.
@factors_and_multiplicities.inject(0) {|rv, fnm| rv += fnm[1]}
end
def get(i)
@factors_and_multiplicities[i]
end
def get_factor(i)
@factors_and_multiplicities[i][0]
end
def get_multiplicity(i)
@factors_and_multiplicities[i][1]
end
# ----------------------------------------------------------------
def to_s
s = ""
num_printed = 0
if !@trivial_factor.nil?
s << @trivial_factor.to_s
num_printed += 1
end
for factor, multiplicity in @factors_and_multiplicities
if num_printed > 0
s << " "
end
s << factor.to_s
if multiplicity != 1
s << "^" << multiplicity.to_s
end
num_printed+= 1
end
s
end
# ----------------------------------------------------------------
def insert_trivial_factor(new_factor)
if !new_factor.nil?
if !@trivial_factor.nil?
@trivial_factor *= new_factor
else
@trivial_factor = new_factor
end
end
end
# Inserts in sorted order. This makes it easier to unit-test the
# factorization algorithm.
def insert_factor(new_factor, new_multiplicity=1)
m = @factors_and_multiplicities.length
for i in 0..(m-1)
factor, multiplicity = @factors_and_multiplicities[i]
if new_factor == factor
@factors_and_multiplicities[i][1] += new_multiplicity
return
elsif new_factor < factor
@factors_and_multiplicities.insert(i, \
[new_factor, new_multiplicity])
return
end
end
# Append to the end (or, new first element in the previously empty
# list).
@factors_and_multiplicities.push([new_factor, new_multiplicity])
end
def merge(other)
self.insert_trivial_factor(other.trivial_factor)
other.factors_and_multiplicities.each do |fnm|
f, m = fnm
self.insert_factor(f, m)
end
end
def exp_all(e)
if !@trivial_factor.nil?
@trivial_factor **= e
end
@factors_and_multiplicities.each {|fnm| fnm[1] *= e}
end
# ----------------------------------------------------------------
# Given the prime factorization p1^m1 ... pk^mk of n, how to enumerate all
# factors of n?
#
# Example 72 = 2^3 * 3^2. Exponent on 2 is one of 0, 1, 2, 3.
# Exponent on 3 is one of 0, 1, 2. Number of possibilities: product
# over i of (mi + 1). Factors are:
#
# 2^0 3^0 1
# 2^0 3^1 3
# 2^0 3^2 9
# 2^1 3^0 2
# 2^1 3^1 6
# 2^1 3^2 18
# 2^2 3^0 4
# 2^2 3^1 12
# 2^2 3^2 36
# 2^3 3^0 8
# 2^3 3^1 24
# 2^3 3^2 72
def num_divisors
ndf = self.num_distinct_factors
if ndf <= 0
if @trivial_factor.nil?
raise "Factorization.num_divisors: " \
"No factors have been inserted.\n"
end
end
rv = 1
for i in (0..(ndf-1))
rv *= @factors_and_multiplicities[i][1] + 1
end
return rv
end
# ----------------------------------------------------------------
# See comments to num_divisors. k is treated as a multibase
# representation over the bases mi+1. (This may overflow a 32-bit
# integer when factoring something large -- but if something really
# has more than a billion factors, it is impractical to loop over all
# its factors anyway.)
def kth_divisor(k)
ndf = self.num_distinct_factors
if ndf <= 0
if !@trivial_factor.nil?
return @trivial_factor / @trivial_factor # abstract one
else
raise << "Factorization.kth_divisor: "
"No factors have been inserted.\n"
end
end
x = @factors_and_multiplicities[0][0]
rv = x / x # abstract one
for i in (0..(ndf-1))
base = @factors_and_multiplicities[i][1] + 1
power = k % base
k = k / base
rv *= @factors_and_multiplicities[i][0] ** power
end
return rv
end
# ----------------------------------------------------------------
# Returns an array of divisors. The output is sorted from smallest to
# largest.
def all_divisors
ndf = self.num_distinct_factors
if ndf <= 0
if @trivial_factor.nil?
raise "Factorization.all_divisors: " \
"No factors have been inserted.\n"
end
end
nd = self.num_divisors
rv = [0] * nd
for k in (0..(nd-1))
rv[k] = self.kth_divisor(k)
end
rv.sort!
return rv
end
# ----------------------------------------------------------------
# The output is sorted from smallest to largest.
def maximal_proper_divisors
ndf = self.num_distinct_factors
if ndf <= 0
if @trivial_factor.nil?
raise "Factorization.maximal_proper_divisors: " \
"No factors have been inserted.\n"
else
return []
end
end
save_trivial_factor = @trivial_factor
@trivial_factor = nil
n = self.unfactor
rv = [0] * ndf
for k in (0..(ndf-1))
rv[k] = n / @factors_and_multiplicities[k][0]
end
rv.sort!
@trivial_factor = save_trivial_factor
return rv
end
# ----------------------------------------------------------------
def unfactor
ndf = self.num_distinct_factors
if ndf <= 0
if @trivial_factor.nil?
raise "Factorization.maximal_proper_divisors: " \
"No factors have been inserted.\n"
else
return @trivial_factor
end
end
x = @factors_and_multiplicities[0][0]
rv = x/x
if !@trivial_factor.nil?
rv *= @trivial_factor
end
for p,e in @factors_and_multiplicities
rv *= p**e
end
return rv
end
end