# Panagrams (#86)

**URL:** <https://www.ruby-forum.com/t/panagrams-86/97515>\
**Category:** Ruby\
**Created:** [July 7, 2006, 5:03pm UTC](https://www.ruby-forum.com/t/panagrams-86/97515 "2006-07-07T17:03:29Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 8, 2006, 7:27am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/21 "2006-07-08T07:27:57Z")

</div>

On Sat, Jul 08, 2006 at 08:06:27AM +0900, Christoffer Lern? wrote:

> h’s, sixteen i’s, one j, one k, three l’s, two m’s, twentyfive n’s,  
> fifteen o’s, one p, two q’s, eleven r’s, twentynine s’s, twentyfive  
> t’s, nine u’s, four v’s, nine w’s, three x’s, six y’s and two z’s
> 
> …but it took my program 8018361 iterations.
> 
> Is it the optimization or the algorithm I got wrong?
> 
> /Christoffer

I ran mine again and after 10 minutes it hadn’t found anything. I did a  
basic “randomized Robisonizing” algorithm, so, I think the speed just  
depends on luck.

from [http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt](http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt) :

randomized Robisonizing:  
let’s say in candidate(N) which includes the phrase  
"… eight `a's ..." actually contains 13 `a’s. then  
in candidate(N+1) you pick a random number R between 8  
and 13 (inclusive) and put the phrase “… [R] `a’s  
…”. you do this for each letter.

Brian

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 8, 2006, 6:37pm UTC](https://www.ruby-forum.com/t/panagrams-86/97515/22 "2006-07-08T18:37:40Z")

</div>

Christoffer Lernö wrote:

> sixteen i’s, one j, one k, three l’s, two m’s, twentyfive n’s, fifteen  
> o’s, one p, two q’s, eleven r’s, twentynine s’s, twentyfive t’s, nine  
> u’s, four v’s, nine w’s, three x’s, six y’s and two z’s
> 
> …but it took my program 8018361 iterations.
> 
> Is it the optimization or the algorithm I got wrong?
> 
> /Christoffer

Well, what’s an iteration in your code?

I found a solution (the same) to your sentence above after  
255062 full circles over all 26 letters. (changing only some  
of them each time) It took 36s on my laptop. (Pentium M 2.1 GHz)

I ran it again and it took 134s and did 10105853 changes to  
the original guess (counting each changed count separately).

Obviously (at least with my algorithm) it depends on luck.  
And for the total Time it takes it depends on your PC and  
optimization.

cheers

Simon

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 9, 2006, 7:53am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/23 "2006-07-09T07:53:37Z")

</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:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 9, 2006, 7:49pm UTC](https://www.ruby-forum.com/t/panagrams-86/97515/24 "2006-07-09T19:49:40Z")

</div>

Hi!

Plese note that this message uses ISO 8859-7 and contains greek  
letters. For MUAs not capable of displaying this encodig I added  
transliteration as well as TeX notation.

At Sat, 8 Jul 2006 00:21:37 +0900, Brown, Warren wrote:

> > So is it “pangram” or “panagram”?
> 
> The word is “pangram”.

The origin of “pangram” is greek “pan gramma” (“ðáí ãñáììá”, TeX  
notation “\pi\alpha\nu \gamma\rho\alpha\mu\mu\alpha”) meaning “every  
letter”.

Josef ‘Jupp’ Schugt

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 9, 2006, 10:31pm UTC](https://www.ruby-forum.com/t/panagrams-86/97515/25 "2006-07-09T22:31:17Z")

</div>

Hi,

## here is my solution: (some remarks are below)

require ‘narray’

class NArray;  
include Enumerable;  
srand(1)  
end

class Fixnum  
NUMS = %w(zero one two three four five six seven eight nine ten  
eleven) +  
%w(twelve thirteen fourteen fifteen sixteen seventeen eighteen  
nineteen)  
TENS = %w(no teen twenty thirty forty fifty sixty seventy eighty  
ninety)

def to\_english  
return NUMS[self] if self \< 20  
return TENS[self / 10] if (self % 10).zero?  
TENS[self / 10] + ‘-’ + NUMS[self % 10]  
end  
end

class String  
def to\_na  
freq = NArray.int(26)  
each\_byte do |b|  
freq[b - ?a] += 1 if b \>= ?a and b \<= ?z  
freq[b - ?A] += 1 if b \>= ?A and b \<= ?Z  
end  
freq  
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  
guess.add! rnd.random!.mul!(real.sbt!(guess)).round  
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:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 9, 2006, 8:44pm UTC](https://www.ruby-forum.com/t/panagrams-86/97515/26 "2006-07-09T20:44:47Z")

</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/](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 Q. 86, Pangrams: Solution by Mat S. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

# uses numeric\_spell library available from [http://tanjero.com/svn/](http://tanjero.com/svn/)

plugins/numeric\_spell/

require ‘numeric\_spell’

class SelfDocumentingPangram  
LETTERS = (?a…?z).to\_a

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

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

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

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

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

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

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

if ARGV[0] =~ /test/i  
require ‘test/unit’

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

```
   # check case for a..z, A..Z, and some punctiation that we're

```

likely to use  
string = ((‘a’…‘z’).to\_a + (‘A’…‘Z’).to\_a + [‘'’, ‘,’, ‘.’,  
‘-’]).to\_s  
count = SelfDocumentingPangram.new.count(string)  
assert\_equal(26, count.length)  
count.each do |key, value|  
assert\_equal(2, value)  
end  
end

```
def test_count_to_s
   assert_equal("one a", SelfDocumentingPangram.new.count_to_s(?

```

a, 1))  
assert\_equal(“fifteen z’s”,  
SelfDocumentingPangram.new.count\_to\_s(?z, 15))  
assert\_equal(“forty-two d’s”,  
SelfDocumentingPangram.new.count\_to\_s(?d, 42))  
end

