Paralysis Analysis - Part 2
The solution to the problem is this:
Word #1: PARALYSIS
Word #2: ANALYSIS
Best Split: PARALY * SIS (587893 * 141 = 82892913 with delta 228 (0.0000387826%) compared to ANALYSIS (82893141)))
First, it is impossible to find a way to split PARALYSIS in such a way that the multiplication of its two parts would arrive exactly at ANALYSIS. But it is possible to get very close, with a discrepancy of only 0.0000387826%.
The idea of how to solve this problem lies on few insights:
1) Brute-force is doable. Remember that we cannot have repeated digits, and we only have digits from 1-9 in the mix. Because of that we'll have a lower bound of 9!. In reality though, the loop might be stretched to 9^9, which is still doable (387,420,489).
2) The split of the words into two incurs into a simple binary and linear separation of the word PARALYSIS, which will only affect the constant of the algorithm by a factor of Len(PARALYSIS)-1 (or a factor of 8x), which even when multiplied by the worst-case in #a, still leads to a reasonably small number of combinations (around 3 billion). To give you an idea, in my own laptop a for-loop from 1 to 3B takes 7 seconds.
3) The other computations are very simple mathematical computations, and won't incur much into the overall execution time.
Code is below. In few seconds the answer pops-up. Now try taking into account the "zero" which wasn't allowed in the original problem.
Word #1: PARALYSIS
Word #2: ANALYSIS
Best Split: PARALY * SIS (587893 * 141 = 82892913 with delta 228 (0.0000387826%) compared to ANALYSIS (82893141)))
First, it is impossible to find a way to split PARALYSIS in such a way that the multiplication of its two parts would arrive exactly at ANALYSIS. But it is possible to get very close, with a discrepancy of only 0.0000387826%.
The idea of how to solve this problem lies on few insights:
1) Brute-force is doable. Remember that we cannot have repeated digits, and we only have digits from 1-9 in the mix. Because of that we'll have a lower bound of 9!. In reality though, the loop might be stretched to 9^9, which is still doable (387,420,489).
2) The split of the words into two incurs into a simple binary and linear separation of the word PARALYSIS, which will only affect the constant of the algorithm by a factor of Len(PARALYSIS)-1 (or a factor of 8x), which even when multiplied by the worst-case in #a, still leads to a reasonably small number of combinations (around 3 billion). To give you an idea, in my own laptop a for-loop from 1 to 3B takes 7 seconds.
3) The other computations are very simple mathematical computations, and won't incur much into the overall execution time.
Code is below. In few seconds the answer pops-up. Now try taking into account the "zero" which wasn't allowed in the original problem.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
namespace MathWord
{
class Program
{
static void Main(string[]
args)
{
if
(args.Length != 2)
{
Console.WriteLine("MathWord.exe <word1> <word2>");
return;
}
string
word1 = args[0].ToUpper();
string
word2 = args[1].ToUpper();
Hashtable
letterMap = new Hashtable();
Hashtable
usedDigit = new Hashtable();
long
minDelta = Int64.MaxValue;
string
bestPart1 = "";
string bestPart2 = "";
string
bestMapping1 = "";
string
bestMapping2 = "";
string
bestResult = "";
string
bestRightSideMapping = "";
Console.WriteLine("Processing...");
Process(word1,
word2,
0,
letterMap,
usedDigit,
"",
"",
ref
minDelta,
ref
bestPart1,
ref
bestPart2,
ref
bestMapping1,
ref
bestMapping2,
ref
bestResult,
ref
bestRightSideMapping);
Console.WriteLine("Done!");
Console.WriteLine();
if
(bestMapping1.Length == 0 ||
bestMapping2.Length == 0)
{
Console.WriteLine("No Solution!");
}
else
{
double
deltaPerc = (minDelta * 100.0) / (Int64.Parse(bestMapping1
+ bestMapping2));
Console.WriteLine("Word #1: {0}", word1);
Console.WriteLine("Word #2: {0}", word2);
Console.WriteLine("Best Split: {0} * {1} ({2} * {3} = {4} with delta
{5} ({6}%) compared to {7} ({8})))", bestPart1,
bestPart2,
bestMapping1,
bestMapping2,
bestResult,
minDelta,
deltaPerc.ToString("F10"),
word2,
bestRightSideMapping);
}
}
static void Process(string
word1,
string word2,
int
currentPosition,
Hashtable letterMap,
Hashtable usedDigit,
string wordNumber1,
string wordNumber2,
ref long minDelta,
ref string bestPart1,
ref string bestPart2,
ref string
bestMapping1,
ref string
bestMapping2,
ref string
bestResult,
ref string
bestRightSideMapping)
{
//Base
case
if
(currentPosition >= word1.Length + word2.Length)
{
CheckWordNumber(word1,
wordNumber1,
word2,
wordNumber2,
ref minDelta,
ref bestPart1,
ref bestPart2,
ref bestMapping1,
ref bestMapping2,
ref bestResult,
ref bestRightSideMapping);
return;
}
//Induction
int
relativeCurrentPosition = 0;
string
currentWord = "";
bool
firstWord = false;
if
(currentPosition < word1.Length)
{
relativeCurrentPosition = currentPosition;
currentWord = word1;
firstWord = true;
}
else
{
relativeCurrentPosition =
currentPosition - word1.Length;
currentWord = word2;
firstWord = false;
}
if
(letterMap.ContainsKey(currentWord[relativeCurrentPosition]))
{
Process(word1,
word2,
currentPosition + 1,
letterMap,
usedDigit,
firstWord ? wordNumber1
+ (string)letterMap[currentWord[relativeCurrentPosition]]
: wordNumber1,
firstWord ? wordNumber2
: wordNumber2 + (string)letterMap[currentWord[relativeCurrentPosition]],
ref minDelta,
ref bestPart1,
ref bestPart2,
ref bestMapping1,
ref bestMapping2,
ref
bestResult,
ref bestRightSideMapping);
}
else
{
//for
(int i = (currentPosition == 0) ? 1 : 0; i < 10; i++)
for
(int i = 1; i < 10; i++)
{
if
(!usedDigit.ContainsKey(i))
{
letterMap.Add(currentWord[relativeCurrentPosition], i.ToString());
usedDigit.Add(i, true);
Process(word1,
word2,
currentPosition
+ 1,
letterMap,
usedDigit,
firstWord ?
wordNumber1 + i.ToString() : wordNumber1,
firstWord ?
wordNumber2 : wordNumber2 + i.ToString(),
ref minDelta,
ref bestPart1,
ref
bestPart2,
ref bestMapping1,
ref bestMapping2,
ref bestResult,
ref bestRightSideMapping);
usedDigit.Remove(i);
letterMap.Remove(currentWord[relativeCurrentPosition]);
}
}
}
}
static void CheckWordNumber(string
word1,
string wordNumber1,
string word2,
string wordNumber2,
ref long minDelta,
ref string bestPart1,
ref string bestPart2,
ref string
bestMapping1,
ref string
bestMapping2,
ref string
bestResult,
ref
string bestRightSideMapping)
{
long
rightSide = Int64.Parse(wordNumber2);
for
(int i = 1; i < wordNumber1.Length; i++)
{
long
part1 = Int64.Parse(wordNumber1.Substring(0,
i));
long
part2 = Int64.Parse(wordNumber1.Substring(i));
long
delta = (long)Math.Abs((part1
* part2) - rightSide);
if
(delta < minDelta)
{
bestPart1 =
word1.Substring(0, i);
bestPart2 =
word1.Substring(i);
bestMapping1 =
wordNumber1.Substring(0, i);
bestMapping2 =
wordNumber1.Substring(i);
bestResult = Convert.ToString(part1 * part2);
bestRightSideMapping =
wordNumber2;
minDelta = delta;
}
}
}
}
}
Comments
Post a Comment