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.


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

Popular posts from this blog

Advent of Code - Day 6, 2024: BFS and FSM

Golang vs. C#: performance characteristics (simple case study)

Advent of Code - Day 7, 2024: Backtracking and Eval