C# 寻找Padovan数的优化方法

C# 寻找Padovan数的优化方法,c#,algorithm,optimization,C#,Algorithm,Optimization,最优化的方法是什么? 这就是我目前拥有的。 即使它返回正确的结果,我想知道什么是最快的方法 long Sum = 0; public long Get(int number) { Sum = 0; if (number == 0 || number == 1 || number == 2) return 1; return GetPa

最优化的方法是什么? 这就是我目前拥有的。 即使它返回正确的结果,我想知道什么是最快的方法

        long Sum = 0;
        public long Get(int number)
        {
            Sum = 0;      
            if (number == 0 || number == 1 || number == 2)
                return 1;
            return GetPadovanAlt(number);
        }

        public long GetPadovanAlt(int n)
        {
          if(n == 0 || n == 1 || n == 2)
          return 1;
          return GetPadovanAlt(n - 2) + GetPadovanAlt(n - 3);

        }

只能通过调用一个方法来完成

public long GetPadovanAlt(int n)
    {
      if(n == 0 || n == 1 || n == 2)
      return 1;
      return GetPadovanAlt(n - 2) + GetPadovanAlt(n - 3);

    }

通过使用递归,您所做的工作超出了您的需要。当您计算
GetPadovan(42)
时,您将触发一个递归二叉树,其中将包括子调用,如
GetPadovan(12)
。在递归树的许多分支上,
GetPadovan(12)
将被多次调用。输入类似于
if(n==12)的内容,然后打印(“*”)以查看有多少

当您计算特定的Padovan号码时,存储该号码并检索存储的号码,以便进行第二次和后续呼叫。或者切换到非递归计算:从Padovan(3)开始计算,并在继续时跟踪四个数字:P(n)、P(n-1)、P(n-2)和P(n-3)


ETA:运行一个快速程序,
GetPadovan(42)
调用
GetPadovan(12)
1897次。递归肯定不是最快的方法。

Ol'For循环可能没有递归那么花哨。
但如果它真的起作用,那么就没有理由小看它

public静态biginger GetPadovan(int n)
{
如果(n>156 | | n<-316)返回GetBigPadovan(n);
返回GetSmallPadovan(n);
}
静态BigInteger GetBigPadovan(int n)
{
如果(n==0)返回1;
如果(n==1 | | n==2)返回1;
BigInteger padovan=0,prev1=1,prev2=1,prev3=1;
如果(n>2)
{
对于(变量i=2;in;i--)
{
padovan=prev3-prev1;
prev3=prev2;
prev2=prev1;
prev1=padovan;
}
}
返回帕多瓦;
}
静态长GetSmallPadovan(int n)
{
如果(n==0)返回1;
如果(n==1 | | n==2)返回1;
如果(n>156 | | n<-316)返回0;
长帕多瓦=0,prev1=1,prev2=1,prev3=1;
如果(n>2)
{
对于(变量i=2;in;i--)
{
padovan=prev3-prev1;
prev3=prev2;
prev2=prev1;
prev1=padovan;
}
}
返回帕多瓦;
}
代码还将计算负数。
它可以处理N大于156的P(N)。
(因为对于较大的帕多瓦人来说,它太小了)

测试:

for (var i = 0; i <= 21; i++) Console.Write("{0} ", GetPadovan(i)); 
static public long GetPadovanSlowRecursive(int n)
{
    if (n == 0 || n == 1 || n == 2) return 1;
    if (n < 0 || n > 156) return 0;
    return GetPadovanSlowRecursive(n - 2) + GetPadovanSlowRecursive(n - 3);
}
var timer = new System.Diagnostics.Stopwatch(); timer.Stop();
var padovan = new BigInteger();
int num = 72;

timer.Restart(); padovan = GetPadovanSlowRecursive(num); timer.Stop();
Console.WriteLine("GetPadovanSlowRecursive({0}):\t{1}\t{2,12:F6} ms", num, padovan, timer.Elapsed.TotalMilliseconds);

timer.Restart(); padovan = GetPadovanRecursive(num); timer.Stop();
Console.WriteLine("GetPadovanRecursive({0}):\t{1}\t{2,12:F6} ms", num, padovan, timer.Elapsed.TotalMilliseconds);

timer.Restart(); padovan = GetPadovanBinomial(num); timer.Stop();
Console.WriteLine("GetPadovanBinomial({0}):\t\t{1}\t{2,12:F6} ms", num, padovan, timer.Elapsed.TotalMilliseconds);

