我正在编写一个快速解决方案,用于解决欧拉问题#4,其中必须从两个三位数的乘积中找到最大的回文数字。
要确定一个数字是否是回文的,显然需要将该数字的反转与原始数字进行比较。
由于C#没有内置的String.Reverse()方法,那么最快的字符串反转方法是什么?
我将在一个循环中测试所有建议的解决方案,进行10,000,000次迭代。给出最快解决方案的人将获得正确答案。
我将在一个C#.Net 3.5控制台应用程序中测试这个解决方案。
我正在编写一个快速解决方案,用于解决欧拉问题#4,其中必须从两个三位数的乘积中找到最大的回文数字。
要确定一个数字是否是回文的,显然需要将该数字的反转与原始数字进行比较。
由于C#没有内置的String.Reverse()方法,那么最快的字符串反转方法是什么?
我将在一个循环中测试所有建议的解决方案,进行10,000,000次迭代。给出最快解决方案的人将获得正确答案。
我将在一个C#.Net 3.5控制台应用程序中测试这个解决方案。
// 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;
}
digit
始终是number
的个位数。 - configurator如果您想将一个数字与它的反向数进行比较,使用除法翻转数字可能比将其转换为字符串更快。 我仍需要测试其速度。
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 ;
}
将方法返回布尔值比在循环中与布尔值进行比较稍微慢了一些,但我不明白为什么。请在您的计算机上测试。
乘法和位移操作应该比除法更快,但可能不够精确。编辑:使用 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。
我认为在原地比较可能会更快。如果你反转字符串,你需要:
如果您在原地执行比较,则只需执行最后一步。即使这样,您的比较也仅限于字符串的一半(如果字符数为奇数,则是半数减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;
}
编辑:
虽然这回答了问题,但根据我的测试,ggf31416和configurator提供的解决方案可以比本题作者提供的解决方案更快地解决原问题,快约30%。如果将其转换为静态方法并使用int
而不是ulong
,则configurator的解决方案略快于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()) 耗时0.3064174秒。 ggf31416Reverse(product) == product 耗时0.1933994秒。 configuratorReverse(product) == product 耗时0.1872061秒。
每个方法都得到了正确的结果913 * 993 = 906609
。
string test = "ABC";
string reversed = new String(test.ToCharArray().Reverse().ToArray());
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);
}
编辑:哎呀!忘记了为了性能要把长度减半 :)
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--;
}
}
}
string Reverse(string s)
{
return new string(s.ToCharArray().Reverse().ToArray());
}
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;
}
}
}