[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

[QUIZ] String Equations (#112

James Gray

2/2/2007 1:08:00 PM

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

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.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

This quiz was adapted from an ACM programming challenge at the suggestion of
Gavin Kistner.

Let's define two operators for a simple set of string equations.

First, we will use the plus (+) operator and keep Ruby's meaning of
concatenation for it. Therefore, "james" + "gray" is "jamesgray".

The other operator we will support is equality (==). Two strings will be
considered equal if they are anagrams (they contain the same letters in a
possibly different ordering). In other words, "cinema" == "iceman".

Using these two operators we can build equations:

"i" + "am" + "lord" + "voldemort" == "tom" + "marvolo" + "riddle"

This week's quiz is to write a program that accepts a list of words on STDIN and
outputs any one string equation using those words, assuming there is one. Each
word in the list may be used zero or more times, but only on one side of the
equation. The program should exit(0) on success and exit(1), without printing
any output, if a solution cannot be found.

Let's sidestep case sensitivity issues by lower casing all incoming words. You
can also remove all non-alphanumeric characters.

Posting word lists and/or equations is not spoiler material.

19 Answers

Daniel Lucraft

2/2/2007 4:44:00 PM

0

cool quiz.

I'm currently having trouble with this evil word list:
bwu
ha
bwuhahahahahaha


--
Posted via http://www.ruby-....

James Gray

2/2/2007 4:50:00 PM

0

On Feb 2, 2007, at 10:43 AM, Daniel Lucraft wrote:

> cool quiz.
>
> I'm currently having trouble with this evil word list:
> bwu
> ha
> bwuhahahahahaha

I like it. ;)

James Edward Gray II

Daniel Lucraft

2/2/2007 5:59:00 PM

0

James Gray
> one. Each
> word in the list may be used zero or more times, but only on one side of
> the
> equation.

Shall we also add the requirement that at least one word is used at
least once? :)

Daniel Lucraft

--
Posted via http://www.ruby-....

James Gray

2/2/2007 6:53:00 PM

0

On Feb 2, 2007, at 11:59 AM, Daniel Lucraft wrote:

> James Gray
>> one. Each
>> word in the list may be used zero or more times, but only on one
>> side of
>> the
>> equation.
>
> Shall we also add the requirement that at least one word is used at
> least once? :)

Yes, see my post about this a little earlier.

James Edward Gray II

Sander Land

2/2/2007 10:12:00 PM

0

On 2/2/07, Daniel Lucraft <dan@fluentradical.com> wrote:
> cool quiz.
>
> I'm currently having trouble with this evil word list:
> bwu
> ha
> bwuhahahahahaha

Here are the tests I'm using:
http://pastie.cabo...

Started
....
Finished in 0.735 seconds.
4 tests, 14 assertions, 0 failures, 0 errors

Tests are boolean only because my solution doesn't do anything else...

Daniel Lucraft

2/3/2007 12:28:00 PM

0

Sander Land wrote:
> Here are the tests I'm using:
> http://pastie.cabo...
>
> Started
> ....
> Finished in 0.735 seconds.
> 4 tests, 14 assertions, 0 failures, 0 errors
>
> Tests are boolean only because my solution doesn't do anything else...

Thanks Sander that was useful in finding some bugs! I've added what
should be the longest equation possible from your test_long word list:
http://pastie.cabo...

Daniel Lucraft

--
Posted via http://www.ruby-....

Kalman Noel

2/4/2007 10:39:00 AM

0

Ruby Quiz (James E. Gray):
> This week's quiz is to write a program that accepts a list of words on STDIN and
> outputs any one string equation using those words, assuming there is one. Each
> word in the list may be used zero or more times, but only on one side of the
> equation. The program should exit(0) on success and exit(1), without printing
> any output, if a solution cannot be found.

Shouldn't there be a requirement that an equations is invalid if there is
another, shorter equation that uses the same words? Otherwise:

$ ruby quiz112.rb foo oof
"foo" = "oof"
"foo" + "foo" = "oof" + "oof"
"foo" + "foo" + "foo" = "oof" + "oof" + "oof"
...

Kalman

Daniel Lucraft

2/4/2007 11:17:00 AM

0

Kalman Noel wrote:
> Shouldn't there be a requirement that an equations is invalid if there
> is
> another, shorter equation that uses the same words? Otherwise:
>
> $ ruby quiz112.rb foo oof
> "foo" = "oof"
> "foo" + "foo" = "oof" + "oof"
> "foo" + "foo" + "foo" = "oof" + "oof" + "oof"
> ...
>
> Kalman

That sounds right. How about if you have an equation a == b, you should
not be able to find equations c == d, and e == f such that c + e makes a
and d + f makes b?

Daniel

--
Posted via http://www.ruby-....

Paulo Köch

2/4/2007 2:13:00 PM

0


On 2007/02/04, at 11:17, Daniel Lucraft wrote:
> That sounds right. How about if you have an equation a == b, you
> should
> not be able to find equations c == d, and e == f such that c + e
> makes a
> and d + f makes b?

Wouldn't a prolog script be better at describing all the rules than
trying to figuring them out?

If someone developed a prolog script that would behave as the final
ruby script should, we would then have the specs without a shadow of
doubt.

Just don't look at me to write it =P

Paulo Jorge Duarte Köch
paulo.koch@gmail.com



Paolo Negri

2/4/2007 3:20:00 PM

0