timer.Restart(); padovan = GetPadovanSumCombos(num); timer.Stop();
Console.WriteLine("GetPadovanSumCombos({0}):\t{1}\t{2,12:F6} ms", num, padovan, timer.Elapsed.TotalMilliseconds);

timer.Restart(); padovan = GetPadovan(num); timer.Stop();
Console.WriteLine("GetPadovan({0}):\t\t\t{1}\t{2,12:F6} ms", num, padovan, timer.Elapsed.TotalMilliseconds);
其他方法

新的更快的递归:

for (var i = 0; i <= 21; i++) Console.Write("{0} ", GetPadovan(i)); 
static public long GetPadovanSlowRecursive(int n)
{
    if (n == 0 || n == 1 || n == 2) return 1;
    if (n < 0 || n > 156) return 0;
    return GetPadovanSlowRecursive(n - 2) + GetPadovanSlowRecursive(n - 3);
}
var timer = new System.Diagnostics.Stopwatch(); timer.Stop();
var padovan = new BigInteger();
int num = 72;

timer.Restart(); padovan = GetPadovanSlowRecursive(num); timer.Stop();
Console.WriteLine("GetPadovanSlowRecursive({0}):\t{1}\t{2,12:F6} ms", num, padovan, timer.Elapsed.TotalMilliseconds);

timer.Restart(); padovan = GetPadovanRecursive(num); timer.Stop();
Console.WriteLine("GetPadovanRecursive({0}):\t{1}\t{2,12:F6} ms", num, padovan, timer.Elapsed.TotalMilliseconds);

timer.Restart(); padovan = GetPadovanBinomial(num); timer.Stop();
Console.WriteLine("GetPadovanBinomial({0}):\t\t{1}\t{2,12:F6} ms", num, padovan, timer.Elapsed.TotalMilliseconds);

timer.Restart(); padovan = GetPadovanSumCombos(num); timer.Stop();
Console.WriteLine("GetPadovanSumCombos({0}):\t{1}\t{2,12:F6} ms", num, padovan, timer.Elapsed.TotalMilliseconds);

timer.Restart(); padovan = GetPadovan(num); timer.Stop();
Console.WriteLine("GetPadovan({0}):\t\t\t{1}\t{2,12:F6} ms", num, padovan, timer.Elapsed.TotalMilliseconds);
通过使用参数,它可以在不爆发分叉函数调用的情况下完成。 使用长类型,因此P(N)仅适用于-316到156之间的N

static public long GetPadovanRecursive(int n, long prev1 = 1, long prev2 = 1, long prev3 = 1)
{
    if (n > 156 || n < -316) return 0;
    if (n > 2) return GetPadovanRecursive(--n, prev2 + prev3, prev1, prev2);
    if (n < 0) return GetPadovanRecursive(++n, prev3 - prev1, prev1, prev2);
    return prev1;
}
返回:

GetPadovanSlowRecursive(72):    448227521       13283,251300 ms
GetPadovanRecursive(72):        448227521           0,278300 ms
GetPadovanBinomial(72):         448227521           0,486300 ms
GetPadovanSumCombos(72):        448227521           0,722400 ms
GetPadovan(72):                 448227521           0,365900 ms

上面应用的方法是递归编程。它工作得很好,但最终需要花费大量时间来获得更大的数值

迭代方法的工作速度要快得多

下面是一个java程序来演示

