I have been wondering for some time if it might be possible to create an algorithm to solve a particular math puzzle.
Here is the puzzle:
SEND
+MORE
----
MONEY
The idea is to find the numeric values which add up so that the values given to any letter are the same in all the words. That is, if you assign a value of 1 to the E in SEND, then all the other E's must also be 1. Additionally, no two different letters can have the same number. Since there is only one S, if you assign it a value of 2, then no other letters in the entire equation can be 2. The result will of course have to be mathematically correct.
I'm not sure if any of these such puzzles can have more than one correct answer, but I seem to recall finding two for this one.
How might we go about creating an algorithm that can narrow down the possibilities efficiently? There must be a way, though I admit I've been too busy with other things to give that any thought.
RoboTact
September 24th, 2005, 08:29 AM
It's linear Diophantine equation (in fact system of equations and inequalities).
WizBang
September 24th, 2005, 09:07 AM
Well, thanks RoboTact, but that's a fancy term which has little or no meaning to me, even after looking it up.
I do wish those math guys would speak English, or at least in laymen's terms. Would you care to expound a bit on how that Diophantine thingy relates to the problem?
cilu
September 24th, 2005, 09:08 AM
This implementation should be ok. I have tested it for examples with less possible combination. For
SEND
+MORE
----
MONEY
there are 8^10 = 2 ^ 30 examples. I'll let you compute them. ;)
My code assumes that each letter can take a value of 0 - 9, and two letters can have same value.
void LetterMap::MoveToNextConfiguration()
{
int transport = 1;
for(std::map<char, int>::reverse_iterator it = LetterToNumber.rbegin(); it != LetterToNumber.rend(); ++it)
{
char key = (*it).first;
int value = (*it).second;
value = value + transport;
if(value < 10)
{
LetterToNumber[key] = value;
break;
}
else
{
LetterToNumber[key] = 0;
transport = 1;
}
}
}
int LetterMap::GetNumericValue(char value) const
{
std::map<char, int>::const_iterator pos = LetterToNumber.find(value);
if(pos == LetterToNumber.end())
return 0;
return (*pos).second;
}
std::ostream& operator << (std::ostream& os, const LetterMap& lmap)
{
for(std::map<char, int>::const_iterator it = lmap.LetterToNumber.begin();
it != lmap.LetterToNumber.end(); ++it)
{
os << (*it).first << " : " << (*it).second << ", ";
}
os << '\n';
return os;
}
No two letters can have the same value. I just found that this problem is called an "arithmetical rebus". Like I said, never researched it, but thought I'd throw it out to everyone and see what happens. Here is a link to some actual code for the thing! http://pcbunn.cacr.caltech.edu/jjb/oddments/rebus.htm
RoboTact
September 24th, 2005, 09:35 AM
No two letters can have the same value. I just found that this problem is called an "arithmetical rebus". Like I said, never researched it, but thought I'd throw it out to everyone and see what happens. Here is a link to some actual code for the thing! http://pcbunn.cacr.caltech.edu/jjb/oddments/rebus.htmYou asked about effective algorithm, which this one is not. Though it serves relatively well, as 10! woudl take about 1-10 seconds.
cilu
September 24th, 2005, 09:46 AM
No two letters can have the same value. I just found that this problem is called an "arithmetical rebus". Like I said, never researched it, but thought I'd throw it out to everyone and see what happens. Here is a link to some actual code for the thing! http://pcbunn.cacr.caltech.edu/jjb/oddments/rebus.htm
Well, you did not specify that in the first place. Well, only LetterMap::MoveToNextConfiguration() and LetterMap::HasNewConfiguration() need a change. The rest is ok. ;)
WizBang
September 24th, 2005, 09:52 AM
You asked about effective algorithm, which this one is not.
Right. I still wanted to share what I turned up.
I was thinking that it should be possible to avoid selecting values for all the letters and adding up everything. I say this because when choosing a number, you can readily see when the result makes two letters have the same value, though not always so immediately. Still, I'd like to believe it's not a matter of trial and error. At least not entirely.
WizBang
September 24th, 2005, 09:54 AM
Well, you did not specify that in the first place.
Oh, sorry. I guess you saw my post before I got back to edit it.
RoboTact
September 24th, 2005, 10:18 AM
I was thinking that it should be possible to avoid selecting values for all the letters and adding up everything. I say this because when choosing a number, you can readily see when the result makes two letters have the same value, though not always so immediately. Still, I'd like to believe it's not a matter of trial and error. At least not entirely.It's possible. What you describing here is just heuristics to cut obviously wrong cases. If you want for example write programm to generate puzzles which can be solved manually without implications like "in this letter is 1 and that letter is 3, let's see if nothing goes wrong" with hundreds or so cases, heuristics may be right way to go. If your algorithms cover enough tricks, you can assume that puzzle is too difficult if such algorithm can't find solution using tricks.
However, tricks can't be effective for all puzzles. You need to apply Diophantine equation solving algorithms to do so. I mean regard puzzle letters as integer variables and apply multiplying by 10, 100, etc. forming numbers. For example, for puzzle AA+B=BDD, equation is
10*A+A+B=100*B+10*D+D or
11*A-99*B-11*D=0
which is Diophantine equation to solve.
WizBang
September 24th, 2005, 10:27 AM
Thanks RoboTact, I did see a reference to something like that moments ago.
Here is a whole site full of such puzzles:
http://www.geocities.com/Athens/Agora/2160/puzzlemenu.html
cilu
September 24th, 2005, 11:39 AM
OK, this is the revised algorithm, verified with several examples from that site:
void LetterMap::MoveToNextConfiguration()
{
do
{
int transport = 1;
for(std::map<char, int>::reverse_iterator it = LetterToNumber.rbegin(); it != LetterToNumber.rend(); ++it)
{
char key = (*it).first;
int value = (*it).second;
value = value + transport;
if(value < 10)
{
LetterToNumber[key] = value;
break;
}
else
{
LetterToNumber[key] = 0;
transport = 1;
}
}
}
while(HasNewConfiguration() && !AreDifferent());
}
int LetterMap::GetNumericValue(char value) const
{
std::map<char, int>::const_iterator pos = LetterToNumber.find(value);
if(pos == LetterToNumber.end())
return 0;
return (*pos).second;
}
std::ostream& operator << (std::ostream& os, const LetterMap& lmap)
{
for(std::map<char, int>::const_iterator it = lmap.LetterToNumber.begin();
it != lmap.LetterToNumber.end(); ++it)
{
os << (*it).first << " : " << (*it).second << ", ";
}
os << '\n';
return os;
}
I know that fore some examples is slow, but I don't have time to optimize it.
WizBang
September 24th, 2005, 04:35 PM
Thanks cilu. I didn't expect answers so quickly, much less fully working code. I will look at this in detail for sure.
One thing that occured to me is that it might help to think backwards. That is, if we can devise an algo which can create a new puzzle from a list of words, it could provide further insight. This actually sounds like a neat idea for a puzzle game, which can create new puzzles on-the-fly. No need to spend time writing code, but if anyone has some ideas on how it could be handled, that would be useful.
Thanks everyone. I've always had a thing for brain-teasers.
RoboTact
September 25th, 2005, 04:07 AM
That' where you might find useful heuristics technics. I once created such generator, for puzzles where you are given operation (sum or multiplication) of 2-4 digit numbers, but some digits are commented away (replaced by stars). I first wrote algorithm to check whether there is single solution and starting with concrete operation without commented digits gradually commented random digits, checking each time if solution is still single. The problem was that resulted puzzles were too difficult to solve: no tricks allowed to solve it, you needed to enumerate many guesses. At last I used some heuristic rules on where it's allowed to comment digits and resulted puzzles were more or less solvable.
With your puzzle you can pick pairs of given words, enumerate character replacements and get resulted word allowed by mask of resulted number, then check if solution is single and heuristically obtainable. Though I think it would require many words to sutisfy both criteria.
mehdi62b
September 25th, 2005, 08:42 AM
For example, for puzzle AA+B=BDD, equation is
10*A+A+B=100*B+10*D+D or
11*A-99*B-11*D=0
which is Diophantine equation to solve.another example!AB+CB=ABC10A+10C+2B=100A+10B+c --> 90A+8B-9C=0 -->
90A-9C=-8B --> 9 should count -8B --> B is 9 or 0
if B=0, A and C would be 0(unacceptable)
if B=9, C=8 and A=0(its only answer)
cilu
September 25th, 2005, 12:32 PM
another example!AB+CB=ABC10A+10C+2B=100A+10B+c --> 90A+8B-9C=0 -->
90A-9C=-8B --> 9 should count -8B --> B is 9 or 0
if B=0, A and C would be 0(unacceptable)
if B=9, C=8 and A=0(its only answer)
How did you produce these equations from the given equation?
mehdi62b
September 25th, 2005, 02:18 PM
my example was just one of those a few specific cases
for example if our equation turns out
77A+91B+7C=93D --> (77,91,7)=7 --> 7(11A+13B+C)=93D --> so 93D=7k(k is an inetegr number) so 7 should count D (7 doesn't count 93) so D is 7
for RoboTact example
11*A-99*B-11*D=0 --> A-9B-D=0 --> this trick doesn't work.
cilu
September 26th, 2005, 09:14 AM
I was asking, how starting with this equation:
AB+CB=ABC
you ended up with these two:
10A+10C+2B=100A+10B+c
90A-9C=-8B
RoboTact
September 26th, 2005, 10:23 AM
A, B and C are digits, equation AB+CB=ABC is symbolic: if, say, A=2, B=3, C=4, it would read 23+43=234 (incorrect). Floowing equations are just translation of symbolic equation to formal one (with digits-variables).
mehdi62b
September 26th, 2005, 12:06 PM
A,B,C are just variable between 0 and 9AA+B=BDD --> 11*A-99*B-11*D=0 --> A-9B-D=0 --> this trick doesn't work.
A-9B-D=0 --> A-D=9B --> because A and D are just between 0 and 9 so B just can be either 0 or 1
if B=0, A=D!=0 (9 answers)
if B=1, A-D=9 , because 0<=A,D<=9 so just A=9 and D=0 (1 answer)
totally 10 answers.
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.