```
 def test_counts_to_s
   start = "The last of these contained "
   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."  
pangram = SelfDocumentingPangram.new  
result = pangram.counts\_to\_s(pangram.count(start))  
assert\_equal(expected, result)  
end

```
 def test_rand_between
   100.times do
     a = rand(100)
     b = [a, rand(100)].max
     c = SelfDocumentingPangram.new.rand_between(a,b)
     assert (a..b) === c, "#{c} is not between #{a} and #{b}"
   end
 end

 def test_add_count
   pangram = SelfDocumentingPangram.new("hi ")
   count = Hash.new(0)
   expected = "hi " + pangram.counts_to_s(Hash.new(0))
   assert_equal(expected, pangram.add_count(Hash.new(0)))
 end

 # runs the SelfDocumentingPangram class to verify that it can

```

produce the pangrams found at  
# [http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt](http://www.cs.indiana.edu/~tanaka/GEB/pangram.txt)  
def test\_to\_s  
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.”  
assert\_equal(pangram1, SelfDocumentingPangram.new("This  
pangram tallies ").to\_s)

```
   #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."  
#assert\_equal(pantram2, SelfDocumentingPangram.new("This  
computer-generated pangram contains ").to\_s)  
end

```
 # This is mainly a sanity check to see that a pangram will

```

evaluate to itself when counted and regenerated  
def test\_approach  
prefix = "This pangram tallies "  
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.”  
pangram = SelfDocumentingPangram.new(prefix)  
assert\_equal(solution, pangram.add\_count(pangram.count  
(solution)))

