# \[QUIZ\] Panagrams (#86)

**URL:** <https://rubytalk.org/t/quiz-panagrams-86/28732>\
**Category:** ruby-talk\
**Created:** [7 July 2006 15:01 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732 "2006-07-07T15:01:35Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![James\_Edward\_Gray\_II](https://avatars.discourse-cdn.com/v4/letter/j/ea5d25/32.png) [@James\_Edward\_Gray\_II](https://rubytalk.org/u/James_Edward_Gray_II)\
**Post date:** [7 July 2006 15:01 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/1 "2006-07-07T15:01:35Z")

</div>

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until  
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

[http://www.rubyquiz.com/](http://www.rubyquiz.com/)

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone  
on Ruby Talk follow the discussion. Please reply to the original quiz message,  
if you can.

> **···**
>
> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
> 
> by Darren Kirby
> 
> One thing that interests me are word puzzles and language oddities. One such  
> example is the self-documenting panagram. If a panagram is a sentence that uses  
> every letter in the alphabet, then a self-documenting panagram is a sentence  
> that enumerates its own letter count. Simple enough, but what if we state that  
> the letter count must be spelled ie: 'twenty-seven' instead of '27'. Now we  
> have a challenge.
> 
> A while back I wrote a script in Python that finds these sentences. Today I  
> rewrote it in Ruby and it found me this sentence:
> 
> &nbsp;&nbsp;Darren's ruby panagram program found this sentence which contains exactly  
> &nbsp;&nbsp;nine 'a's, two 'b's, five 'c's, four 'd's, thirty-five 'e's, nine 'f's,  
> &nbsp;&nbsp;three 'g's, nine 'h's, sixteen 'i's, one 'j', one 'k', two 'l's, three 'm's,  
> &nbsp;&nbsp;twenty-seven 'n's, fourteen 'o's, three 'p's, one 'q', fifteen 'r's,  
> &nbsp;&nbsp;thirty-four 's's, twenty-two 't's, six 'u's, six 'v's, seven 'w's, six 'x's,  
> &nbsp;&nbsp;seven 'y's, and one 'z'.
> 
> My script does have its problems, and I would love to see what kind of code the  
> Ruby experts could come up with to find self-documenting panagrams.
> 
> There is a lot more info on self-documenting panagrams at this address:
> 
> &nbsp;&nbsp;[http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt](http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt)

---

<div class="post-metadata">

**Author:** ![Matthew\_D\_Moss](https://avatars.discourse-cdn.com/v4/letter/m/b9e5f3/32.png) [@Matthew\_D\_Moss](https://rubytalk.org/u/Matthew_D_Moss)\
**Post date:** [7 July 2006 15:13 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/2 "2006-07-07T15:13:22Z")

</div>

So is it "pangram" or "panagram"?

> **···**
>
> > There is a lot more info on self-documenting panagrams at this address:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt](http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt)

---

<div class="post-metadata">

**Author:** ![Brian\_Mattern](https://avatars.discourse-cdn.com/v4/letter/b/47e85d/32.png) [@Brian\_Mattern](https://rubytalk.org/u/Brian_Mattern)\
**Post date:** [7 July 2006 17:34 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/3 "2006-07-07T17:34:23Z")

</div>

Is it a spoiler if we post a resulting pangram (no code)?

I got one in:  
real 1m17.089s  
user 1m8.444s  
sys 0m2.492s

on an athlon 64 300+

Brian

---

<div class="post-metadata">

**Author:** ![Mat\_Schaffer](https://avatars.discourse-cdn.com/v4/letter/m/71e660/32.png) [@Mat\_Schaffer](https://rubytalk.org/u/Mat_Schaffer)\
**Post date:** [9 July 2006 05:51 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/4 "2006-07-09T05:51:19Z")

</div>

Thank the lord, I can go to sleep now:

This terribly inefficient pangram contains five a's, two b's, three c's, two d's, thirty-one e's, six f's, four g's, ten h's, sixteen i's, one j, one k, three l's, two m's, twenty n's, thirteen o's, two p's, one q, twelve r's, twenty-eight s's, twenty-eight t's, three u's, three v's, nine w's, four x's, six y's and one z.

Sadly I wasn't timing it when it ran. But I'd estimate it took 5 minutes or so. Can't wait to see how the rest of the group is getting the time so low.  
-Mat

---

<div class="post-metadata">

**Author:** ![Mat\_Schaffer](https://avatars.discourse-cdn.com/v4/letter/m/71e660/32.png) [@Mat\_Schaffer](https://rubytalk.org/u/Mat_Schaffer)\
**Post date:** [9 July 2006 18:43 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/5 "2006-07-09T18:43:53Z")

</div>

This isn't much to look at, but it appears to be \> 48 hours since the posting. So I thought I'd post it anyway. I still can't generate the example pangrams given at [http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt](http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt) but it does generate this pangram in 1-5 minutes:

This terribly inefficient pangram contains five a's, two b's, three c's, two d's, thirty-one e's, six f's, four g's, ten h's, sixteen i's, one j, one k, three l's, two m's, twenty n's, thirteen o's, two p's, one q, twelve r's, twenty-eight s's, twenty-eight t's, three u's, three v's, nine w's, four x's, six y's and one z.

It's a basic randomized robinsoniziz..ing. With a rather absurd amount of test code.  
-Mat

-- pangram.rb  
#!/usr/bin/ruby

# Ruby Quiz 86, Pangrams: Solution by Mat Schaffer \<schapht@gmail.com\>  
# uses numeric\_spell library available from [http://tanjero.com/svn/plugins/numeric\_spell/](http://tanjero.com/svn/plugins/numeric_spell/)

require 'numeric\_spell'

class SelfDocumentingPangram  
&nbsp;&nbsp;&nbsp;LETTERS = (?a..?z).to\_a

&nbsp;&nbsp;&nbsp;def initialize(starter\_string = "This test starter contains ")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@start = starter\_string  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def to\_s  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;current = count(@start)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actual = count(add\_count(current))  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while current != actual  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LETTERS.each do |letter|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;current[letter] = rand\_between(current[letter], actual[letter])  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actual = count(add\_count(current))  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add\_count(current)  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def rand\_between a,b  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;range = (a - b).abs + 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rand(range) + [a,b].min  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def add\_count(counts)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@start + counts\_to\_s(counts)  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def count\_to\_s(char, count)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if count != 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count.spell + " " + char.chr + "'s"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count.spell + " " + char.chr  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def counts\_to\_s(count)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string\_counts =   
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LETTERS.each do |letter|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string\_counts \<\< count\_to\_s(letter, count[letter])  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;last = string\_counts.pop  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string\_counts.join(", ") + " and " + last + "."  
&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;def count(string)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count = Hash.new(0)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string.downcase.each\_byte do |letter|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if LETTERS.include? letter  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count[letter] += 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count  
&nbsp;&nbsp;&nbsp;end  
end

if ARGV[0] =~ /test/i  
&nbsp;&nbsp;&nbsp;require 'test/unit'

&nbsp;&nbsp;&nbsp;class TestSelfDocumentingPangram \< Test::Unit::TestCase  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# checks that count will yield accurate counts for only letters, ignoring case  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_count  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# check basic case containing only a..z  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string = ('a'..'z').to\_a.to\_s  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count = SelfDocumentingPangram.new.count(string)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(26, count.length)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count.each do |key, value|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(1, value)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# check case for a..z, A..Z, and some punctiation that we're likely to use  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string = (('a'..'z').to\_a + ('A'..'Z').to\_a + ['\'', ',', '.', '-']).to\_s  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count = SelfDocumentingPangram.new.count(string)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(26, count.length)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count.each do |key, value|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(2, value)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;def test\_count\_to\_s  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal("one a", SelfDocumentingPangram.new.count\_to\_s(?a, 1))  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal("fifteen z's", SelfDocumentingPangram.new.count\_to\_s(?z, 15))  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal("forty-two d's", SelfDocumentingPangram.new.count\_to\_s(?d, 42))  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_counts\_to\_s  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;start = "The last of these contained "  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expected = "two a's, zero b's, one c, one d, four e's, one f, zero g's, two h's, one i, zero j's, zero k's, one l, zero m's, two n's, two o's, zero p's, zero q's, zero r's, two s's, four t's, zero u's, zero v's, zero w's, zero x's, zero y's and zero z's."  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pangram = SelfDocumentingPangram.new  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result = pangram.counts\_to\_s(pangram.count(start))  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(expected, result)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_rand\_between  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;100.times do  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a = rand(100)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b = [a, rand(100)].max  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c = SelfDocumentingPangram.new.rand\_between(a,b)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert (a..b) === c, "#{c} is not between #{a} and #{b}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_add\_count  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pangram = SelfDocumentingPangram.new("hi ")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count = Hash.new(0)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expected = "hi " + pangram.counts\_to\_s(Hash.new(0))  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(expected, pangram.add\_count(Hash.new(0)))  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# runs the SelfDocumentingPangram class to verify that it can produce the pangrams found at  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# [http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt](http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_to\_s  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pangram1 = "This pangram tallies five a's, one b, one c, two d's, twenty-eight e's, eight f's, six g's, eight h's, thirteen i's, one j, one k, three l's, two m's, eighteen n's, fifteen o's, two p's, one q, seven r's, twenty-five s's, twenty-two t's, four u's, four v's, nine w's, two x's, four y's and one z."  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(pangram1, SelfDocumentingPangram.new("This pangram tallies ").to\_s)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#pangram2 = "This computer-generated pangram contains six a's, one b, three c's, three d's, thirty-seven e's, six f's, three g's, nine h's, twelve i's, one j, one k, two l's, three m's, twenty-two n's, thirteen o's, three p's, one q, fourteen r's, twenty-nine s's, twenty-four t's, five u's, six v's, seven w's, four x's, five y's and one z."  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#assert\_equal(pantram2, SelfDocumentingPangram.new("This computer-generated pangram contains ").to\_s)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# This is mainly a sanity check to see that a pangram will evaluate to itself when counted and regenerated  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;def test\_approach  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;prefix = "This pangram tallies "  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;solution = "This pangram tallies five a's, one b, one c, two d's, twenty-eight e's, eight f's, six g's, eight h's, thirteen i's, one j, one k, three l's, two m's, eighteen n's, fifteen o's, two p's, one q, seven r's, twenty-five s's, twenty-two t's, four u's, four v's, nine w's, two x's, four y's and one z."  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pangram = SelfDocumentingPangram.new(prefix)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(solution, pangram.add\_count(pangram.count(solution)))

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;prefix = "This terribly inefficient pangram contains "  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;solution = "This terribly inefficient pangram contains five a's, two b's, three c's, two d's, thirty-one e's, six f's, four g's, ten h's, sixteen i's, one j, one k, three l's, two m's, twenty n's, thirteen o's, two p's, one q, twelve r's, twenty-eight s's, twenty-eight t's, three u's, three v's, nine w's, four x's, six y's and one z."  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pangram = SelfDocumentingPangram.new(prefix)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert\_equal(solution, pangram.add\_count(pangram.count(solution)))  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;end  
else  
&nbsp;&nbsp;&nbsp;puts SelfDocumentingPangram.new("This terribly inefficient pangram contains ").to\_s  
end

> **···**
>
> On Jul 7, 2006, at 11:01 AM, Ruby Quiz wrote:
> 
> > The three rules of Ruby Quiz:
> > 
> > 1. Please do not post any solutions or spoiler discussion for this quiz until  
> > 48 hours have passed from the time on this message.
> > 
> > 2. Support Ruby Quiz by submitting ideas as often as you can:
> > 
> > [http://www.rubyquiz.com/](http://www.rubyquiz.com/)
> > 
> > 3. Enjoy!
> > 
> > Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone  
> > on Ruby Talk follow the discussion. Please reply to the original quiz message,  
> > if you can.
> > 
> > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
> > 
> > by Darren Kirby
> > 
> > One thing that interests me are word puzzles and language oddities. One such  
> > example is the self-documenting panagram. If a panagram is a sentence that uses  
> > every letter in the alphabet, then a self-documenting panagram is a sentence  
> > that enumerates its own letter count. Simple enough, but what if we state that  
> > the letter count must be spelled ie: 'twenty-seven' instead of '27'. Now we  
> > have a challenge.
> > 
> > A while back I wrote a script in Python that finds these sentences. Today I  
> > rewrote it in Ruby and it found me this sentence:
> > 
> > &nbsp;&nbsp;Darren's ruby panagram program found this sentence which contains exactly  
> > &nbsp;&nbsp;nine 'a's, two 'b's, five 'c's, four 'd's, thirty-five 'e's, nine 'f's,  
> > &nbsp;&nbsp;three 'g's, nine 'h's, sixteen 'i's, one 'j', one 'k', two 'l's, three 'm's,  
> > &nbsp;&nbsp;twenty-seven 'n's, fourteen 'o's, three 'p's, one 'q', fifteen 'r's,  
> > &nbsp;&nbsp;thirty-four 's's, twenty-two 't's, six 'u's, six 'v's, seven 'w's, six 'x's,  
> > &nbsp;&nbsp;seven 'y's, and one 'z'.
> > 
> > My script does have its problems, and I would love to see what kind of code the  
> > Ruby experts could come up with to find self-documenting panagrams.
> > 
> > There is a lot more info on self-documenting panagrams at this address:
> > 
> > &nbsp;&nbsp;[http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt](http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt)

---

<div class="post-metadata">

**Author:** ![Simon\_Kroger](https://avatars.discourse-cdn.com/v4/letter/s/49beb7/32.png) [@Simon\_Kroger](https://rubytalk.org/u/Simon_Kroger)\
**Post date:** [9 July 2006 20:29 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/6 "2006-07-09T20:29:17Z")

</div>

Hi,

here is my solution: (some remarks are below)

> **···**
>
> ----------------------------------------------------------------------  
> require 'narray'
> 
> class NArray;  
> &nbsp;&nbsp;include Enumerable;  
> &nbsp;&nbsp;srand(1)  
> end
> 
> class Fixnum  
> &nbsp;&nbsp;NUMS = %w(zero one two three four five six seven eight nine ten eleven) +  
> &nbsp;&nbsp;&nbsp;&nbsp;%w(twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen)  
> &nbsp;&nbsp;TENS = %w(no teen twenty thirty forty fifty sixty seventy eighty ninety)
> 
> &nbsp;&nbsp;def to\_english  
> &nbsp;&nbsp;&nbsp;&nbsp;return NUMS[self] if self \< 20  
> &nbsp;&nbsp;&nbsp;&nbsp;return TENS[self / 10] if (self % 10).zero?  
> &nbsp;&nbsp;&nbsp;&nbsp;TENS[self / 10] + '-' + NUMS[self % 10]  
> &nbsp;&nbsp;end  
> end
> 
> class String  
> &nbsp;&nbsp;def to\_na  
> &nbsp;&nbsp;&nbsp;&nbsp;freq = NArray.int(26)  
> &nbsp;&nbsp;&nbsp;&nbsp;each\_byte do |b|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;freq[b - ?a] += 1 if b \>= ?a and b \<= ?z  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;freq[b - ?A] += 1 if b \>= ?A and b \<= ?Z  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;freq  
> &nbsp;&nbsp;end  
> end
> 
> text = "This is a pangram from simon, it contains "  
> nums = NArray[\*(0..99).map{|i| i.to\_english.to\_na + (i\>1 ? 's' : '').to\_na}]  
> seed = text.to\_na + 'and'.to\_na + 1  
> guess = NArray.int(26).fill!(1)  
> real = seed + nums[true, guess].sum(1)  
> rnd = NArray.float(26)
> 
> while real != guess  
> &nbsp;&nbsp;guess.add! rnd.random!.mul!(real.sbt!(guess)).round  
> &nbsp;&nbsp;real.fill!(0).add!(seed).add!(nums[true, guess].sum(1))  
> end
> 
> s = guess.zip([\*(' a'..' z')]).map{|g, l| g.to\_english + l + (g\>1 ? "'s":"")}  
> result = text + s[0..-2].join(', ') + ' and ' + s.last + '.'
> 
> puts result  
> puts(result.to\_na == guess)  
> ----------------------------------------------------------------------
> 
> I always wanted to look at NArray because a well known rubyist keeps  
> promoting it - if it's good enough for the Nasa than it may be good  
> enough for word puzzles 🙂
> 
> This algorithm is stupid, but why use a scissor if u can have a chain saw?  
> NArray is such a chain saw, so it doesn't really matter how well you aim.
> 
> Ok, the details. First a two dimensional NArray is created containing the  
> frequencies of all 26 letters in the words 'zero' upto 'ninety-nine'. (nums)
> 
> seed contains the frequencies of the static part of the sentence - the  
> preamble the 'and' and 1 of each letter.
> 
> guess is the actual guess and will change over time (very often) I start  
> with a 1 for every letter (which is obviously wrong)
> 
> real contains the frequencies for all letters if one would spell out the  
> sentence described in guess (one 'a', one 'b', one 'c'... for the initial  
> guess). To get this we select the lines from nums that correspond to the  
> numbers (26 times 'one' for the first guess) and sums every column up.  
> (you start to see the chain saw?)
> 
> Ok, now we can start the massacre.
> 
> If real == guess we are ready, if not we calculate the error for each  
> letter (in one step of course: real.sbt!(guess)).  
> We multiply each error with a random value 0 \<= rand \< 1  
> (in one step: rnd.ramdom!.mul!) and add this randomized error back onto  
> the guess. (again, one step)
> 
> All that is left is to calculate the real frequencies again and check  
> if we hit the spot.
> 
> (this is my second version, the other one is longer and uses a slightly  
> other algorithm (and is sometimes even faster) but i like this one)
> 
> cheers
> 
> Simon

---

<div class="post-metadata">

**Author:** ![Tim\_Hollingsworth](https://avatars.discourse-cdn.com/v4/letter/t/5daacb/32.png) [@Tim\_Hollingsworth](https://rubytalk.org/u/Tim_Hollingsworth)\
**Post date:** [9 July 2006 23:52 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/7 "2006-07-09T23:52:18Z")

</div>

Hello

First post here, by the way. Greetings to all:) New to ruby, love it, slaps  
on backs all round.

While I haven't solved the pangram problem, I did find what appears to be a  
bug in Numeric.divmod on large numbers, as can be seen when using  
Bignum.modulo:

irb(main):017:0\> 1e22 % 1e21  
=\> 0.0  
irb(main):018:0\> 1e23 % 1e22  
=\> 9.99999999999999e+021

whoops!

Also, I had a bit of fun hacking away at the plumbing required for the  
quiz. My solution to generating spelt numbers is, while neither fast nor  
optimally pretty I'm sure, novel and rather flexible.

I wrote a little RangeMap class which will map a continuous set of variable  
ranges to values. Then I used it to map ranges to spelling rules (in the  
form of proc objects). The rules can recursively call eachother using  
further lookups on the RangeMap. This system allows easy implentation of  
exceptions (such as "twelve hundred" etc), and translation into different  
languages or variations of english. Also it could be used to solve the  
pangrams problem directly by configuring the rules to tally the characters  
rather than generate the actual strings.

class RangeMap

&nbsp;&nbsp;def initialize  
&nbsp;&nbsp;&nbsp;&nbsp;@values = Hash.new  
&nbsp;&nbsp;&nbsp;&nbsp;@ranges =   
&nbsp;&nbsp;end

&nbsp;&nbsp;#Insert your range by specifying the lower bound.  
&nbsp;&nbsp;#RangeMap generates the upper value based on what's  
&nbsp;&nbsp;#already in the map. While not ideal, it makes it  
&nbsp;&nbsp;#a lot easier to keep the set continuous.  
&nbsp;&nbsp;def insert range\_first, value  
&nbsp;&nbsp;&nbsp;&nbsp;@values[range\_first] = value  
&nbsp;&nbsp;&nbsp;&nbsp;lowers = @values.keys.sort  
&nbsp;&nbsp;&nbsp;&nbsp;uppers = @values.keys.sort  
&nbsp;&nbsp;&nbsp;&nbsp;lowers.pop  
&nbsp;&nbsp;&nbsp;&nbsp;uppers.shift  
&nbsp;&nbsp;&nbsp;&nbsp;@ranges =   
&nbsp;&nbsp;&nbsp;&nbsp;for i in 0...lowers.size do  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@ranges \<\< (lowers[i]...uppers[i])  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end

&nbsp;&nbsp;def find n  
&nbsp;&nbsp;&nbsp;&nbsp;if n \< @ranges.first.first || n \> @ranges.last.last  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Number outside ranges: #{@ranges.first.first}-#{@  
ranges.last.last}"  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;range\_first = binary\_search(n, 0, @ranges.size)  
&nbsp;&nbsp;&nbsp;&nbsp;@values[range\_first]  
&nbsp;&nbsp;end

protected  
&nbsp;&nbsp;def binary\_search n, a, b  
&nbsp;&nbsp;&nbsp;&nbsp;middle = (a + b) / 2  
&nbsp;&nbsp;&nbsp;&nbsp;range = @ranges[middle]  
&nbsp;&nbsp;&nbsp;&nbsp;if n \< range.first  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;binary\_search(n, a, middle)  
&nbsp;&nbsp;&nbsp;&nbsp;elsif n \>= range.last  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;binary\_search(n, middle, b)  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;range.first  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end

end

class Integer

&nbsp;&nbsp;@@rules = RangeMap.new

&nbsp;&nbsp;[{ :first =\> 0, :name =\> "zero" }, { :first =\> 1, :name =\> "one" },  
&nbsp;&nbsp;{ :first =\> 2, :name =\> "two" }, { :first =\> 3, :name =\> "three" },  
&nbsp;&nbsp;{ :first =\> 4, :name =\> "four" }, { :first =\> 5, :name =\> "five" },  
&nbsp;&nbsp;{ :first =\> 6, :name =\> "six" }, { :first =\> 7, :name =\> "seven" },  
&nbsp;&nbsp;{ :first =\> 8, :name =\> "eight" }, { :first =\> 9, :name =\> "nine" },  
&nbsp;&nbsp;{ :first =\> 10, :name =\> "ten" }, { :first =\> 11, :name =\> "eleven" },  
&nbsp;&nbsp;{ :first =\> 12, :name =\> "twelve" }, { :first =\> 13, :name =\> "thirteen"  
},  
&nbsp;&nbsp;{ :first =\> 14, :name =\> "fourteen" }, { :first =\> 15, :name =\> "fifteen"  
},  
&nbsp;&nbsp;{ :first =\> 16, :name =\> "sixteen" }, { :first =\> 17, :name =\> "seventeen"  
},  
&nbsp;&nbsp;{ :first =\> 18, :name =\> "eighteen" },  
&nbsp;&nbsp;{ :first =\> 19, :name =\> "nineteen" }].each do |single|  
&nbsp;&nbsp;&nbsp;&nbsp;name = single[:name].freeze  
&nbsp;&nbsp;&nbsp;&nbsp;@@rules.insert(single[:first], lambda {|n| name})  
&nbsp;&nbsp;end

&nbsp;&nbsp;[{ :first =\> 20, :name =\> "twenty" },  
&nbsp;&nbsp;{ :first =\> 30, :name =\> "thirty" },  
&nbsp;&nbsp;{ :first =\> 40, :name =\> "forty" },  
&nbsp;&nbsp;{ :first =\> 50, :name =\> "fifty" },  
&nbsp;&nbsp;{ :first =\> 60, :name =\> "sixty" },  
&nbsp;&nbsp;{ :first =\> 70, :name =\> "seventy" },  
&nbsp;&nbsp;{ :first =\> 80, :name =\> "eighty" },  
&nbsp;&nbsp;{ :first =\> 90, :name =\> "ninety" }].each do |ten|  
&nbsp;&nbsp;&nbsp;&nbsp;divisor = ten[:first]  
&nbsp;&nbsp;&nbsp;&nbsp;name = ten[:name].freeze  
&nbsp;&nbsp;&nbsp;&nbsp;@@rules.insert(divisor, lambda do |n|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spelt = name.dup  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remainder = n % divisor  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spelt \<\< "-" + execute\_rule(remainder) if remainder != 0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spelt  
&nbsp;&nbsp;&nbsp;&nbsp;end)  
&nbsp;&nbsp;end

&nbsp;&nbsp;[{ :first =\> 1E2.to\_i, :name =\> "hundred" },  
&nbsp;&nbsp;{ :first =\> 1E3.to\_i, :name =\> "thousand" },  
&nbsp;&nbsp;{ :first =\> 1E6.to\_i, :name =\> "million" },  
&nbsp;&nbsp;{ :first =\> 1E9.to\_i, :name =\> "billion" },  
&nbsp;&nbsp;{ :first =\> 1E12.to\_i, :name =\> "trillion" },  
&nbsp;&nbsp;{ :first =\> 1E15.to\_i, :name =\> "quadrillion" },  
&nbsp;&nbsp;{ :first =\> 1E18.to\_i, :name =\> "quintillion" },  
&nbsp;&nbsp;{ :first =\> 1E21.to\_i, :name =\> "sextillion" },  
&nbsp;&nbsp;{ :first =\> 1E24.to\_i, :name =\> "septillion" },  
&nbsp;&nbsp;{ :first =\> 1E27.to\_i, :name =\> "octillion" },  
&nbsp;&nbsp;{ :first =\> 1E30.to\_i, :name =\> "nonillion" },  
&nbsp;&nbsp;{ :first =\> 1E33.to\_i, :name =\> "decillion" },  
&nbsp;&nbsp;{ :first =\> 1E36.to\_i, :name =\> "undecillion" },  
&nbsp;&nbsp;{ :first =\> 1E39.to\_i, :name =\> "duodecillion" },  
&nbsp;&nbsp;{ :first =\> 1E42.to\_i, :name =\> "tredecillion" },  
&nbsp;&nbsp;{ :first =\> 1E45.to\_i, :name =\> "quattuordecillion" },  
&nbsp;&nbsp;{ :first =\> 1E48.to\_i, :name =\> "quindecillion" },  
&nbsp;&nbsp;{ :first =\> 1E51.to\_i, :name =\> "sexdecillion" },  
&nbsp;&nbsp;{ :first =\> 1E54.to\_i, :name =\> "septendecillion" },  
&nbsp;&nbsp;{ :first =\> 1E57.to\_i, :name =\> "octodecillion" },  
&nbsp;&nbsp;{ :first =\> 1E60.to\_i, :name =\> "novemdecillion" },  
&nbsp;&nbsp;{ :first =\> 1E63.to\_i, :name =\> "vigintillion" }].each do |big|  
&nbsp;&nbsp;&nbsp;&nbsp;divisor = big[:first]  
&nbsp;&nbsp;&nbsp;&nbsp;name = " " + big[:name].freeze  
&nbsp;&nbsp;&nbsp;&nbsp;@@rules.insert(divisor, lambda do |n|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spelt = execute\_rule(n/divisor) + name  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remainder = n % divisor  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (remainder \> 0)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if remainder \< 100  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spelt \<\< " and "  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spelt \<\< ", "  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spelt \<\< execute\_rule(remainder)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spelt  
&nbsp;&nbsp;&nbsp;&nbsp;end)  
&nbsp;&nbsp;end

&nbsp;&nbsp;def self.execute\_rule n  
&nbsp;&nbsp;&nbsp;&nbsp;@@rules.find(n).call(n)  
&nbsp;&nbsp;end

&nbsp;&nbsp;def to\_english  
&nbsp;&nbsp;&nbsp;&nbsp;self.class.execute\_rule(self)  
&nbsp;&nbsp;end  
end

puts 123456789.to\_english

> **···**
>
> On 7/7/06, Ruby Quiz \<james@grayproductions.net\> wrote:
> 
> > The three rules of Ruby Quiz:
> > 
> > 1. Please do not post any solutions or spoiler discussion for this quiz  
> > until  
> > 48 hours have passed from the time on this message.
> > 
> > 2. Support Ruby Quiz by submitting ideas as often as you can:
> > 
> > [http://www.rubyquiz.com/](http://www.rubyquiz.com/)
> > 
> > 3. Enjoy!
> > 
> > Suggestion: A [QUIZ] in the subject of emails about the problem helps  
> > everyone  
> > on Ruby Talk follow the discussion. Please reply to the original quiz  
> > message,  
> > if you can.
> > 
> > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
> > 
> > by Darren Kirby
> > 
> > One thing that interests me are word puzzles and language oddities. One  
> > such  
> > example is the self-documenting panagram. If a panagram is a sentence that  
> > uses  
> > every letter in the alphabet, then a self-documenting panagram is a  
> > sentence  
> > that enumerates its own letter count. Simple enough, but what if we state  
> > that  
> > the letter count must be spelled ie: 'twenty-seven' instead of '27'. Now  
> > we  
> > have a challenge.
> > 
> > A while back I wrote a script in Python that finds these sentences. Today  
> > I  
> > rewrote it in Ruby and it found me this sentence:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Darren's ruby panagram program found this sentence which contains  
> > exactly  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nine 'a's, two 'b's, five 'c's, four 'd's, thirty-five 'e's, nine  
> > 'f's,  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;three 'g's, nine 'h's, sixteen 'i's, one 'j', one 'k', two 'l's,  
> > three 'm's,  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;twenty-seven 'n's, fourteen 'o's, three 'p's, one 'q', fifteen  
> > 'r's,  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;thirty-four 's's, twenty-two 't's, six 'u's, six 'v's, seven 'w's,  
> > six 'x's,  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;seven 'y's, and one 'z'.
> > 
> > My script does have its problems, and I would love to see what kind of  
> > code the  
> > Ruby experts could come up with to find self-documenting panagrams.
> > 
> > There is a lot more info on self-documenting panagrams at this address:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt](http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt)

---

<div class="post-metadata">

**Author:** ![Daniel\_Martin](https://avatars.discourse-cdn.com/v4/letter/d/dc4da7/32.png) [@Daniel\_Martin](https://rubytalk.org/u/Daniel_Martin)\
**Post date:** [10 July 2006 02:04 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/8 "2006-07-10T02:04:48Z")

</div>

#!ruby

# First, we need a way to convert integers into English.  
# Fortunately, that was quiz #25, so I just stole one of  
# the many solutions offered there.

> **···**
>
> #  
> # More commentary after the swiped code
> 
> # stolen from Glenn Parker's solution to ruby  
> # quiz #25. [ruby-talk:135449]
> 
> # Begin swiped code  
> class Integer
> 
> &nbsp;&nbsp;&nbsp;Ones = %w[zero one two three four five six seven eight nine]  
> &nbsp;&nbsp;&nbsp;Teen = %w[ ten eleven twelve thirteen fourteen fifteen  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sixteen seventeen eighteen nineteen ]  
> &nbsp;&nbsp;&nbsp;Tens = %w[ zero ten twenty thirty forty fifty  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sixty seventy eighty ninety ]  
> &nbsp;&nbsp;&nbsp;Mega = %w[none thousand million billion]
> 
> &nbsp;&nbsp;&nbsp;def to\_en  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;places = to\_s.split(//).collect {|s| s.to\_i}.reverse  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name = []  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((places.length + 2) / 3).times do |p|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strings = Integer.trio(places[p \* 3, 3])  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name.push(Mega[p]) if strings.length \> 0 and p \> 0  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name += strings  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name.push(Ones[0]) unless name.length \> 0  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name.reverse.join(" ")  
> &nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;private
> 
> &nbsp;&nbsp;&nbsp;def Integer.trio(places)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strings = []  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if places[1] == 1  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strings.push(Teen[places[0]])  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elsif places[1] and places[1] \> 0  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strings.push(places[0] == 0 ? Tens[places[1]] :  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"#{Tens[places[1]]}-#{Ones[places[0]]}")  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elsif places[0] \> 0  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strings.push(Ones[places[0]])  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if places[2] and places[2] \> 0  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strings.push("hundred", Ones[places[2]])  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strings  
> &nbsp;&nbsp;&nbsp;end
> 
> end  
> # End swiped code
> 
> # Okay, now on with my solution. I assume that almost every  
> # solution will use some form of the "Robbinsoning" described  
> # in the pangram page linked from the quiz. This somewhat limits  
> # the number of different variations we can have.  
> #  
> # In my solution, I was trying something a bit tricky with how I  
> # represented letter frequency to speed things up - though I still  
> # think that there's some slight twist to the search scheme that  
> # makes things even faster. Hopefully I'll find out by examining  
> # others' solutions.  
> #  
> # I represented the letter frequencies of letters in a sentence  
> # as one huge bignum, such that if "freq" was a variable containing  
> # the number, then "freq & 0xFF" would be the number of "a"s in the  
> # sentence, "(freq\>\>8) & 0xFF" would be the number of "b"s, etc.  
> #  
> # This means that when I adjust a guess, changing the actual frequency  
> # is as simple as adding and subtracting from a single variable.  
> #  
> # I probably could have split this up some, but the option of writing  
> # Hash.new to get the equivalent of a memoized lambda made it really  
> # easy to simply write it as one big routine.  
> #  
> # One last thing - note when I take a random step towards the actual  
> # frequency, I actually call rand twice, and take the larger value.  
> # I found this to be about twice as fast as calling rand just once.  
> # (This biases things towards larger steps; i.e. towards the actual  
> # measured frequency)
> 
> # This routine is for debugging - it turns a bignum containing  
> # letter frequencies into a human-readable string.  
> def explode(big)  
> &nbsp;&nbsp;s = ""  
> &nbsp;&nbsp;('a'..'z').each{|x| big, r = big.divmod(256); s += "%2d " % r}  
> &nbsp;&nbsp;s  
> end
> 
> def find\_sentence(prefix, suffix, initial = {})  
> &nbsp;&nbsp;letterre = Regexp.new('(?i:[a-z])');  
> &nbsp;&nbsp;letters = ('a'..'z').to\_a  
> &nbsp;&nbsp;letterpower = Hash.new {|h,k| h[k] = 1 \<\< ((k[0]-?a)\*8)}  
> &nbsp;&nbsp;lettershifts = letters.map {|x| ((x[0]-?a)\*8)}  
> &nbsp;&nbsp;basesentence = prefix + letters.map {|x|  
> &nbsp;&nbsp;&nbsp;&nbsp;(x == 'z'? 'and ' : '') + "\_ '#{x}'"}.join(', ') + suffix  
> &nbsp;&nbsp;basefreq = 0  
> &nbsp;&nbsp;basesentence.scan(letterre) {|x| basefreq +=  
> letterpower[x.downcase]}  
> &nbsp;&nbsp;# enfreq holds the letter counts that spelling out that number adds  
> to  
> &nbsp;&nbsp;# the sentence.  
> &nbsp;&nbsp;# E.g. enfreq[1] == letterpower['o'] + letterpower['n'] +  
> letterpower['e']  
> &nbsp;&nbsp;enfreq = Hash.new {|h,k|  
> &nbsp;&nbsp;&nbsp;&nbsp;if k \> 255 then  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h[k] = h[k \>\> 8]  
> &nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h[k] = 0  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;k.to\_en.scan(letterre) {|x| h[k] += letterpower[x.downcase]}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h[k] += letterpower['s'] if k != 1  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;h[k]  
> &nbsp;&nbsp;}  
> &nbsp;&nbsp;guessfreq = 0  
> &nbsp;&nbsp;letters.each{|x|  
> &nbsp;&nbsp;&nbsp;&nbsp;guessfreq += (initial[x]||0) \* letterpower[x]  
> &nbsp;&nbsp;}  
> &nbsp;&nbsp;guessfreq = basefreq if guessfreq == 0  
> &nbsp;&nbsp;actualfreq = 0  
> &nbsp;&nbsp;sentence = ""  
> &nbsp;&nbsp;begin  
> &nbsp;&nbsp;&nbsp;&nbsp;cyclecount, adjusts = 0, 0  
> &nbsp;&nbsp;&nbsp;&nbsp;until guessfreq == actualfreq do  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if actualfreq \> 0 then  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lettershifts.each{ |y|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g = 0xFF & (guessfreq \>\> y)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a = 0xFF & (actualfreq \>\> y)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (g != a)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;d = (g-a).abs  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r1 = rand(d+1)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r2 = rand(d+1)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r1=r2 if r1 \< r2  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r1=-r1 if a\<g  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (r1 != 0) then  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;adjusts += 1  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;guessfreq += r1 \<\< y  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actualfreq += enfreq[g+r1] - enfreq[g]  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actualfreq = basefreq  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lettershifts.each {|y|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g = 0xFF & (guessfreq \>\> y)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actualfreq += enfreq[g]  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> #DEBUG  
> # puts explode(actualfreq)  
> # return "\_\_\_" if cyclecount \> 10\_000\_000  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cyclecount += 1  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;ensure  
> &nbsp;&nbsp;&nbsp;&nbsp;puts [cyclecount, adjusts].inspect  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;sentence = prefix + ('a'..'z').map {|x|  
> &nbsp;&nbsp;&nbsp;&nbsp;g = (guessfreq \>\> ((x[0]-?a)\*8))%256  
> &nbsp;&nbsp;&nbsp;&nbsp;(x == 'z'? 'and ' : '') + "#{g.to\_en} '#{x}'" +  
> &nbsp;&nbsp;&nbsp;&nbsp;(g==1 ? '' : 's')}.join(', ') + suffix  
> &nbsp;&nbsp;sentence  
> end
> 
> # And here's where I actually call it. Note that I \*cannot\* get an  
> # answer if I use the prefix that contains my email address.
> 
> puts find\_sentence(  
> # "a ruby quiz solution found this sentence enumerating ",  
> # "The program from [martin@snowplow.org](mailto:martin@snowplow.org) produced a sentence with ",  
> &nbsp;&nbsp;"Daniel Martin's sallowsgram program produced a sentence with ",  
> # "This terribly inefficient pangram contains ",  
> # "Darren's ruby panagram program found this sentence which contains  
> exactly ",  
> &nbsp;&nbsp;"."  
> &nbsp;&nbsp;# optional seed value - this is the same as the sentence given  
> &nbsp;&nbsp;# in the quiz description.  
> &nbsp;&nbsp;# {'a'=\>9,'b'=\>2,'c'=\>5,'d'=\>4,'e'=\>35,  
> &nbsp;&nbsp;# 'f'=\>9,'g'=\>3,'h'=\>9,'i'=\>16,'j'=\>1,  
> &nbsp;&nbsp;# 'k'=\>1,'l'=\>2,'m'=\>3,'n'=\>27,'o'=\>14,  
> &nbsp;&nbsp;# 'p'=\>3,'q'=\>1,'r'=\>15,'s'=\>34,'t'=\>22,  
> &nbsp;&nbsp;# 'u'=\>6,'v'=\>6,'w'=\>7,'x'=\>6,'y'=\>7,  
> &nbsp;&nbsp;# 'z'=\>1}  
> &nbsp;&nbsp;)

---

<div class="post-metadata">

**Author:** ![Sam\_Kong](https://avatars.discourse-cdn.com/v4/letter/s/ad7895/32.png) [@Sam\_Kong](https://rubytalk.org/u/Sam_Kong)\
**Post date:** [12 July 2006 04:29 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/9 "2006-07-12T04:29:07Z")

</div>

Here's my late submission.

I borrowed the English Numerals solution from Quiz #25.  
([http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/135446](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/135446))  
Also, I stole the ideas from other posts.  
The reason I made this solution was not that I wanted to challenge it,  
but that I was very impressed by the Robinsonizing algorithm.  
My first solution (brute force) took forever and couldn't find an  
answer.  
Now this solution finds an answer mostly within 10 seconds.

It was very interesting.

Sam

> **···**
>
> --------
> 
> class Integer
> 
> &nbsp;&nbsp;def teen  
> &nbsp;&nbsp;&nbsp;&nbsp;case self  
> &nbsp;&nbsp;&nbsp;&nbsp;when 0: "ten"  
> &nbsp;&nbsp;&nbsp;&nbsp;when 1: "eleven"  
> &nbsp;&nbsp;&nbsp;&nbsp;when 2: "twelve"  
> &nbsp;&nbsp;&nbsp;&nbsp;else in\_compound + "teen"  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;def ten  
> &nbsp;&nbsp;&nbsp;&nbsp;case self  
> &nbsp;&nbsp;&nbsp;&nbsp;when 1: "ten"  
> &nbsp;&nbsp;&nbsp;&nbsp;when 2: "twenty"  
> &nbsp;&nbsp;&nbsp;&nbsp;else in\_compound + "ty"  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;def in\_compound  
> &nbsp;&nbsp;&nbsp;&nbsp;case self  
> &nbsp;&nbsp;&nbsp;&nbsp;when 3: "thir"  
> &nbsp;&nbsp;&nbsp;&nbsp;when 5: "fif"  
> &nbsp;&nbsp;&nbsp;&nbsp;when 8: "eigh"  
> &nbsp;&nbsp;&nbsp;&nbsp;else to\_en  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;def to\_en(ands=true)  
> &nbsp;&nbsp;&nbsp;&nbsp;small\_nums = [""] + %w[one two three four five six seven eight nine]  
> &nbsp;&nbsp;&nbsp;&nbsp;if self \< 10: small\_nums[self]  
> &nbsp;&nbsp;&nbsp;&nbsp;elsif self \< 20: (self % 10).teen  
> &nbsp;&nbsp;&nbsp;&nbsp;elsif self \< 100:  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result = (self/10).ten  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result += "-" if (self % 10) != 0  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result += (self % 10).to\_en  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return result  
> &nbsp;&nbsp;&nbsp;&nbsp;elsif self \< 1000  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if self%100 != 0 and ands  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(self/100).to\_en(ands)+" hundred and "+(self%100).to\_en(ands)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else ((self/100).to\_en(ands)+  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" hundred "+(self%100).to\_en(ands)).chomp(" ")  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;front,back = case (self.to\_s.length) % 3  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when 0: [0..2,3..-1].map{|i| self.to\_s[i]}.map{|i| i.to\_i}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when 2: [0..1,2..-1].map{|i| self.to\_s[i]}.map{|i| i.to\_i}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when 1: [0..0,1..-1].map{|i| self.to\_s[i]}.map{|i| i.to\_i}  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;degree = [""] + %w[thousand million billion trillion quadrillion  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;quintillion sextillion septillion octillion nonillion decillion  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;undecillion duodecillion tredecillion quattuordecillion  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;quindecillion sexdecillion septdecillion novemdecillion  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vigintillion unvigintillion duovigintillion trevigintillion  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;quattuorvigintillion quinvigintillion sexvigintillion  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;septvigintillion octovigintillion novemvigintillion trigintillion  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;untregintillion duotrigintillion googol]  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result = front.to\_en(false) + " " + degree[(self.to\_s.length-1)/3]  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result += if back \> 99: ", "  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elsif back \> 0: ands ? " and " : " "  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else ""  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result += back.to\_en(ands)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return result.chomp(" ")  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end
> 
> end
> 
> class Pangram
> 
> &nbsp;&nbsp;LETTERS = ('a'..'z').to\_a  
> &nbsp;&nbsp;RE = /[a-z]/
> 
> &nbsp;&nbsp;def initialize sentence  
> &nbsp;&nbsp;&nbsp;&nbsp;@original\_sentence = sentence  
> &nbsp;&nbsp;&nbsp;&nbsp;@count = Hash.new(1)  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;def self\_documenting\_pangram  
> &nbsp;&nbsp;&nbsp;&nbsp;while true  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;make\_sentence\_with\_current\_count  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LETTERS.each do |letter|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return @sentence if is\_correct?  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;update\_count letter  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end
> 
> private
> 
> &nbsp;&nbsp;def update\_count letter  
> &nbsp;&nbsp;&nbsp;&nbsp;current = @count[letter]  
> &nbsp;&nbsp;&nbsp;&nbsp;real = get\_count letter  
> &nbsp;&nbsp;&nbsp;&nbsp;@count[letter] = rand\_between current, real  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;def rand\_between n1, n2  
> &nbsp;&nbsp;&nbsp;&nbsp;case (n1 - n2).abs  
> &nbsp;&nbsp;&nbsp;&nbsp;when 0, 1  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;n2  
> &nbsp;&nbsp;&nbsp;&nbsp;when 2  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[n1, n2].min + 1  
> &nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[n1, n2].min + rand((n1 - n2).abs - 2) + 1  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;def get\_count letter  
> &nbsp;&nbsp;&nbsp;&nbsp;@sentence.downcase.scan(/#{letter}/).size  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;def is\_correct?  
> &nbsp;&nbsp;&nbsp;&nbsp;@count ==  
> &nbsp;&nbsp;&nbsp;&nbsp;@sentence.downcase.scan(RE).inject(Hash.new(0)) do |m, i|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m[i] += 1  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;def make\_sentence\_with\_current\_count  
> &nbsp;&nbsp;&nbsp;&nbsp;@sentence = @original\_sentence + " " +  
> &nbsp;&nbsp;&nbsp;&nbsp;LETTERS.inject("") do |memo, letter|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memo \<\< "and " if letter == 'z'  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memo \<\< @count[letter].to\_en + " " + letter  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if @count[letter] \> 1  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memo \<\< "'s"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memo \<\< (letter == 'z' ? "." : ", ")  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end
> 
> end
> 
> sentence = \<\<END  
> Darren's ruby panagram program found this sentence which contains  
> exactly  
> END
> 
> pangram = Pangram.new sentence  
> puts pangram.self\_documenting\_pangram
> 
> --  
> Posted via [http://www.ruby-forum.com/](http://www.ruby-forum.com/).

---

<div class="post-metadata">

**Author:** ![darren\_kirby](https://avatars.discourse-cdn.com/v4/letter/d/ed8c4c/32.png) [@darren\_kirby](https://rubytalk.org/u/darren_kirby)\
**Post date:** [12 July 2006 22:09 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/10 "2006-07-12T22:09:50Z")

</div>

Thanks for catching the "pangram" versus "panagram" issue, I had never noticed  
it. Thanks to everyone for your great solutions, they are very interesting  
and I have learned a lot from reading them.

In the interest of completeness, here is my original script that prompted me  
to suggest this quiz. Those that read the Perl solution will recognize it is  
very similar. This code is limited because (like the perl script) it will  
barf if the count of any one letter is \> 99, however, I have not found this  
to be a practical problem with any seeds I have tried (I think the greatest  
letter count was ~35).

This code produced two solutions using the same seed, something I thought not  
possible (at least when using a reasonable short seed):

Only a fool would check that this sentence contains exactly six 'a's, one 'b',  
six 'c's, three 'd's, thirty-four 'e's, five 'f's, two 'g's, ten 'h's,  
thirteen 'i's, one 'j', two 'k's, five 'l's, one 'm', twenty-two 'n's,  
seventeen 'o's, one 'p', one 'q', seven 'r's, thirty-two 's's,  
twenty-six 't's, three 'u's, seven 'v's, eight 'w's, six 'x's, seven 'y's,  
and one 'z'.

and:

Only a fool would check that this sentence contains exactly six 'a's, one 'b',  
six 'c's, three 'd's, thirty-three 'e's, four 'f's, two 'g's, ten 'h's,  
twelve 'i's, one 'j', two 'k's, six 'l's, one 'm', twenty 'n's, sixteen 'o's,  
one 'p', one 'q', seven 'r's, thirty-two 's's, twenty-five 't's, three 'u's,  
six 'v's, eight 'w's, seven 'x's, seven 'y's, and one 'z'

And the code:

> **···**
>
> #################  
> $snum = {  
> &nbsp;&nbsp;&nbsp;&nbsp;1 =\> 'one', 2 =\> 'two', 3 =\> 'three', 4 =\> 'four', 5 =\> 'five', 6  
> =\> 'six',  
> &nbsp;&nbsp;&nbsp;&nbsp;7 =\> 'seven', 8 =\> 'eight', 9 =\> 'nine', 10 =\> 'ten', 11 =\> 'eleven',  
> &nbsp;&nbsp;&nbsp;&nbsp;12 =\> 'twelve', 13 =\> 'thirteen', 14 =\> 'fourteen', 15 =\> 'fifteen',  
> &nbsp;&nbsp;&nbsp;&nbsp;16 =\> 'sixteen', 17 =\> 'seventeen', 18 =\> 'eighteen', 19 =\> 'nineteen',  
> &nbsp;&nbsp;&nbsp;&nbsp;20 =\> 'twenty', 30 =\> 'thirty', 40 =\> 'forty', 50 =\> 'fifty', 60  
> =\> 'sixty',  
> &nbsp;&nbsp;&nbsp;&nbsp;70 =\> 'seventy', 80 =\> 'eighty', 90 =\> 'ninety'  
> &nbsp;&nbsp;&nbsp;&nbsp;}
> 
> def spelledNumber(x)  
> &nbsp;&nbsp;if x \>= 100  
> &nbsp;&nbsp;&nbsp;&nbsp;print "must be 99 or less"  
> &nbsp;&nbsp;&nbsp;&nbsp;exit  
> &nbsp;&nbsp;elsif x \<= 20  
> &nbsp;&nbsp;&nbsp;&nbsp;$snum[x]  
> &nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;tens = (x / 10).to\_i \* 10  
> &nbsp;&nbsp;&nbsp;&nbsp;if x - tens == 0  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$snum[x]  
> &nbsp;&nbsp;&nbsp;&nbsp;else  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$snum[tens] + "-" + $snum[x - tens]  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end  
> end
> 
> def checkIfTrue(s)  
> &nbsp;&nbsp;realCount = {}  
> &nbsp;&nbsp;LETTERS.each do |c|  
> &nbsp;&nbsp;&nbsp;&nbsp;realCount[c] = s.count(c)  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;if $fixedCount == realCount  
> &nbsp;&nbsp;&nbsp;&nbsp;puts "Found it:"  
> &nbsp;&nbsp;&nbsp;&nbsp;puts s  
> &nbsp;&nbsp;&nbsp;&nbsp;exit  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;$fixedCount.each do |key, value|  
> &nbsp;&nbsp;&nbsp;&nbsp;x = s.count(key)  
> &nbsp;&nbsp;&nbsp;&nbsp;y = value  
> &nbsp;&nbsp;&nbsp;&nbsp;$fixedCount[key] = randomizer(x, y)  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;$fixedCount  
> end
> 
> def randomizer(x, y)  
> &nbsp;&nbsp;if x == y then return x end  
> &nbsp;&nbsp;if x \> y then x, y = y, x end  
> &nbsp;&nbsp;rand(y-x+1)+x  
> end
> 
> LETTERS = ('a'..'z').to\_a  
> seed = %q/darrens ruby panagram program found this sentence which contains  
> exactly and /  
> $fixedCount = {}  
> LETTERS.each { |c| $fixedCount[c] = rand(50) }
> 
> while 1  
> &nbsp;&nbsp;(1..10000).each do  
> &nbsp;&nbsp;&nbsp;&nbsp;s = seed  
> &nbsp;&nbsp;&nbsp;&nbsp;LETTERS.each do |c|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s += spelledNumber($fixedCount[c])  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s += " '#{c}'"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s += $fixedCount[c] \>= 2 ? "s, " : ", "  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;$fixedCount = checkIfTrue(s)  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;print "\t10K blip...\n"  
> end  
> ###################
> 
> --  
> darren kirby :: Part of the problem since 1976 :: [http://badcomputer.org](http://badcomputer.org)  
> "...the number of UNIX installations has grown to 10, with more expected..."  
> - Dennis Ritchie and Ken Thompson, June 1972

---

<div class="post-metadata">

**Author:** ![Matthew\_D\_Moss](https://avatars.discourse-cdn.com/v4/letter/m/b9e5f3/32.png) [@Matthew\_D\_Moss](https://rubytalk.org/u/Matthew_D_Moss)\
**Post date:** [7 July 2006 15:20 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/11 "2006-07-07T15:20:07Z")

</div>

Nevermind, answered my own question with the dictionary: pangram.

> **···**
>
> On 7/7/06, Matthew Moss \<matthew.moss.coder@gmail.com\> wrote:
> 
> > So is it "pangram" or "panagram"?
> > 
> > \> There is a lot more info on self-documenting panagrams at this address:  
> > \>  
> > \> [http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt](http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt)

---

<div class="post-metadata">

**Author:** ![James\_Edward\_Gray\_II](https://avatars.discourse-cdn.com/v4/letter/j/ea5d25/32.png) [@James\_Edward\_Gray\_II](https://rubytalk.org/u/James_Edward_Gray_II)\
**Post date:** [7 July 2006 15:20 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/12 "2006-07-07T15:20:37Z")

</div>

Good catch, I didn't even notice the name discrepancy. Pangram does appear to be the correct term.

James Edward Gray II

> **···**
>
> On Jul 7, 2006, at 10:13 AM, Matthew Moss wrote:
> 
> > So is it "pangram" or "panagram"?
> > 
> > > There is a lot more info on self-documenting panagrams at this address:
> > > 
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt](http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt)

---

<div class="post-metadata">

**Author:** ![Brown\_Warren](https://avatars.discourse-cdn.com/v4/letter/b/73ab20/32.png) [@Brown\_Warren](https://rubytalk.org/u/Brown_Warren)\
**Post date:** [7 July 2006 15:21 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/13 "2006-07-07T15:21:37Z")

</div>

> So is it "pangram" or "panagram"?

&nbsp;&nbsp;&nbsp;&nbsp;The word is "pangram". Some people misspell it as "panagram",  
probably due to an association with the word "anagram", another word  
puzzle favorite.

&nbsp;&nbsp;&nbsp;&nbsp;- Warren Brown

---

<div class="post-metadata">

**Author:** ![Brian\_Mattern](https://avatars.discourse-cdn.com/v4/letter/b/47e85d/32.png) [@Brian\_Mattern](https://rubytalk.org/u/Brian_Mattern)\
**Post date:** [7 July 2006 17:39 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/14 "2006-07-07T17:39:02Z")

</div>

Actually, I lied. Almost correct, but at least one number is off...

> **···**
>
> On Sat, Jul 08, 2006 at 02:34:23AM +0900, brian.mattern@gmail.com wrote:
> 
> > Is it a spoiler if we post a resulting pangram (no code)?
> > 
> > I got one in:  
> > real 1m17.089s  
> > user 1m8.444s  
> > sys 0m2.492s
> > 
> > on an athlon 64 300+
> > 
> > Brian

---

<div class="post-metadata">

**Author:** ![James\_Edward\_Gray\_II](https://avatars.discourse-cdn.com/v4/letter/j/ea5d25/32.png) [@James\_Edward\_Gray\_II](https://rubytalk.org/u/James_Edward_Gray_II)\
**Post date:** [7 July 2006 18:18 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/15 "2006-07-07T18:18:26Z")

</div>

Not at all. Please do.

James Edward Gray II

> **···**
>
> On Jul 7, 2006, at 12:34 PM, brian.mattern@gmail.com wrote:
> 
> > Is it a spoiler if we post a resulting pangram (no code)?

---

<div class="post-metadata">

**Author:** ![Tom\_Rauchenwald](https://avatars.discourse-cdn.com/v4/letter/t/ccd318/32.png) [@Tom\_Rauchenwald](https://rubytalk.org/u/Tom_Rauchenwald)\
**Post date:** [10 July 2006 00:17 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/16 "2006-07-10T00:17:40Z")

</div>

"Tim Hollingsworth" \<timhollingsworth@gmail.com\> writes:

> Hello
> 
> First post here, by the way. Greetings to all:) New to ruby, love it, slaps  
> on backs all round.
> 
> While I haven't solved the pangram problem, I did find what appears to be a  
> bug in Numeric.divmod on large numbers, as can be seen when using  
> Bignum.modulo:
> 
> irb(main):017:0\> 1e22 % 1e21  
> =\> 0.0  
> irb(main):018:0\> 1e23 % 1e22  
> =\> 9.99999999999999e+021
> 
> whoops!

Not quite. 1e23 is a float, that's why you run into precision  
problems. This has been discussed here not long ago.  
If you use Bignums it works as expected:

> **···**
>
> ,----
> 
> > irb(main):001:0\> 1e23.class  
> > =\> Float  
> > irb(main):002:0\> (10\*\*23).class  
> > =\> Bignum  
> > irb(main):003:0\> 10\*\*23 % 10\*\*22  
> > =\> 0
> 
> `----
> 
> Tom

---

<div class="post-metadata">

**Author:** ![Daniel\_Martin](https://avatars.discourse-cdn.com/v4/letter/d/dc4da7/32.png) [@Daniel\_Martin](https://rubytalk.org/u/Daniel_Martin)\
**Post date:** [10 July 2006 02:17 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/17 "2006-07-10T02:17:58Z")

</div>

Simon Kröger \<SimonKroeger@gmx.de\> writes:

> (this is my second version, the other one is longer and uses a slightly  
> other algorithm (and is sometimes even faster) but i like this one)

Huh. I must say that when I saw your program, it wasn't at all what I  
expected - namely, you are changing all 26 letters at once, and then  
recalculating. I had assumed you were doing one letter at a time,  
which I found to be much, much faster than doing all 26 at once.

I'm going to have to look into NArray, clearly. In fact, I think I'll  
try converting my solution to use NArray.

---

<div class="post-metadata">

**Author:** ![Brian\_Mattern](https://avatars.discourse-cdn.com/v4/letter/b/47e85d/32.png) [@Brian\_Mattern](https://rubytalk.org/u/Brian_Mattern)\
**Post date:** [7 July 2006 18:38 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/18 "2006-07-07T18:38:36Z")

</div>

How about:

rephorm@boru ~/code/ruby/quiz/pangram $ time ruby pangram.rb \> p  
real 0m28.613s  
user 0m27.474s  
sys 0m1.000s

rephorm@boru ~/code/ruby/quiz/pangram $ cat p  
a ruby quiz solution found this sentence enumerating four a's, two b's,  
two c's, three d's, thirty-four e's, nine f's, three g's, eight h's,  
sixteen i's, one j, one k, three l's, two m's, twenty-five n's, fifteen  
o's, one p, two q's, eleven r's, twenty-nine s's, twenty-five t's, nine  
u's, four v's, nine w's, three x's, six y's, and two z's

rephorm@boru ~/code/ruby/quiz/pangram $ ruby verify.rb p  
Sentence passes!

It looks like the exact wording of the prelude has a large effect on how  
quickly a solution can be found.

Brian

> **···**
>
> On Sat, Jul 08, 2006 at 03:18:26AM +0900, James Edward Gray II wrote:
> 
> > On Jul 7, 2006, at 12:34 PM, brian.mattern@gmail.com wrote:
> > 
> > \>Is it a spoiler if we post a resulting pangram (no code)?
> > 
> > Not at all. Please do.

---

<div class="post-metadata">

**Author:** ![Tsuraan1](https://avatars.discourse-cdn.com/v4/letter/t/d07c76/32.png) [@Tsuraan1](https://rubytalk.org/u/Tsuraan1)\
**Post date:** [7 July 2006 19:18 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/19 "2006-07-07T19:18:03Z")

</div>

I was wondering where you can get an athlon64 300+ 🙂

> **···**
>
> On Jul 7, 2006, at 12:39 PM, brian.mattern@gmail.com wrote:
> 
> > On Sat, Jul 08, 2006 at 02:34:23AM +0900, brian.mattern@gmail.com \> wrote:
> > 
> > > Is it a spoiler if we post a resulting pangram (no code)?
> > > 
> > > I got one in:  
> > > real 1m17.089s  
> > > user 1m8.444s  
> > > sys 0m2.492s
> > > 
> > > on an athlon 64 300+
> > > 
> > > Brian
> > 
> > Actually, I lied. Almost correct, but at least one number is off...

---

<div class="post-metadata">

**Author:** ![Simon\_Kroger](https://avatars.discourse-cdn.com/v4/letter/s/49beb7/32.png) [@Simon\_Kroger](https://rubytalk.org/u/Simon_Kroger)\
**Post date:** [7 July 2006 20:47 UTC](https://rubytalk.org/t/quiz-panagrams-86/28732/20 "2006-07-07T20:47:51Z")

</div>

James Edward Gray II wrote:

> > Is it a spoiler if we post a resulting pangram (no code)?
> 
> Not at all. Please do.
> 
> James Edward Gray II

This is the pangram from simon, there are four a's, one b, one c, one d,  
thirty-two e's, seven f's, four g's, nine h's, eleven i's, one j, one k,  
two l's, four m's, twenty n's, eighteen o's, two p's, one q, thirteen  
r's, twenty-seven s's, eighteen t's, six u's, four v's, six w's, three  
x's, four y's, one z.  
Time: 8.39s

cheers

Simon

> **···**
>
> > On Jul 7, 2006, at 12:34 PM, brian.mattern@gmail.com wrote:

[Next page](https://rubytalk.org/t/quiz-panagrams-86/28732.md?page=2)
