-
Notifications
You must be signed in to change notification settings - Fork 2
/
markov.lua
153 lines (130 loc) · 4.72 KB
/
markov.lua
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
local Markov = {}
Markov.data = {}
Markov.starters = {}
Markov.enders = {}
Markov.totals = {
words = {},
starters = 0,
enders = 0
}
function Markov:Learn(str)
local wordArray = {}
for word in str:gmatch("(%S+)") do
wordArray[#wordArray + 1] = word
end
if wordArray[1] then
self.starters[wordArray[1]] = self.starters[wordArray[1]] or 0
self.starters[wordArray[1]] = self.starters[wordArray[1]] + 1
self.totals.starters = self.totals.starters or 0
self.totals.starters = self.totals.starters + 1
end
if wordArray[#wordArray] then
self.enders[wordArray[#wordArray]] = self.enders[wordArray[#wordArray]] or 0
self.enders[wordArray[#wordArray]] = self.enders[wordArray[#wordArray]] + 1
self.totals.enders = self.totals.enders or 0
self.totals.enders = self.totals.enders + 1
end
for i,word in ipairs(wordArray) do
local nextWord = wordArray[i+1]
local nextNextWord = wordArray[i+2]
if nextWord and nextNextWord then
self.data[word] = self.data[word] or {}
self.totals.words[word] = self.totals.words[word] or {
[0] = 0
}
if not self.data[word][nextWord] then
self.data[word][nextWord] = {}
self.totals.words[word][0] = self.totals.words[word][0] + 1
end
self.data[word][nextWord][nextNextWord] = self.data[word][nextWord][nextNextWord] or 0
self.data[word][nextWord][nextNextWord] = self.data[word][nextWord][nextNextWord] + 1
self.totals.words[word][nextWord] = self.totals.words[word][nextWord] or 0
self.totals.words[word][nextWord] = self.totals.words[word][nextWord] + 1
end
end
end
function Markov:getNextWord(wordArray)
local lastLastWord = wordArray[#wordArray-1]
local lastWord = wordArray[#wordArray]
--[[print("======")]]
for i,v in ipairs(wordArray) do --[[print(i,v)]] end
--[[print("")]]
if not lastWord then
--[[print("getNextWord failed: [1] ("..tostring(lastLastWord)..", "..tostring(lastWord)..")")]]
return false
end
if not lastLastWord then
if not self.data[lastWord] then
--[[print("getNextWord failed: [2] ("..tostring(lastLastWord)..", "..tostring(lastWord)..")")]]
return false
end
local total = self.totals.words[lastWord][0]
local target,val = math.random(1,total),1
for nextWord,_ in pairs(self.data[lastWord]) do
if (val >= target) then return nextWord end
val = val + 1
end
--[[print("getNextWord failed: [3] ("..tostring(lastLastWord)..", "..tostring(lastWord)..")")]]
return false
end
if self.data[lastLastWord] and self.data[lastLastWord][lastWord] then
local total = self.totals.words[lastLastWord][lastWord] or 0
if total == 0 then
--[[print("getNextWord failed: [4] ("..tostring(lastLastWord)..", "..tostring(lastWord)..")")]]
return false
end
local target,val = math.random(1,total),1
for nextWord,hits in pairs(self.data[lastLastWord][lastWord]) do
--[[print(nextWord,val,val+hits,target,total)]]
if (val <= target) and ((val + hits) > target) then return nextWord end
val = val + hits
end
end
--[[print("getNextWord failed: [5] ("..tostring(lastLastWord)..", "..tostring(lastWord)..")")]]
return false
end
function Markov:getStarterWord()
local total = self.totals.starters
if total == 0 then return false end
local target,val = math.random(1,total),0
for word,hits in pairs(self.starters) do
if (val <= target) and ((val + hits) >= target) then return word end
val = val + hits
end
end
function Markov:getEndingProbability(word)
if not self.enders[word] then return 0 end
local total = self.totals.enders
return self.enders[word]/total
end
function Markov:Generate(start,length)
local wordArray = {}
if start then
for word in start:gmatch("(%S+)") do
wordArray[#wordArray+1] = word
end
else
wordArray = {self:getStarterWord()}
end
if length then
for i=1,length do
local nextWord = self:getNextWord(wordArray)
if nextWord then
wordArray[#wordArray+1] = nextWord
else
break
end
end
else
while true do
local nextWord = self:getNextWord(wordArray)
if nextWord then
wordArray[#wordArray+1] = nextWord
else
break
end
end
end
return table.concat(wordArray," ")
end
return Markov