[QUIZ: Solution] Banned Words #9

M

Michael Ulm

Another lunchbreak used for debugging, but I present my
official solution to the Quiz #9. Here is some sample
output

15 words of 1000 found after 135 calls (expected 96.4228550642991)
21 words of 1000 found after 169 calls (expected 164.313332333478)
24 words of 1000 found after 186 calls (expected 222.183614864463)
39 words of 1000 found after 280 calls (expected 273.874191108301)
50 words of 1000 found after 329 calls (expected 319.623421186338)
67 words of 1000 found after 389 calls (expected 362.608933077592)
62 words of 1000 found after 393 calls (expected 402.75735262937)
80 words of 1000 found after 435 calls (expected 438.254450457805)
92 words of 1000 found after 484 calls (expected 472.825290910606)
94 words of 1000 found after 487 calls (expected 506.505348749998)


9 words of 1000 found after 87 calls (expected 96.4228550642991)
16 words of 1000 found after 143 calls (expected 164.313332333478)
23 words of 1000 found after 190 calls (expected 222.183614864463)
41 words of 1000 found after 276 calls (expected 273.874191108301)
34 words of 1000 found after 252 calls (expected 319.623421186338)
79 words of 1000 found after 443 calls (expected 362.608933077592)
60 words of 1000 found after 378 calls (expected 402.75735262937)
64 words of 1000 found after 398 calls (expected 438.254450457805)
75 words of 1000 found after 422 calls (expected 472.825290910606)
105 words of 1000 found after 537 calls (expected 506.505348749998)

=============Proposed Solution to Quiz #9=============
# this holds classes and methods to "solve"
# ruby-talk quiz #9: Banned Words
#
#
# creates the language filter
class LanguageFilter
def initialize(size, prob)
@size = size
if prob <= 0 or prob >= 1 # sanity check
throw "illegal probability in LanguageFilter::initialize"
end

@badWords = []
@size.times {|k| @badWords.push(k) if rand < prob}
@Count = 0
end

def countReset
@Count = 0
end

def getBad
@badWords
end

# checks if one of the numbers in the array testText is on the
# badWords list
def clean?(testText)
sortedClean?(testText.sort)
end

# checks if one of the numbers in the array testText is on the
# badWords list testText is assumed to be sorted
def sortedClean?(testText)
@Count += 1
binStart, binEnd = 0, @badWords.size
testText.each do |current|
binStart = binarySearch(current, binStart, binEnd)
return false if @badWords[binStart] == current
end
return true
end

def test(testWords)
[@count, testWords.sort == @badWords]
end

private

def binarySearch(what, searchStart, searchEnd)
return searchEnd if what > @badWords[searchEnd - 1]
while searchEnd - searchStart > 1
testSearch = (searchStart + searchEnd) / 2
testWord = @badWords[testSearch]
return testSearch if testWord == what
if testWord < what
searchStart = testSearch
else
searchEnd = testSearch
end
end
return searchStart
end
end

class QueryStrategy
def initialize(prob)
@prob = prob
@qrob = 1.0 - prob
@eNone = [0, 1] # expected value for interval without additional info
@eOne = [0, 0] # expected values for interval with at least one bad
word
@kNone = [0, 1] # optimal interval query length in interval without
info
@kOne = [0, 0] # optimal interval query length in interval with one
bad word
@size = 2
@qToN = [1, @qrob]
end

def getStrategy(size, noInfo = true, quick = false)
if size < @size
return noInfo ? [@kNone[size], @eNone[size]] : [@kOne[size],
@eOne[size]]
end

@size.upto(size) {|k| @qToN[k] = @qToN[k - 1] * @qrob}

@size.upto(size) do |n|
# compute F_p(n)
minExp = n.to_f
minK = 1
1.upto(n - 1) do |k|
thisExp = (@qToN[k] - @qToN[n]) * @eOne[n - k] + (1 - @qToN[k])
* (@eOne[k] + @eNone[n - k])
if thisExp < minExp
minK, minExp = k, thisExp
end
end
@kOne[n] = minK
@eOne[n] = 1.0 + minExp / (1 - @qToN[n])

# compute E_p(n)
minExp = n.to_f
minK = 1
1.upto(n) do |k|
thisExp = @eNone[n - k] + (1 - @qToN[k]) * @eOne[k]
if thisExp < minExp
minK, minExp = k, thisExp
end
end
@kNone[n] = minK
@eNone[n] = 1 + minExp
end

@size = size + 1;
return noInfo ? [@kNone[size], @eNone[size]] : [@kOne[size],
@eOne[size]]
end

def findWords(filter, size)
@myWords = []
getWordsRecursively(filter, 0...size, true)
@myWords
end

private
def getWordsRecursively(filter, range, noInfo)
rangesize = range.end - range.begin
return if rangesize == 0
if rangesize == 1
if noInfo
@myWords.push(range.begin) unless
filter.sortedClean?([range.begin])
else
@myWords.push(range.begin)
end
else
thisStrat = getStrategy(rangesize, noInfo)
testRange = range.begin...(range.begin + thisStrat[0])
testArray = []
testRange.each {|k| testArray.push(k)}
if filter.sortedClean?(testArray)
getWordsRecursively(filter, (range.begin +
thisStrat[0])...range.end, noInfo)
else
getWordsRecursively(filter, testRange, false)
getWordsRecursively(filter, (range.begin +
thisStrat[0])...range.end, true)
end
end
end
end

# test
testsize = 1000
10.times do |level|
testprob = 0.01 * (level + 1)

myfilt = LanguageFilter.new(testsize, testprob)

strat = QueryStrategy.new(testprob)
testWords = strat.findWords(myfilt, testsize)
number, found = myfilt.test(testWords)
if found
puts "#{testWords.size} words of #{testsize} found after #{number}
calls (expected #{strat.getStrategy(testsize)[1]})"
else
puts "word list not found after #{number} calls"
end
end

--
Michael Ulm
R&D Team
ISIS Information Systems Austria
tel: +43 2236 27551-219, fax: +43 2236 21081
e-mail: (e-mail address removed)
Visit our Website: www.isis-papyrus.com
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top