Fastest way to solve chain-calculations

后端 未结 5 828
长发绾君心
长发绾君心 2021-02-04 07:14

I have a input like

string input = \"14 + 2 * 32 / 60 + 43 - 7 + 3 - 1 + 0 * 7 + 87 - 32 / 34\"; 
// up to 10MB string size

int result = Calc(input); // 11
         


        
5条回答
  •  庸人自扰
    2021-02-04 07:33

    The following solution is a finite automaton. Calc(input) = O(n). For better performance, this solution does not use IndexOf, Substring, Parse, string concatenation, or repeated reading of value (fetching input[i] more than once)... just a character processor.

        static int Calculate1(string input)
        {
            int acc = 0;
            char last = ' ', operation = '+';
    
            for (int i = 0; i < input.Length; i++)
            {
                var current = input[i];
                switch (current)
                {
                    case ' ':
                        if (last != ' ')
                        {
                            switch (operation)
                            {
                                case '+': acc += last - '0'; break;
                                case '-': acc -= last - '0'; break;
                                case '*': acc *= last - '0'; break;
                                case '/': acc /= last - '0'; break;
                            }
    
                            last = ' ';
                        }
    
                        break;
    
                    case '0': case '1': case '2': case '3': case '4':
                    case '5': case '6': case '7': case '8': case '9':
                        if (last == ' ') last = current;
                        else
                        {
                            var num = (last - '0') * 10 + (current - '0');
                            switch (operation)
                            {
                                case '+': acc += num; break;
                                case '-': acc -= num; break;
                                case '*': acc *= num; break;
                                case '/': acc /= num; break;
                            }
                            last = ' ';
                        }
                        break;
    
                    case '+': case '-': case '*': case '/':
                        operation = current;
                        break;
                }
            }
    
            if (last != ' ')
                switch (operation)
                {
                    case '+': acc += last - '0'; break;
                    case '-': acc -= last - '0'; break;
                    case '*': acc *= last - '0'; break;
                    case '/': acc /= last - '0'; break;
                }
    
            return acc;
        }
    

    And another implementation. It reads 25% less from the input. I expect that it has 25% better performance.

        static int Calculate2(string input)
        {
            int acc = 0, i = 0;
            char last = ' ', operation = '+';
    
            while (i < input.Length)
            {
                var current = input[i];
                switch (current)
                {
                    case ' ':
                        if (last != ' ')
                        {
                            switch (operation)
                            {
                                case '+': acc += last - '0'; break;
                                case '-': acc -= last - '0'; break;
                                case '*': acc *= last - '0'; break;
                                case '/': acc /= last - '0'; break;
                            }
    
                            last = ' ';
                            i++;
                        }
    
                        break;
    
                    case '0': case '1': case '2': case '3': case '4':
                    case '5': case '6': case '7': case '8': case '9':
                        if (last == ' ')
                        {
                            last = current;
                            i++;
                        }
                        else
                        {
                            var num = (last - '0') * 10 + (current - '0');
                            switch (operation)
                            {
                                case '+': acc += num; break;
                                case '-': acc -= num; break;
                                case '*': acc *= num; break;
                                case '/': acc /= num; break;
                            }
    
                            last = ' ';
                            i += 2;
                        }
                        break;
    
                    case '+': case '-': case '*': case '/':
                        operation = current;
                        i += 2;
                        break;
                }
            }
    
            if (last != ' ')
                switch (operation)
                {
                    case '+': acc += last - '0'; break;
                    case '-': acc -= last - '0'; break;
                    case '*': acc *= last - '0'; break;
                    case '/': acc /= last - '0'; break;
                }
    
            return acc;
        }
    

    I implemented one more variant:

        static int Calculate3(string input)
        {
            int acc = 0, i = 0;
            var operation = '+';
    
            while (true)
            {
                var a = input[i++] - '0';
                if (i == input.Length)
                {
                    switch (operation)
                    {
                        case '+': acc += a; break;
                        case '-': acc -= a; break;
                        case '*': acc *= a; break;
                        case '/': acc /= a; break;
                    }
    
                    break;
                }
    
                var b = input[i];
                if (b == ' ') i++;
                else
                {
                    a = a * 10 + (b - '0');
                    i += 2;
                }
    
                switch (operation)
                {
                    case '+': acc += a; break;
                    case '-': acc -= a; break;
                    case '*': acc *= a; break;
                    case '/': acc /= a; break;
                }
    
                if (i >= input.Length) break;
                operation = input[i];
                i += 2;
            }
    
            return acc;
        }
    

    Results in abstract points:

    • Calculate1 230
    • Calculate2 192
    • Calculate3 111

提交回复
热议问题