在C#.net中反转字符串的最快方法

时间:2009-01-11 17:02:12

标签: c#

我目前正在为Euler Problem#4编写一个快速解决方案,其中必须找到两个3位数字产品中最大的回文数。

要确定某个数字是否是回文,您显然会将该数字与原始数字相反。

由于C#没有内置的String.Reverse()方法,反转字符串的最快方法是什么?

我将在循环中测试所有建议的解决方案,迭代次数为100,000,000次。正确答案将提交给提交最快解决方案的人。

我将在C#.Net 3.5控制台应用程序中测试该解决方案

11 个答案:

答案 0 :(得分:21)

不会更快地扭转数字吗?

// unchecked code, don't kill me if it doesn't even compile.
ulong Reverse(ulong number) {
    ulong result = 0;

    while (number > 0) {
        ulong digit = number % 10;
        result = result * 10 + digit;
        number /= 10;
    }

    return result;
}

答案 1 :(得分:15)

你想比较一个数字与它的反向,使用除法反转数字可能会更快,而不是将其转换为字符串。我仍然需要测试它的速度。

 private static int Reverse(int num) {
     int res = 0;
     while (num > 0) {
        int rm ;
        num = Math.DivRem(num, 10, out rm);
        res = res * 10 + rm;
     }
     return res;
  }

编辑: DivRem比计算机中的分区和模块快1%左右。 如果最后一位为0,则退出速度优化:

  private static int Reverse(int num) {
     int res = 0;
     int rm;
     num = Math.DivRem(num, 10, out rm);
     //Some magic value or return false, see below.
     if (rm == 0) return -1 ; 
     res = res * 10 + rm;
     while (num > 0) {
        num = Math.DivRem(num, 10, out rm);
        res = res * 10 + rm;
     }
     return res ;
  }

使方法返回bool比在计算机中循环中的bool稍微慢一些,但我不明白为什么。请在您的计算机上测试。

乘法和位移应该比除法快,但可能不够精确。编辑:使用long似乎足够精确。

  private static int FastReverse(int num) {
     int res = 0;
     int q = (int)((214748365L * num) >> 31);
     int rm = num - 10 * q;
     num = q;
     if (rm == 0) return -1;
     res = res * 10 + rm;
     while (num > 0) {
        q = (int)((214748365L * num) >> 31);
        rm = num - 10 * q;
        num = q;
        res = res * 10 + rm;
     }
     return res;
  }

(214748365L * num)>> 31等于i / 10,直到1,073,741,829,其中1/10给出107374182,乘法+二进制移位给出107374183。

答案 2 :(得分:13)

我认为在原地进行比较可能会更快。如果你反转字符串,你必须:

  1. 实例化新的字符串对象(或StringBuffer对象)
  2. 将数据(反向)从第一个字符串复制到新字符串
  3. 进行比较。
  4. 如果您执行比较,则只执行最后一步。即便如此,你的比较只是字符串的一半(或半数 - 0.5,如果是奇数个字符)。以下内容应该有效:

    static bool IsPalindromic(string s){
        int len = s.Length;
        int half = len-- >> 1;
        for(int i = 0; i < half; i++)
            if(s[i] != s[len - i])
                return false;
        return true;
    }
    

    修改

    尽管这回答了OP的问题,但ggf31416configurator提供的解决方案通过我的测试解决了OP的实际需求快了约30%。如果你将它转换为静态方法并使用int而不是ulong s(但速度要慢得多),配置器的解决方案比ggf31416快一点。


    顺便提一下,通过这些例子来解决问题,OP提到(找到任意两个三位数的最大回文产品),下面是简单的(可能是天真的)循环:

    for(int i = 100; i < 1000; i++)
        for(int j = i; j < 1000; j++) // calculations where j < i would be redundant
            ...
    

    在我的机器上产生以下结果:

    IsPalindromic(product.ToString()) took 0.3064174 seconds.
    ggf31416Reverse(product) == product took 0.1933994 seconds.
    configuratorReverse(product) == product took 0.1872061 seconds.
    

    每个都会生成913 * 993 = 906609的正确结果。

答案 3 :(得分:4)

答案 4 :(得分:3)

string test = "ABC";
string reversed = new String(test.ToCharArray().Reverse().ToArray());

答案 5 :(得分:1)

public static String Reverse(string input) {
  var length = input.Length;
  var buffer = new char[length];
  for ( var i= 0; i < input.Length; i++ ) {
    buffer[i] = input[(length-i)-1];
  }
  return new String(buffer);
}
编辑:Doh!忘了将perf的长度减半:)

答案 6 :(得分:1)