```
   prefix = "This terribly inefficient pangram contains "
   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."  
pangram = SelfDocumentingPangram.new(prefix)  
assert\_equal(solution, pangram.add\_count(pangram.count  
(solution)))  
end  
end  
else  
puts SelfDocumentingPangram.new("This terribly inefficient pangram  
contains ").to\_s  
end

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 10, 2006, 2:20am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/27 "2006-07-10T02:20:10Z")

</div>

“Tim Hollingsworth” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> =\> 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:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 10, 2006, 1:52am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/28 "2006-07-10T01:52:36Z")

</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

def initialize  
@values = Hash.new  
@ranges = []  
end

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

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

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

end

class Integer

@@rules = RangeMap.new

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

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

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

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

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

puts 123456789.to\_english

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 10, 2006, 2:47am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/29 "2006-07-10T02:47:19Z")

</div>

On 09/07/2006, at 8:17 PM, Tom R. wrote:

> > bug in Numeric.divmod on large numbers, as can be seen when using  
> > problems. This has been discussed here not long ago.
> 
> Tom

aha right you are! I was using to\_i in my code, and there lies my  
problem.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 10, 2006, 4:19am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/30 "2006-07-10T04:19:59Z")

</div>

Simon Kröger [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 10, 2006, 4:07am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/31 "2006-07-10T04:07:22Z")

</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

Ones = %w[zero one two three four five six seven eight nine]  
Teen = %w[ ten eleven twelve thirteen fourteen fifteen  
sixteen seventeen eighteen nineteen ]  
Tens = %w[ zero ten twenty thirty forty fifty  
sixty seventy eighty ninety ]  
Mega = %w[none thousand million billion]

def to\_en  
places = to\_s.split(//).collect {|s| s.to\_i}.reverse  
name = []  
((places.length + 2) / 3).times do |p|  
strings = Integer.trio(places[p \* 3, 3])  
name.push(Mega[p]) if strings.length \> 0 and p \> 0  
name += strings  
end  
name.push(Ones[0]) unless name.length \> 0  
name.reverse.join(" ")  
end

private

def Integer.trio(places)  
strings = []  
if places[1] == 1  
strings.push(Teen[places[0]])  
elsif places[1] and places[1] \> 0  
strings.push(places[0] == 0 ? Tens[places[1]] :  
“#{Tens[places[1]]}-#{Ones[places[0]]}”)  
elsif places[0] \> 0  
strings.push(Ones[places[0]])  
end  
if places[2] and places[2] \> 0  
strings.push(“hundred”, Ones[places[2]])  
end  
strings  
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)  
s = “”  
(‘a’…‘z’).each{|x| big, r = big.divmod(256); s += "%2d " % r}  
s  
end

def find\_sentence(prefix, suffix, initial = {})  
letterre = Regexp.new(’(?i:[a-z])’);  
letters = (‘a’…‘z’).to\_a  
letterpower = Hash.new {|h,k| h[k] = 1 \<\< ((k[0]-?a)\*8)}  
lettershifts = letters.map {|x| ((x[0]-?a)\*8)}  
basesentence = prefix + letters.map {|x|  
(x == ‘z’? ‘and ’ : ‘’) + “\_ ‘#{x}’”}.join(’, ') + suffix  
basefreq = 0  
basesentence.scan(letterre) {|x| basefreq +=  
letterpower[x.downcase]}

# enfreq holds the letter counts that spelling out that number adds

to

# the sentence.

# E.g. enfreq[1] == letterpower[‘o’] + letterpower[‘n’] +

letterpower[‘e’]  
enfreq = Hash.new {|h,k|  
if k \> 255 then  
h[k] = h[k \>\> 8]  
else  
h[k] = 0  
k.to\_en.scan(letterre) {|x| h[k] += letterpower[x.downcase]}  
h[k] += letterpower[‘s’] if k != 1  
end  
h[k]  
}  
guessfreq = 0  
letters.each{|x|  
guessfreq += (initial[x]||0) \* letterpower[x]  
}  
guessfreq = basefreq if guessfreq == 0  
actualfreq = 0  
sentence = “”  
begin  
cyclecount, adjusts = 0, 0  
until guessfreq == actualfreq do  
if actualfreq \> 0 then  
lettershifts.each{ |y|  
g = 0xFF & (guessfreq \>\> y)  
a = 0xFF & (actualfreq \>\> y)  
if (g != a)  
d = (g-a).abs  
r1 = rand(d+1)  
r2 = rand(d+1)  
r1=r2 if r1 \< r2  
r1=-r1 if a\<g  
if (r1 != 0) then  
adjusts += 1  
guessfreq += r1 \<\< y  
actualfreq += enfreq[g+r1] - enfreq[g]  
end  
end  
}  
else  
actualfreq = basefreq  
lettershifts.each {|y|  
g = 0xFF & (guessfreq \>\> y)  
actualfreq += enfreq[g]  
}  
end  
#DEBUG

# puts explode(actualfreq)

# return “\_\_\_” if cyclecount \> 10\_000\_000

```
  cyclecount += 1
end

