매우 큰 피보나치 수의 모듈을 계산하는 내 알고리즘은 너무 느립니다

청소 마리우스 :

최대 10 전원 (18) : 목표는 n은 정말 엄청난 될 수있다, (m 최대 10 전원 5) F (n)의 모듈로 m을 계산하는 것이다.

내 알고리즘은 너무 느립니다.

내 방법 : 계산 및 반복 처리는 그 배열을 통해 다음, m까지의 모든 피보나치 수를 저장하고 피보나치의에 모듈을 적용 할 수 있습니다.

피사노 기간의 길이가 발견되면, 나는 어떤의 모듈을 계산하기 위해이 길이를 사용할 수 있습니다 F(n)

내 코드 :

import java.math.BigInteger;
import java.util.*;

public class FibonacciAgain {


    private static ArrayList<BigInteger> calc_fib() {
        ArrayList<BigInteger> fib = new ArrayList<>();

        fib.add(BigInteger.ZERO);
        fib.add(BigInteger.ONE);
        for (int i = 2; i <= 100000; i++) {
            fib.add(fib.get(i - 2).add(fib.get(i - 1)));

        }
        return fib;
    }

    private static long calculatePeriod(ArrayList<BigInteger> fib, long modulo) {

        long periodLength = 0;
        boolean periodFound = false;
        long[] period = new long[1000000];
        period[0] = 0;
        period[1] = 1;
        period[2] = 1;


        int i = 3;
        while (!periodFound) {
            //period[i] = fib.get(i)
            //period[i]= fib.get(i).divide(new BigInteger(String.valueOf(i))).longValue();
            //System.out.println("Fib at " + i + ": " + fib.get(i));
            period[i] = fib.get(i).mod(new BigInteger(String.valueOf(modulo))).longValue();
            //System.out.println("1:" + period[i]);
            //System.out.println("2:" + period[i - 1]);
           // System.out.println("3: " + period[i - 2]);
            if (i == 100000){
                periodFound = true;
                periodLength = i - 1;

            }

           // if (period[i] == 1 && period[i - 1] == 1 && period[i - 2] == 0) {
            if (period[i - 1] == 1 && period[i - 2] == 0) {
                periodFound = true;
                periodLength = i - 2;

                //System.out.println("found");

            }
            i++;

        }
        //System.out.println("Period Length:" + periodLength);

        return periodLength;
    }


    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        long n = scanner.nextLong();
        long m = scanner.nextLong();


        //M Fibonacci Numbers up to are stored here
        ArrayList<BigInteger> fib = new ArrayList<>();
        fib = calc_fib();

        // get the length of the pisano period
        long periodLength = calculatePeriod(fib, m);

        //
        long fibFirst = n%periodLength;
        System.out.println(fib.get((int) fibFirst).mod(new BigInteger(String.valueOf(m))).longValue());

    }
}

어떤 조언을 얼마나 빨리 만들려면?

범위 :

사용할 필요가 없습니다 BigInteger때문에이 :

1*2*3*4*...*N mod M
1+2+3+4+...+N mod M

와 같다

(...(((1*2 mod M)*3 mod M)*4 mod M)...*N mod M)
(...(((1+2 mod M)+3 mod M)+4 mod M)...+N mod M)

이는 많이 (가정 카라 츠바 승산) 발 빠르게해야 O(3*N*(n^log2(3)))하고 또는 부가 O(N*n)선형으로 도 훨씬 더 시간 상수와 multiplicants / 추가 항목의 비트 폭은 비례을 ...O(N)n

IIRC가 빠른 fibonaci의 계산을위한 공식은 (변환 곳도 O(N)근처 무언가로O(log(N))

다음은 몇 가지 예 : 빠른 피보나치 알고리즘

여기서 C ++ (순의 예 modfib0를 빠르고) 및 ( modfib1ALGO 2 × 2 행렬의 제곱 파워를 사용) :

//---------------------------------------------------------------------------
int modfib0(int n,int m)
    {
    for (int i=0,x0=0,x1=1;;)
        {
        if (i>=n) return x1; x0+=x1; x0%=m; i++;
        if (i>=n) return x0; x1+=x0; x1%=m; i++;
        }
    }
//---------------------------------------------------------------------------
// matrix 2x2:  0 1
//              2 3
void modmul2x2(int *c,int *a,int *b,int m)  // c[4] = a[4]*b[4] %m
    {
    int t[4];
    t[0]=((a[0]*b[0])+(a[1]*b[2]))%m;
    t[1]=((a[0]*b[1])+(a[1]*b[3]))%m;
    t[2]=t[1]; // result is symetric so no need to compute: t[2]=((a[2]*b[0])+(a[3]*b[2]))%m;
    t[3]=((a[2]*b[1])+(a[3]*b[3]))%m;
    c[0]=t[0];
    c[1]=t[1];
    c[2]=t[2];
    c[3]=t[3];
    }
void modpow2x2(int *c,int *a,int n,int m)   // c[4] = a[4]^n %m
    {
    int t[4];
    t[0]=a[0]; c[0]=1;
    t[1]=a[1]; c[1]=0;
    t[2]=a[2]; c[2]=0;
    t[3]=a[3]; c[3]=1;
    for (;;)
        {
        if (int(n&1)!=0) modmul2x2(c,c,t,m);
        n>>=1; if (!n) break;
        modmul2x2(t,t,t,m);
        }
    }
int modfib1(int n,int m)
    {
    if (n<=0) return 0;
    int a[4]={1,1,1,0};
    modpow2x2(a,a,n,m);
    return a[0];
    }
//---------------------------------------------------------------------------

사용 된 제약 조건을 준수하기 위해 조심 int변수가 있어야합니다 최소한 64 비트에서 폭 넓은! 나는 기존 32 비트 환경에서 그리고 난이 만 테스트 있도록 BIGINT 클래스 코드를 망치고 싶지 않았다 :

int x,m=30000,n=0x7FFFFFFF;
x=modfib0(n,m);
x=modfib1(n,m);

그리고 결과에 여기 :

[10725.614 ms] modfib0:17301 O(N)
[    0.002 ms] modfib1:17301 O(log2(N))

당신이 빠른 너 한테 훨씬 빠르게 선형보다 많이 볼 수있는 것처럼 ... 그러나 측정 된 시간은 Windows 환경에 대한 너무 작 나는 그것도 충분히 빨리해야한다고 생각합니다 그래서 대부분의 시간은 오버 헤드 대신 함수 자체의 가능성이 높습니다 에 대한 n=10^18복잡성 그대로 O(log2(N))I 추정 :

64-31 = 33 bits
0.002 ms * 33 = 0.066 ms

64 비트 연산이 잘 아래 수행해야합니다 그래서 0.1 ms내 컴퓨터에서 실행 시간의 I 생각 (AMD A8-5500 3.2 GHz의) 허용 ...

64 비트의 선형 너 한테은 다음과 같이 될 것이다 :

10.725614 s * 2^33 = 865226435999039488 s = 27.417*10^9 years

당신이 볼 수 있지만, 당신은 오래 전에 그 나이의 염색 것입니다 ...

추천

출처http://43.154.161.224:23101/article/api/json?id=311770&siteId=1