class PadovanNumber {

public static long findPadovanNumRec (int n) {
    if ((n == 0) || (n == 1) || (n == 2))
        return 1;
    return findPadovanNumRec(n - 2) + findPadovanNumRec(n - 3); 
}

public static void main (String args[]) {
    int num = (Integer.valueOf(args[0])).intValue();
    //long timeRecStart = System.currentTimeMillis();   
    //long answerRec = findPadovanNumRec(num);
    //long timeRecEnd = System.currentTimeMillis(); 
    //System.out.println("Padovan Number Rec " + args[0] + " = " + answerRec + " Time Taken = " + (timeRecEnd - timeRecStart));
    long timeIterStart = System.currentTimeMillis();    
    long answerIter = findPadovanNumIter(num);
    long timeIterEnd = System.currentTimeMillis();  
    System.out.println("Padovan Number Iter " + args[0] + " = " + answerIter + " Time Taken = " + (timeIterEnd - timeIterStart));
}

public static long findPadovanNumIter (int n) {
    long sum = 0;
    long sumn1 = 1;
    long sumn2 = 1;
    long sumn3 = 1;
    for (int i = 0; i <= n; i++) {
        if ((i == 0) || (i == 1) || (i == 2))
            sum = 1;
        else {
            sum = sumn2 + sumn3;
            sumn3 = sumn2;
            sumn2 = sumn1;
            sumn1 = sum;
        }
    }
    return sum;
}
类PadovanNumber{
公共静态长findPadovanNumRec(int n){
如果((n==0)| |(n==1)| |(n==2))
返回1;
返回findPadovanNumRec(n-2)+findPadovanNumRec(n-3);
}
公共静态void main(字符串参数[]){
int num=(Integer.valueOf(args[0])).intValue();
//long-timeRecStart=System.currentTimeMillis();
//long answerRec=findPadovanNumRec(num);
//long-timeRecEnd=System.currentTimeMillis();
//System.out.println(“Padovan Number Rec”+args[0]+”=“+answerRec+”耗时=“+(timeRecEnd-timeRecStart));
long-timeIterStart=System.currentTimeMillis();
长应答器=findPadovanNumIter(num);
long-timeIterEnd=System.currentTimeMillis();
System.out.println(“Padovan编号Iter”+args[0]+”=“+answerIter+”耗时=“+(timeIterEnd-timeIterStart));
}
公共静态长findPadovanNumIter(int n){
长和=0;
长sumn1=1;
长sumn2=1;
长sumn3=1;

对于(int i=0;i,由于您专门询问C#和优化,我提供了我认为将为您指明正确方向的代码。首先是程序的输出:

From https://oeis.org/A000931
Ref: 1  0  0  1  0  1  1  1  2  2  3  4  5  7  9  12  16  21  28  37  49  65  86  114  151  200  265
Rec: 1  0  0  1  0  1  1  1  2  2  3  4  5  7  9  12  16  21  28  37  49  65  86  114  151  200  265
Qik: 1  0  0  1  0  1  1  1  2  2  3  4  5  7  9  12  16  21  28  37  49  65  86  114  151  200  265

Recursive method:   709.238100 ms   checksum: 35676949
Quick     method:     0.004800 ms   checksum: 35676949
正如您所看到的,递归方法和“quick”方法之间有很大的区别。这有几个原因。首先,递归需要大量额外的工作来移动每个函数调用的堆栈上和堆栈下的地址和值(并且有很多调用)。其次,此代码使用静态分配的数组(非线程安全)作为调用quick方法时的工作区。您可以在方法内分配数组,但这需要少量的额外时间

quick方法只需输入一个for循环,将值直接计算到静态数组中。您可以创建一个此类,并仅初始化前四个数组值一次。您还可以跟踪最后计算的值,以便在计算较大值时可以从该位置开始,或使用“n”作为直接检索预计算值的索引

下面是代码(创造性地命名为ConsoleApplication1;-))

使用系统;
使用系统诊断;
命名空间控制台应用程序1{
班级计划{
静态公共长GetPadovanRecursive(int n){
如果(n==0){返回1;}
如果(n==1){返回0;}
如果(n==2){返回0;}
如果(n==3){返回1;}
返回GetPadovanRecursive(n-2)+GetPadovanRecursive(n-3);
}
静态整数N_Max=8192;
静态长[]pdvn=新长[N_Max];
静态公共长GetPadovank
using System;
using System.Diagnostics;

namespace ConsoleApplication1 {
    class Program {
        static public long GetPadovanRecursive(int n) {
            if (n == 0) { return 1; }
            if (n == 1) { return 0; }
            if (n == 2) { return 0; }
            if (n == 3) { return 1; }
            return GetPadovanRecursive(n - 2) + GetPadovanRecursive(n - 3);
            }

        static int N_Max = 8192;
        static long[] pdvn = new long[N_Max];

        static public long GetPadovanQuick(int n) {
            Debug.Assert(n < N_Max);
            if (n == 0) { return 1; }
            if (n == 1) { return 0; }
            if (n == 2) { return 0; }
            if (n == 3) { return 1; }

            pdvn[3] = 1;
            for (int i = 4; i <= n; i++) {
                pdvn[i] = pdvn[i - 2] + pdvn[i - 3];
                }
            return pdvn[n];
            }


        static void Main(string[] args) {
            const int Count = 64;
            Stopwatch stp = new Stopwatch();

            // Sanity check
            Console.WriteLine("From https://oeis.org/A000931");
            Console.WriteLine("Ref: 1  0  0  1  0  1  1  1  2  2  3  4  5  7  9  12  16  21  28  37  49  65  86  114  151  200  265");
            Console.Write("Rec: ");
            for (int i = 0; i < 27; i++) {
                Console.Write("{0}  ", GetPadovanRecursive(i));
                }
            Console.WriteLine();
            Console.Write("Qik: ");
            for (var i = 0; i < 27; i++) {
                Console.Write("{0}  ", GetPadovanQuick(i));
                }
            Console.WriteLine();
            Console.WriteLine();

            long sum = 0;
            stp.Start();
            for (int i = 0; i < Count; i++) {
                sum += GetPadovanRecursive(i);
                }
            stp.Stop();
            Console.WriteLine("Recursive method: {0,12:F6} ms   checksum: {1}", stp.Elapsed.TotalMilliseconds, sum);


        sum = 0;
        stp.Restart();
        for (var i = 0; i < Count; i++) {
            sum += GetPadovanQuick(i);
            }
        Console.WriteLine("Quick     method: {0,12:F6} ms   checksum: {1}", stp.Elapsed.TotalMilliseconds, sum);
        }
    }
}
F: 1 1   P: 0 0 1
   1 0      1 0 1
            0 1 0 (or its transpose)
const long BIG_LIMIT = 1 << 30; // even 1L<<31 is too large
/** computes the <code>n</code>th Padovan number.<br/>
 * (index 0 for the first <code>1</code> in the run of three
 *  ("The triangle interpretation"))<br/>
 * This uses exponentiation of <code><br/>
 * 0 0 1 <br/>
 * 1 0 1 <br/>
 * 0 1 0 </code>by <code>n+5</code>
 * (returning the top-left element) */
static BigInteger padovan(long n) {
    if (n < 3)
        return (n < 0) ? 0 : 1;
// add offset from "non-negative" to "triangle" definition
    n += 5;
    long b, bit = highestOneBit(n),
        a = b = 0, c = 1; // consecutive Padovan numbers

    BigInteger p1 = a, p2 = b, p3 = c;
// raise to the nth power by squaring&multiplying
    while (0 < BIG_LIMIT) { // <= 0: use BigInteger from start
        long bc,
            A  = a*a + 2*(bc= b*c),
            B  = b*(2*a+b) + c*c;
        if (1 == (bit >>= 1))
            return 0 == (n&1) ? A : B; 
        long C  = 2*(a*c + bc) + b*b;
        //  D  = a²+2ab+b²+2bc+c² = (a+b+c)² - 2ac
        if (BIG_LIMIT < B) {
            if (0 == (n&bit)) {
                p3 = C;
                p2 = B;
                p1 = A;
            } else {
                p3 = A + B;
                p2 = C;
                p1 = B;
            }
            break;
        }
        if (0 == (n&bit)) {
            c = C;
            b = B;
            a = A;
        } else {
            c = A + B;
            b = C;
            a = B;
        }
    }

// raise to the nth power by squaring&multiplying
    while (true) {
        if (1 == (bit >>= 1))
            return 0 == (n&1)
                ? p1*p1 + 2 * p2*p3
                : p2*(2*p1+p2) + p3*p3;
        BigInteger
            p23= p2*p3,                // common to A & C
            s2 = p2*p2,                // common to B & C
            A  = p1*p1 + 2 * p23,
            B  = 2*p1*p2 + s2 + p3*p3,
            C  = 2*(p1*p3 + p23) + s2;
        if (0 == (n&bit)) {
            p3 = C;
            p2 = B;
            p1 = A;
        } else {
            p3 = A + B; // D = A + B
            p2 = C;
            p1 = B;
        }
    // ok, so the matrix squaring is multiplied out.
    // and common subexpressions are "eliminated"
    //  at the source code level.
    // and "the single step" uses explicit renaming
    //  and one addition.
    // So what - "most optimized" was called for,
    //  premature or not
    }
}
/** @return the most significant bit set - zero, if none. */
static long highestOneBit(long i) {
// Hacker'sDelight, Figure 3-1
    i |= (i >>  1);
    i |= (i >>  2);
    i |= (i >>  4);
    i |= (i >>  8);
    i |= (i >> 16);
    i |= (i >> 32);
    return i - (i >> 1);
}