```

ensure  
puts [cyclecount, adjusts].inspect  
end  
sentence = prefix + (‘a’…‘z’).map {|x|  
g = (guessfreq \>\> ((x[0]-?a)\*8))%256  
(x == ‘z’? ‘and ’ : ‘’) + “#{g.to\_en} ‘#{x}’” +  
(g==1 ? ‘’ : ‘s’)}.join(’, ') + suffix  
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 removed\_email\_address@domain.invalid produced a sentence with ",

"Daniel M.'s sallowsgram program produced a sentence with ",

# "This terribly inefficient pangram contains ",

# "Darren’s ruby panagram program found this sentence which contains

exactly ",  
“.”

# optional seed value - this is the same as the sentence given

# in the quiz description.

# {‘a’=\>9,‘b’=\>2,‘c’=\>5,‘d’=\>4,‘e’=\>35,

# ‘f’=\>9,‘g’=\>3,‘h’=\>9,‘i’=\>16,‘j’=\>1,

# ‘k’=\>1,‘l’=\>2,‘m’=\>3,‘n’=\>27,‘o’=\>14,

# ‘p’=\>3,‘q’=\>1,‘r’=\>15,‘s’=\>34,‘t’=\>22,

# ‘u’=\>6,‘v’=\>6,‘w’=\>7,‘x’=\>6,‘y’=\>7,

# ‘z’=\>1}

)

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 10, 2006, 10:56am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/32 "2006-07-10T10:56:57Z")

</div>

> 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.

Ok, I will post my other version as soon as I’m home again.  
Btw: nice idea to use Bignums 🙂

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

yes, some of the stuff is a little bit mind bending (using ‘true’ or  
‘false’ for slicing dimensions for example) but you get true C speed  
while implementing in ruby - that’s fun.

cheers

Simon

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 10, 2006, 12:08pm UTC](https://www.ruby-forum.com/t/panagrams-86/97515/33 "2006-07-10T12:08:47Z")

</div>

“Kroeger, Simon (ext)” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> yes, some of the stuff is a little bit mind bending (using ‘true’ or  
> ‘false’ for slicing dimensions for example) but you get true C speed  
> while implementing in ruby - that’s fun.

Unfortunately, I can’t actually use NArray on my fast machine as I  
don’t have VC 6.0 on it. (Windows, using the one-click installer)

“true C speed” on my 500Mhz K6 that’s already heavily loaded with lots  
of spamassassin work is significantly less impressive.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 10, 2006, 8:40pm UTC](https://www.ruby-forum.com/t/panagrams-86/97515/34 "2006-07-10T20:40:15Z")

</div>

> > 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.
> 
> Ok, I will post my other version as soon as I’m home again.

## Here is the second version (which was my first version):

require ‘narray’  
class NArray; include Enumerable; end  
srand(1)

NUMS = %w(no one two three four five six seven eight nine ten eleven) +  
%w(twelve thirteen fourteen fifteen sixteen seventeen eighteen  
nineteen)  
TENS = [nil] + %w(teen twenty thirty forty fifty sixty seventy eighty  
ninety)

class Fixnum  
def to\_english  
return NUMS[self] if self \< 20  
return TENS[self / 10] if (self % 10).zero?  
TENS[self / 10] + ‘-’ + NUMS[self % 10]  
end  
end

class String  
def to\_na  
count = NArray.byte(26)  
each\_byte do |b|  
count[b - ?a] += 1 if b \>= ?a and b \<= ?z  
count[b - ?A] += 1 if b \>= ?A and b \<= ?Z  
end  
count  
end  
end

text = "This is a pangram from simon, it contains "  
number = (0…99).map{|i| i.to\_english.to\_na + (i \> 1 ? ‘s’ : ‘’).to\_na}  
guess = NArray.byte(26).fill!(1)  
real = guess + text.to\_na + ‘and’.to\_na + (number[1] \* 26)

i, r, g, changed = nil, nil, nil, true  
while changed do  
changed = false  
26.times do |i|  
g = guess[i]  
r = real[i]  
if g != r  
real.sbt! number[g]  
guess[i] = g = (g \< r ? g : r) + rand((g - r).abs + 1)  
real.add! number[g]  
changed = true  
end  
end  
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)

It’s quite similar except the loop.

cheers

Simon

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 11, 2006, 2:10am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/35 "2006-07-11T02:10:09Z")

</div>

> well, I’m not sure if it is possible for every start of a sentence,  
> perhaps try another. (any thoughts from the mathematicians out there?)

Not all sentences can be completed to a pangram. Try a sentence  
containing 999998 a’s and a less than, say, 500 of all other letters.  
Yes, I agree that such an example cannot be realized by en english  
sentence, but a greater number of other letters could be allowed by  
appropriately lowering the number of a’s (it could be difficult to  
prove, though).

In italian one can construct a counterexample with ‘only’ 1998 l’s  
(this is based on the fact that 1999 contains two l’s, while 2000  
containes 2, and no number less than 1000 contains l’s).

Paolo C.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 11, 2006, 11:07pm UTC](https://www.ruby-forum.com/t/panagrams-86/97515/36 "2006-07-11T23:07:33Z")

</div>

Simon Kröger [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> ## Here is the second version (which was my first version):
> 
> * * *
> 
> It’s quite similar except the loop.

Indeed, this is more what I expected - and this is a distinctly  
different algorithm than the first solution you posted. In fact, your  
main loop is pretty much identical to mine, except that you were using  
NArray whereas I was using a bignum to hold the frequency. This meant  
that it was much more cumbersome for me to extract each component and  
update than it was for you.

I think that this algorithm itself is significantly faster than the  
update-all-26-at-once version, and the only reason you see an  
advantage to the update-all-26-at-once approach is because you can do  
almost all of it by calling NArray vector operations.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 11, 2006, 2:10am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/37 "2006-07-11T02:10:20Z")

</div>

On 7/11/06, Paolo C. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> In italian one can construct a counterexample with ‘only’ 1998 l’s  
> (this is based on the fact that 1999 contains two l’s, while 2000  
> containes 2, and no number less than 1000 contains l’s).

err… 2000 contains _one_ l in italian.

Paolo C.

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 12, 2006, 6:28am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/38 "2006-07-12T06:28:47Z")

</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

def teen  
case self  
when 0: “ten”  
when 1: “eleven”  
when 2: “twelve”  
else in\_compound + “teen”  
end  
end

def ten  
case self  
when 1: “ten”  
when 2: “twenty”  
else in\_compound + “ty”  
end  
end

def in\_compound  
case self  
when 3: “thir”  
when 5: “fif”  
when 8: “eigh”  
else to\_en  
end  
end

def to\_en(ands=true)  
small\_nums = [“”] + %w[one two three four five six seven eight nine]  
if self \< 10: small\_nums[self]  
elsif self \< 20: (self % 10).teen  
elsif self \< 100:  
result = (self/10).ten  
result += “-” if (self % 10) != 0  
result += (self % 10).to\_en  
return result  
elsif self \< 1000  
if self%100 != 0 and ands  
(self/100).to\_en(ands)+" hundred and “+(self%100).to\_en(ands)  
else ((self/100).to\_en(ands)+  
" hundred “+(self%100).to\_en(ands)).chomp(” “)  
end  
else  
front,back = case (self.to\_s.length) % 3  
when 0: [0…2,3…-1].map{|i| self.to\_s[i]}.map{|i| i.to\_i}  
when 2: [0…1,2…-1].map{|i| self.to\_s[i]}.map{|i| i.to\_i}  
when 1: [0…0,1…-1].map{|i| self.to\_s[i]}.map{|i| i.to\_i}  
end  
degree = [””] + %w[thousand million billion trillion quadrillion  
quintillion sextillion septillion octillion nonillion decillion  
undecillion duodecillion tredecillion quattuordecillion  
quindecillion sexdecillion septdecillion novemdecillion  
vigintillion unvigintillion duovigintillion trevigintillion  
quattuorvigintillion quinvigintillion sexvigintillion  
septvigintillion octovigintillion novemvigintillion trigintillion  
untregintillion duotrigintillion googol]  
result = front.to\_en(false) + " " + degree[(self.to\_s.length-1)/3]  
result += if back \> 99: “, "  
elsif back \> 0: ands ? " and " : " "  
else “”  
end  
result += back.to\_en(ands)  
return result.chomp(” ")  
end  
end

end

class Pangram

```
LETTERS = ('a'..'z').to_a
RE = /[a-z]/

