Ive been trying to get this script to work for way too long and I have
no idea how to solve my problem. Towards the end, when I open the text
file to find the word to compare my permutations to, the program only
finds the first word on the list which is “Aarhus”. I can put scrambled
versions of “Aarhus” and it will print out “Aarhus” but if I try to
write the unscrambled version of the next word down the text file, it
only spits out “Aarhus” again!
def to_array
print "Enter input string: "
input_str = STDIN.gets.chomp
letter_array = [ ]
input_str.each_char do |c|
letter_array << c
end
return letter_array
end
result = to_array
a = result
perms = [ ]
a.permutation(a.length) do |perm|
perms << perm.join
end
fin = File.open(“dict.txt”, “r”)
while line = fin.gets
word = line.chomp
if perm = word
print perm
exit
end
end
fin.close
You put “if perm = word”, which will assign the value of “word” to
“perm”. That will then return that value to “if”, which will evaluate to
true because it is a string.
What you meant to put is “if perm == word”.
= is assignment operator, and == is used to check for equality. Also,
you probably want to use Array#index or Array#include?, as you want to
check if one of
many possible permutations match the word.
There seems to be something missing in the code you posted. Inside the
while loop, tge variable “perm” is not defined. Lastly, use break
instead of exit so that the file gets closed properly.
The least amount of changes to make the code work:
def to_array
input_str = STDIN.gets.chomp
letter_array = [ ]
input_str.each_char do |c|
letter_array << c
end
return letter_array
end
result = to_array
a = result
perms = [ ]
a.permutation(a.length) do |perm|
perms << perm.join
end
fin = File.open("dict.txt", "r")
while line = fin.gets
word = line.chomp
if perms.index(word)
puts word
break
end
end
fin.close
But ruby provides many functions to make this task easier:
dic = "dict.txt"
perms = gets.chomp.chars.to_a.permutation.map(&:join)
word = File.read(dic).lines.find{|line|perms.include?(line.chomp)}
if word
puts "matching word found: #{word}"
else
puts "no matching word found"
end
Using a Hash is faster, but the main problem remains: For a word of size
n, there are n! permutations. Try it with the infamous German word
“Donaudampfschifffahrtskapitaen”, it takes a long time.
A better approach is to use an invariant: the number if occurences of
each letter in a word. All you need to do is search the dictionary for a
word with the same counts.
input, scrambled word
scramble = “Donaudampfschifffahrtskapitaen”
count how often each character occurs
counts = []
scramble.downcase!
scramble.chars.to_a.uniq.each do |c|
counts << [c,scramble.count©]
end
open the dictionary and search for any word with the same character
counts
File.open(“dict.txt”) do |io|
io.each_line do |line|
line = line.chomp.strip.downcase
next if line.empty?
if counts.all?{|char,count|line.count(char)==count}
puts “matching word: #{line}”
break
end
end
end
If you’re going to check multiple word, read and process the dictionary
once and store it in a hash.
input
scrambled word
scramble = “Donaudampfschifffahrtskapitaen”
path to dictionary
dic_file = “dict.txt”
counts the number of characters
def hash(x)
Array.new(26){|i|x.count((i+97).chr)}.pack(“c*”)
end
create dictionary
DIC = {}
File.open(dic_file) do |io|
io.each_line do |line|
line = line.chomp.strip.downcase
next if line.empty?
DIC[hash(line)] = line
end
end
checks dictionary for matching word
def unscramble(x)
DIC[hash(x.downcase)]
end
solve input
puts unscramble(scramble) || “no matching word”
Might be faster to use Symbol or Bignum instead of String for the hash,
but you need to try it. The function hash might be faster if you loop
over each letter only once.
Dansei Yuuki wrote in post #1173787:
dic = "dict.txt"
perms = gets.chomp.chars.to_a.permutation.map(&:join)
word = File.read(dic).lines.find{|line|perms.include?(line.chomp)}
if word
puts "matching word found: #{word}"
else
puts "no matching word found"
end
Using a Hash in place of a Array :
dict = “dict.txt”
lperms = (ARGV[0]||gets.chomp).chars.to_a.permutation.map(&:join)
hperms=lperms.inject({}) {|h,a| h[a]=true ; h}
word = File.read(dic).lines.find{|line|
#lperms.include?(line.chomp)
hperms[line.chomp]
}
puts word ? “matching word found: #{word}” : “no matching”
ruby yellowest
Duration 1077.062 ms, with Hash
Duration more than 10 minutes, with Array.include?
In theory, sort should be at most O(n log(n)); and creating a fixed-size
char array and iterating over each character and increasing the
corresponding count should be O(n).
But I don’t think it matters much in practice, and if speed were
super-important, it should probably be written in C etc., so I like the
sort approach and I think it allows for cleaner code.
Wow, you have all helped me greatly. Thank you Dansei Yuuki for making
it clear to me what I have done wrong.
Dansei Yuuki wrote in post #1173817:
Using a Hash is faster, but the main problem remains: For a word of size
n, there are n! permutations. Try it with the infamous German word
“Donaudampfschifffahrtskapitaen”, it takes a long time.
nice.
I have used another solution: comparaison with word.split(//).sort
dic = “dict.txt”
ref=(ARGV[0]||gets.chomp).split(//).sort.join("")
word = File.read(dic).lines.find{|line|
w=line.chomp
w.size==ref.size && w.split(//).sort.join("")==ref
}
puts word ? “matching word found: #{word}” : “no matching”
chrono ruby a.rb disestablishmentarianismanti
Chrono of : > ruby a.rb disestablishmentarianismanti
matching word found: antidisestablishmentarianism
true
Duration 416.024 ms
chrono ruby a.rb oologicallyz
Chrono of : > ruby a.rb oologicallyz
matching word found: zoologically
true
Duration 441.025 ms