【发布时间】:2016-03-07 06:18:45
【问题描述】:
我正在解决this problem,其中他们要求第一个 1000 位数的斐波那契数的索引,我的第一个想法类似于:
BigInteger x = 1;
BigInteger y = 1;
BigInteger tmp = 0;
int currentIndex = 2;
while (x.NoOfDigits < 1000)
{
tmp = x + y;
y = x;
x = tmp;
currentIndex++;
}
return currentIndex;
但是,据我所知,没有办法计算 BigInteger 的位数。这是真的?绕过它的一种方法是使用 BigInteger 的 .ToString().Length 方法,但有人告诉我字符串处理很慢。
BigInteger 也有一个 .ToByteArray(),我想将 BigInteger 转换为字节数组并检查该数组的长度 - 但我不认为这唯一决定了 BigInteger 中的位数。
对于它的价值,我实现了另一种解决方法,即手动将斐波那契数存储在数组中,一旦数组满了就停止,我将其与基于 .ToString 的方法进行了比较,后者大约慢了 2.5 倍,但第一种方法需要 0.1 秒,这似乎也很长。
编辑:我已经测试了以下答案中的两个建议(一个使用 BigInteger.Log,一个使用 MaxLimitMethod)。我得到以下运行时间:
- 原方法:00:00:00.0961957
- StringMethod: 00:00:00.1535350
- BigIntegerLogMethod: 00:00:00.0387479
- MaxLimitMethod: 00:00:00.0019509
程序
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
Stopwatch clock = new Stopwatch();
clock.Start();
int index1 = Algorithms.IndexOfNDigits(1000);
clock.Stop();
var elapsedTime1 = clock.Elapsed;
Console.WriteLine(index1);
Console.WriteLine("Original method: {0}",elapsedTime1);
Console.ReadKey();
clock.Reset();
clock.Start();
int index2 = Algorithms.StringMethod(1000);
clock.Stop();
var elapsedTime2 = clock.Elapsed;
Console.WriteLine(index2);
Console.WriteLine("StringMethod: {0}", elapsedTime2);
Console.ReadKey();
clock.Reset();
clock.Start();
int index3 = Algorithms.BigIntegerLogMethod(1000);
clock.Stop();
var elapsedTime3 = clock.Elapsed;
Console.WriteLine(index3);
Console.WriteLine("BigIntegerLogMethod: {0}", elapsedTime3);
Console.ReadKey();
clock.Reset();
clock.Start();
int index4 = Algorithms.MaxLimitMethod(1000);
clock.Stop();
var elapsedTime4 = clock.Elapsed;
Console.WriteLine(index4);
Console.WriteLine("MaxLimitMethod: {0}", elapsedTime4);
Console.ReadKey();
}
}
static class Algorithms
{
//Find the index of the first Fibonacci number of n digits
public static int IndexOfNDigits(int n)
{
if (n == 1) return 1;
int[] firstNumber = new int[n];
int[] secondNumber = new int[n];
firstNumber[0] = 1;
secondNumber[0] = 1;
int currentIndex = 2;
while (firstNumber[n-1] == 0)
{
int carry = 0, singleSum = 0;
int[] tmp = new int[n]; //Placeholder for the sum
for (int i = 0; i<n; i++)
{
singleSum = firstNumber[i] + secondNumber[i];
if (singleSum >= 10) carry = 1;
else carry = 0;
tmp[i] += singleSum % 10;
if (tmp[i] >= 10)
{
tmp[i] = 0;
carry = 1;
}
int countCarries = 0;
while (carry == 1)
{
countCarries++;
if (tmp[i + countCarries] == 9)
{
tmp[i + countCarries] = 0;
tmp[i + countCarries + 1] += 1;
carry = 1;
}
else
{
tmp[i + countCarries] += 1;
carry = 0;
}
}
}
for (int i = 0; i < n; i++ )
{
secondNumber[i] = firstNumber[i];
firstNumber[i] = tmp[i];
}
currentIndex++;
}
return currentIndex;
}
public static int StringMethod(int n)
{
BigInteger x = 1;
BigInteger y = 1;
BigInteger tmp = 0;
int currentIndex = 2;
while (x.ToString().Length < n)
{
tmp = x + y;
y = x;
x = tmp;
currentIndex++;
}
return currentIndex;
}
public static int BigIntegerLogMethod(int n)
{
BigInteger x = 1;
BigInteger y = 1;
BigInteger tmp = 0;
int currentIndex = 2;
while (Math.Floor(BigInteger.Log10(x) + 1) < n)
{
tmp = x + y;
y = x;
x = tmp;
currentIndex++;
}
return currentIndex;
}
public static int MaxLimitMethod(int n)
{
BigInteger maxLimit = BigInteger.Pow(10, n - 1);
BigInteger x = 1;
BigInteger y = 1;
BigInteger tmp = 0;
int currentIndex = 2;
while (x.CompareTo(maxLimit) < 0)
{
tmp = x + y;
y = x;
x = tmp;
currentIndex++;
}
return currentIndex;
}
}
【问题讨论】:
-
你能通过计算
log_10(number)得到以 10 为底的大小并计算结果吗?编辑:我的意思是这样的:stackoverflow.com/a/1489928/5296568。但是,如果您真的想计算大数的log_10,请小心。字符串处理可能更快。做基准来确认这一点。 -
@MaximilianGerhardt 谢谢。我能找到的唯一 Log10 是 Math.Log10,它以 double 作为输入,当我将 BigInteger 转换为 double 时,我似乎遇到了数值问题。也许我可以实现我自己的 Log10。
-
不,这里有一个静态函数
BigInteger.Log(BigInteger num, double base):(msdn.microsoft.com/de-de/library/dd268364%28v=vs.110%29.aspx)。您还应该记下上述问题中发布的其他答案,但在 all 事情之上,您应该做的是 benchmark 一切。结果可能令人惊讶。也许,.ToString().Length是最快的方法。 -
@HowDoICSharply 计算位数的简单方法是不断除以 10 直到小于 10。除以的次数 + 1 就是位数。跨度>
-
@Kyle——这听起来效率不高。在 1000 位数字上,您需要 1000 个除法才能找到长度。解决该问题的另一种方法,在我看来最快(尽管需要进行基准测试),将不是检查位数,而是为 10^n 设置一个常数,其中 n 是您的位数(n = 1000),并执行运算直到总和大于该常数。
标签: c# arrays biginteger