def initialize sentence
	@original_sentence = sentence
	@count = Hash.new(1)
end

def self_documenting_pangram
	while true
		make_sentence_with_current_count
		LETTERS.each do |letter|
			return @sentence if is_correct?
			update_count letter
		end
	end
end

```

private

```
def update_count letter
	current = @count[letter]
	real = get_count letter
	@count[letter] = rand_between current, real
end

def rand_between n1, n2
	case (n1 - n2).abs
	when 0, 1
		n2
	when 2
		[n1, n2].min + 1
	else
		[n1, n2].min + rand((n1 - n2).abs - 2) + 1
	end
end

def get_count letter
	@sentence.downcase.scan(/#{letter}/).size
end

def is_correct?
	@count ==
	@sentence.downcase.scan(RE).inject(Hash.new(0)) do |m, i|
		m[i] += 1
		m
	end
end

def make_sentence_with_current_count
	@sentence = @original_sentence + " " +
	LETTERS.inject("") do |memo, letter|
		memo << "and " if letter == 'z'
		memo << @count[letter].to_en + " " + letter
		if @count[letter] > 1
			memo << "'s"
		end
		memo << (letter == 'z' ? "." : ", ")
	end
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

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 13, 2006, 12:11am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/39 "2006-07-13T00:11:35Z")