我发现在C#中反转字符串的最快方法是使用以下代码。它一次读取的速度更快,而不是char的长度为16位。 在调试模式下,它会更快,直到你达到约93个字符。任何比Array.Reverse()更长的东西。使用发布版本并在IDE外部运行,此方法将以任何字符串长度将Array.Reverse()从水中吹出。

char[] MyCharArray = MyString.ToCharArray();
UIntStringReverse(ref MyCharArray);     //Code to reverse is below.
string ReversedString = new string(MyCharArray);


private static unsafe void UIntStringReverse(ref char[] arr)
{
    uint Temp;
    uint Temp2;

    fixed (char* arrPtr = &arr[0])
    {
        uint* p, q;
        p = (uint*)(arrPtr);
        q = (uint*)(arrPtr + arr.LongLength - 2);

        if (arr.LongLength == 2)
        {
            Temp = *p;
            *p = ((Temp & 0xFFFF0000) >> 16) | ((Temp & 0x0000FFFF) << 16); 
            return;
        }

        while (p < q)
        {
            Temp = *p;
            Temp2 = *q;

            *p = ((Temp2 & 0xFFFF0000) >> 16) | ((Temp2 & 0x0000FFFF) << 16); 
            *q = ((Temp & 0xFFFF0000) >> 16) | ((Temp & 0x0000FFFF) << 16);

            p++;
            q--;
        }
    }
}

答案 7 :(得分:0)

答案 8 :(得分:0)

string Reverse(string s)
{
  return new string(s.ToCharArray().Reverse().ToArray());
}

答案 9 :(得分:0)

使用ggf31416的FastReverse函数,这是Project Euler的问题#4的解决方案,它在47ms内在我的计算机上完成。

using System;
using System.Diagnostics;

namespace Euler_Problem_4
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch s = new Stopwatch();
            s.Start();

            int t = 0;

            for (int i = 999; i > 99; i--)
            {
                for (int j = i; j > 99; j--)
                {
                    if (i*j == FastReverse(i*j))
                    {
                        if (i * j > t)
                        {
                            t = i * j;
                        }
                    }
                }
            }

            Console.WriteLine(t);

            s.Stop();
            Console.WriteLine("{0}mins {1}secs {2}ms", s.Elapsed.Minutes, s.Elapsed.Seconds, s.Elapsed.Milliseconds);
            Console.ReadKey(true);

        }

        private static int FastReverse(int num)
        {
            int res = 0;
            int q = (int)((214748365L * num) >> 31);
            int rm = num - 10 * q;
            num = q;
            if (rm == 0) return -1;
            res = res * 10 + rm;
            while (num > 0)
            {
                q = (int)((214748365L * num) >> 31);
                rm = num - 10 * q;
                num = q;
                res = res * 10 + rm;
            }
            return res;
        }
    }
}

答案 10 :(得分:-1)

每次运行后,Stopwatch类都需要重置。以下代码已更正

var d = s.ToCharArray();
Array.Reverse(d);
return s == new string(d);

using System;
using System.Diagnostics;

namespace longeststring_codegolf
{
    class Program
    {
        static void Main(string[] args)
        {
            int t = 0, v = 0;
            var sw = new Stopwatch();

            sw.Start();
            for (int i = 999; i > 99; i--)
                for (int j = 999; j > 99; j--)
                    if ((v = i * j) > t && IsPalindromicMine(v.ToString()))
                        t = v;
            sw.Stop();

            var elapsed = sw.Elapsed;
            var elapsedMilliseconds = sw.ElapsedMilliseconds;
            var elapsedTicks = sw.ElapsedTicks; 
            Console.WriteLine("Ticks: " + elapsedTicks.ToString());//~189000
            Console.WriteLine("Milliseconds: " + elapsedMilliseconds.ToString()); //~9

            sw = Stopwatch.StartNew();
            for (int i = 999; i > 99; i--)
                for (int j = 999; j > 99; j--)
                    if ((v = i * j) > t && IsPalindromic(v.ToString()))
                        t = v;
            sw.Stop();
            var elapsed2 = sw.Elapsed;
            var elapsedMilliseconds2 = sw.ElapsedMilliseconds;
            var elapsedTicks2 = sw.ElapsedTicks; 
            Console.WriteLine("Ticks: " + elapsedTicks2.ToString());//~388000
            Console.WriteLine("Milliseconds: " + elapsedMilliseconds2.ToString());//~20

        }

        static bool IsPalindromicMine(string s)
        {
            var d = s.ToCharArray();
            Array.Reverse(d);
            return s == new string(d);
        }

        static bool IsPalindromic(string s)
        {
            int len = s.Length;
            int half = len-- >> 1;
            for (int i = 0; i < half; i++)
                if (s[i] != s[len - i])
                    return false;
            return true;
        }

    }
}