here's my solution for this quiz
It's quite slow, so you may want to tweak LENGTH_LIMIT constant to
have the script running in a reasonable time.
If you want to use this script to find equations for all the terms in
wikipedia you may want to look at some other solution.
It should behave as required from the original quiz.
I don't think it respects all the rules added after the original quiz
requirement :)

With this word list

bici
bwu
ha
bwuhahahahahaha
boci
cibo
bibo
bocibici
bibobici
ci
bo

produces 166 equations

Any comment is really welcome. Thanks for the fun.

#!/usr/bin/env ruby
require 'set'
words = $stdin
#a version of hash that supports + - / %
class AlgHash < Hash
def initialize(default = 0)
super(default)
end
def keys
super.to_set
end
alias len length
%w(+ - % /).each do |sign|
eval <<-END_EVAL
def #{sign}(other)
res = AlgHash.new
total_keys = self.keys + other.keys
total_keys.each {|k| res[k] = self[k] #{sign} other[k]}
res
end
END_EVAL
end
def base?(other)
other = other.signature unless other.respond_to? :keys
return false unless self.keys == other.keys
res = other % self
if res.values.uniq == [0]
if (multiplier = (other / self).values.uniq).size == 1
multiplier.first
end
end
end
end
#some utility modules we want for our special version of string
#and for Arrays
#we want to have some methods used in Set available for arrays and string
module SpecComp

def multiple?(other)
return false unless (self.len % other.len) == 0
if (self.signature % other.signature).values.uniq == [0] &&
(multiplier = (self.signature / other.signature).values.uniq).size == 1
multiplier.first
else
false
end
end

def base?(other)
other.multiple?(self)
end

def minus(other)
(self.signature - other.signature).delete_if {|k, v| v == 0}
end


%w(subset? superset? intersection).each do |comp_method|
eval <<-END_EVAL
def #{comp_method}(other)
return self.origin.send("#{comp_method}", other.origin)
end
END_EVAL
end

end
#a rich indexed version of string
#every string is lowercase and non alphanumeric chars are stripped
#every string has a signature which is hash with letters as keys and number
#of occurrences of the letter as values
#some arithmetics is possible on the signature of the strings
#the string reply to some Set class method that will be useful when we'll
#compare string groups
class HashedString
attr_accessor :signature, :origin, :len
include SpecComp
def initialize(string)
@signature = AlgHash.new
sane_string = string.downcase.unpack('c*')
sane_string.delete_if {|c| c < 49 || c > 122}
sane_string.each {|c| @signature[c] = @signature[c] + 1}
@len = sane_string.length
@sym = sane_string.pack('c*').to_sym
@origin = [@sym].to_set
end

def ==(other)
self.signature == other.signature && self.origin != other.origin
end

def +(other)
[self] + other
end

def *(integer)
ret = []
integer.times {ret += self}
ret
end

def to_s
return "\"#{@sym.to_s}\""
end

def to_ary
[self]
end

def to_sym
@sym
end
end
#Array have signature too
class Array
include SpecComp
def to_s
(self.map {|w| w.to_s}).join(' + ')
end
def signature
@signature ||= self.inject(AlgHash.new) {|sum, element| sum +
element.signature}
end
def origin
@origin ||= (self.map {|element| element.to_sym}).to_set
end
def len
@len ||= self.inject(0) {|len, element| len + element.len}
end
end
#the anagram finder
#LENGTH_LIMIT is necessary if you don't want your PC busy for years
class EquationFinder
LENGTH_LIMIT = 1000
attr_reader :success
def initialize(words_list)
@words_list = words_list.to_a.map! {|w| HashedString.new(w)}
@search_hash = Hash.new() {|h, k| h[k] = []}
@words_list.each_with_index do |word, index|
@search_hash[word.signature.keys.to_a.sort] << word
to_add = []
@search_hash.each_value do |other_words|
other_words.each do |other_word|
if !word.subset?(other_word) && (other_word.origin.size <
LENGTH_LIMIT)
sum = word + other_word
to_add << sum
end
end
end
to_add.each {|v| @search_hash[v.signature.keys.to_a.sort] << v}
end
end
def write_equation(left_string, right_string)
@success = 0 unless @success
puts left_string.to_s + " == " + right_string.to_s
end
def find
@search_hash.each_value do |homogeneus_words_list|
homogeneus_words_list.size.times do
word = homogeneus_words_list.pop
find_equation_in_array(word, homogeneus_words_list)
end
end
end
def find_equation_in_array(word, array)
array.each { |other_word| equation(word, other_word) }
end
def equation(left, right)
if right.intersection(left).empty?
if left == right
write_equation(left, right)
elsif multiplier = left.multiple?(right)
write_equation(left, right * multiplier)
elsif multiplier = left.base?(right)
write_equation(left * multiplier, right)
else
try_other_formulas(left, right)
end
end
end
def try_other_formulas(left, right)
short, long = [left, right].sort! {|a, b| a.len <=> b.len}
short = [short] if short.instance_of? HashedString
#begin
return false if (short.collect {|o| o.len}).min > (long.len/2)
#rescue
# p short
# p short.origin
# raise
#end
difference = (long.minus short)
short.each do |short_origin|
if multiplier = (short_origin.signature.base? difference)
write_equation((short + short_origin * multiplier), long) if
multiplier > 0
end
end
end
end
finder = EquationFinder.new($stdin)
finder.find
exit(finder.success || 1)