</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 = {  
1 =\> ‘one’, 2 =\> ‘two’, 3 =\> ‘three’, 4 =\> ‘four’, 5 =\> ‘five’, 6  
=\> ‘six’,  
7 =\> ‘seven’, 8 =\> ‘eight’, 9 =\> ‘nine’, 10 =\> ‘ten’, 11 =\>  
‘eleven’,  
12 =\> ‘twelve’, 13 =\> ‘thirteen’, 14 =\> ‘fourteen’, 15 =\> ‘fifteen’,  
16 =\> ‘sixteen’, 17 =\> ‘seventeen’, 18 =\> ‘eighteen’, 19 =\>  
‘nineteen’,  
20 =\> ‘twenty’, 30 =\> ‘thirty’, 40 =\> ‘forty’, 50 =\> ‘fifty’, 60  
=\> ‘sixty’,  
70 =\> ‘seventy’, 80 =\> ‘eighty’, 90 =\> ‘ninety’  
}

def spelledNumber(x)  
if x \>= 100  
print “must be 99 or less”  
exit  
elsif x \<= 20  
$snum[x]  
else  
tens = (x / 10).to\_i \* 10  
if x - tens == 0  
$snum[x]  
else  
$snum[tens] + “-” + $snum[x - tens]  
end  
end  
end

def checkIfTrue(s)  
realCount = {}  
LETTERS.each do |c|  
realCount[c] = s.count©  
end  
if $fixedCount == realCount  
puts “Found it:”  
puts s  
exit  
end  
$fixedCount.each do |key, value|  
x = s.count(key)  
y = value  
$fixedCount[key] = randomizer(x, y)  
end  
$fixedCount  
end

def randomizer(x, y)  
if x == y then return x end  
if x \> y then x, y = y, x end  
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  
(1…10000).each do  
s = seed  
LETTERS.each do |c|  
s += spelledNumber($fixedCount[c])  
s += " ‘#{c}’"  
s += $fixedCount[c] \>= 2 ? "s, " : ", "  
end  
$fixedCount = checkIfTrue(s)  
end  
print “\t10K blip…\n”  
end  
###################

---

<div class="post-metadata">

**Author:** ![bbazzarrakk](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/47e85d/32.png) [@bbazzarrakk](https://www.ruby-forum.com/u/bbazzarrakk)\
**Post date:** [July 13, 2006, 10:45am UTC](https://www.ruby-forum.com/t/panagrams-86/97515/40 "2006-07-13T10:45:28Z")

</div>

“Nasir K.” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> You may notice that I have tried both changing current value one at a time  
> and also accumulating changes in @nchar\_cnt\_arr and changing in one go. The  
> convergence has been elusive in either case.  
> One way for me is to start looking at other solutions (which I will  
> certainly do), but it would be a great help if someone points me towards a  
> flaw in this.

I’ll look more at your code later, but have you tried different  
initial sentence parts? Specifically, have your tried the initial  
bits that others were posting over the weekend?

I found that my code was unable to converge (after four hours) if I  
began with:

The program from removed\_email\_address@domain.invalid produced a sentence with

But when I switched to:

Daniel M.'s sallowsgram program produced a sentence with

I then got convergence in less than ten seconds. Some initial  
sequences are just impossible to complete.

[Previous page](https://www.ruby-forum.com/t/panagrams-86/97515.md?page=1)

[Next page](https://www.ruby-forum.com/t/panagrams-86/97515.md?page=3)
