2009-01-11 52 views
13

我正在为欧拉问题#4写一个快速解决方案,其中必须从两个3位数字的乘积中找到最大的回文数。在C#中反向字符串的最快方法.net

要确定一个数字是否是回文,您显然会将数字的反转与原始数据进行比较。

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

我将在一个循环中测试所有建议的解决方案,包含100,000,000次迭代。提交最快解决方案的人将得到正确答案。

笔者将要测试在C#.NET 3.5的控制台应用程序解决方案

+0

您可能已经超出了解决方案的范围,尤其是因为您的字符串太小。为什么不要求一种比较两个整数的方法,如果它们是回文的话就返回true? – jdigital 2009-01-11 17:15:21

回答

13

我想这可能是快做就地进行比较。如果颠倒的字符串,你得:

  1. 实例化一个新的字符串对象(或StringBuffer对象)
  2. 复制从第一个字符串的数据(反向),以新的字符串
  3. 待办事项你的比较。

如果您在原地进行比较,那么您只做最后一步。即使如此,你的比较只是字符串的一半(或者一半 - 在奇数字符的情况下为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%的速度,通过我的测试。配置器的解决方案比ggf31416快一点,如果将其转换为静态方法并使用int s而不是ulong s(但要慢得多,否则)。


顺便说一下,通过这些实例运行解决与下面的简单(也许天真)循环的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正确的结果。

+0

您的建议花了49秒435ms – GateKiller 2009-01-11 18:56:16

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); 
} 

编辑:卫生署!忘记减半长度PERF :)

+0

你的建议花了1分49秒919ms – GateKiller 2009-01-11 18:49:28

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

你的建议花了1分51秒608ms – GateKiller 2009-01-11 18:53:43

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

虽然不是最高性能,但它是最小和最容易写的。 – 2009-01-11 19:06:33

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 ; 
    } 

使得该方法返回一个布尔比在我的电脑上一个循环比较的布尔稍微慢一些,但我不明白为什么。请在您的电脑上测试。

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

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,直到1073741829其中,1/10给出107374182和乘法+二进制移位给出107374183.

+0

我对Math.DivRem不了解。很好的接触。 – configurator 2009-01-11 17:31:25

+0

你的第一个建议花了36秒816ms,你的第二个建议花了34秒74ms – GateKiller 2009-01-11 19:01:58

+1

OMG!你的第三个建议花了13秒916ms! – GateKiller 2009-01-11 19:28:12

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; 
} 
0

使用ggf31416的快退功能,这里是项目欧拉的问题#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; 
     } 
    } 
} 
-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; 
     } 

    } 
} 
1

我发现扭转C#字符串是用下面的代码的最快方式。它的读取速度是32位,而不是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--; 
     } 
    } 
}