最速のフィボナッチを書く


10

これは、フィボナッチ数列に関するもう1つの課題です。

目標は、20'000'000 番目のフィボナッチ数を可能な限り高速に計算することです。10進数の出力は約4 MiBです。それでは始まります:

28543982899108793710435526490684533031144309848579

出力のMD5合計は

fa831ff5dd57a830792d8ded4c24c2cb

実行中に数値を計算し、結果をに送るプログラムを提出する必要がありstdoutます。私のマシンで測定した最速のプログラムが勝ちます。

追加のルールは次のとおりです。

  • x64 Linuxで実行可能なソースコードとバイナリを送信する必要があります
  • ソースコードは1 MiBより短くする必要があります。アセンブリの場合、バイナリのみがそれほど小さい場合でも許容されます。
  • 偽装した場合でも、計算する数値をバイナリに含めないでください。この数は実行時に計算する必要があります。
  • 私のコンピューターには2つのコアがあります。並列処理を使用できます

約4.5秒で実行される小さな実装をインターネットから取得しました。あなたが良いアルゴリズムを持っていると仮定すると、これを打ち負かすことは非常に難しいことではありません。


1
デュード、フロートの精度が不確定なセージのようなものは、1/10秒未満で実行されます。それは、次のように単純な表現ですphi = (1+sqrt(5))/2
JBernardo

4
数値を16進数で出力できますか?
キースランダル

2
@キース・ノープ。それは仕様の一部です。
FUZxxl 2011

3
それはあなたの CPUで測定されるので、私たちはそれについてさらにいくつかの情報を持っているかもしれませんね。IntelまたはAMD?L1および命令キャッシュのサイズ?命令セット拡張?
JB、

2
私が計算すると、開始文字列とMD5は20'000'000番目のものであり、単なる2'000'000番目のものではありません。
JB、

回答:


4

C、GMP、3.6秒

神々、しかしGMPはコードを醜くします。カラツバスタイルのトリックで、ダブリングのステップごとに2乗算を削減することができました。今、FUZxxlのソリューションを読んでいるので、最初に考えたわけではありません。私は私の袖の上にいくつかのトリックを持っています...多分私は後でそれらを試すつもりです。

#include <gmp.h>
#include <stdio.h>

#define DBL mpz_mul_2exp(u,a,1);mpz_mul_2exp(v,b,1);mpz_add(u,u,b);mpz_sub(v,a,v);mpz_mul(b,u,b);mpz_mul(a,v,a);mpz_add(a,b,a);
#define ADD mpz_add(a,a,b);mpz_swap(a,b);

int main(){
    mpz_t a,b,u,v;
    mpz_init(a);mpz_set_ui(a,0);
    mpz_init(b);mpz_set_ui(b,1);
    mpz_init(u);
    mpz_init(v);

    DBL
    DBL
    DBL ADD
    DBL ADD
    DBL
    DBL
    DBL
    DBL ADD
    DBL
    DBL
    DBL ADD
    DBL
    DBL ADD
    DBL ADD
    DBL
    DBL ADD
    DBL
    DBL
    DBL
    DBL
    DBL
    DBL
    DBL
    DBL /*Comment this line out for F(10M)*/

    mpz_out_str(stdout,10,b);
    printf("\n");
}

で構築gcc -O3 m.c -o m -lgmp


笑。識別子の名前付けは別として、それはまさに私の解決策です:)
JB

@JB:最初に!:D
ブース

それを保つ;)私の袖の次のトリックはCよりもHaskellから利益を得るでしょう
JB

まず、GHCバグにぶつかった私の袖をだまします。ドラット。私は2番目の方法にフォールバックする必要がありますが、実装するのはそれほど面白くないので、時間とモチベーションがかかります。
JB、

私のマシンでは3.6秒。
FUZxxl 2011

11

セージ

うーん、あなたは最速がコンパイルされたプログラムになるだろうと想定しているようです。あなたのためのバイナリはありません!

print fibonacci(2000000)

私のマシンでは、0.10 cpu秒、0.15 wall秒かかります。

編集:ノートブックの代わりにコンソールで時間を計る


1
私の考えは、CASがこれをどれだけ速く実行できるかを知ることではなく、自分でこれをどれだけ速くコーディングできるかということでした。
FUZxxl 2011

11
記録のために、私はこれを賢者とするために上げました。ビルトインを使用しないとは言わなかった。
ブース

5

ハスケル

これは私自身の試みですが、アルゴリズムを自分で作成したわけではありません。私はそれをhaskell.orgからコピーしData.Vectorて、有名なストリームフュージョンで使用するように改造しました。

import Data.Vector as V
import Data.Bits

main :: IO ()
main = print $ fib 20000000

fib :: Int -> Integer
fib n = snd . V.foldl' fib' (1,0) . V.dropWhile not $ V.map (testBit n) $ V.enumFromStepN (s-1) (-1) s
    where
        s = bitSize n
        fib' (f,g) p
            | p         = (f*(f+2*g),ss)
            | otherwise = (ss,g*(2*f-g))
            where ss = f*f+g*g

GHC 7.0.3と以下のフラグでコンパイルした場合、これには約4.5秒かかります。

ghc -O3 -fllvm fib.hs

奇妙なことですが、20000000を40000000に変更して、期待した数を出力する必要がありました。
JB、

ゴッチャ。する必要がありますenumFromStepN (s-1)代わりにenumFromStepN s
JB

@JBこのすべての混乱でごめんなさい。最初に、さまざまな値でプログラムをテストして、かなり大きな数を取得し、出力をさまざまなファイルに保存しました。しかし、いくつかは私がそれらを混乱させた。希望する結果に合わせて番号を更新しました。
FUZxxl 2011

@boothbyいいえ、目的のフィボナッチ数ではなく、リファレンス出力を変更しましたが、これは誤りでした。
FUZxxl 2011

補足:私のマシンでは約1.5秒ですが、LLVMもData.Vectorも重要な利点をもたらさないようです。
JB

4

 MoO moO MoO mOo MOO OOM MMM moO moO
 MMM mOo mOo moO MMM mOo MMM moO moO
 MOO MOo mOo MoO moO moo mOo mOo moo

ムー!(時間がかかります。牛乳を飲んでください...)


1
注:これは実際に機能しますが、おそらく20,000,000に達することはありません...
Timtech 2014年

2

Mathematica、解釈:

First@Timing[Fibonacci[2 10^6]]

時限:

0.032 secs on my poor man's laptop.

そしてもちろん、バイナリはありません。


に出力しませんstdout
ブース

@boothby間違い。コマンドラインインターフェイスを使用する場合は、標準出力に書き込みます。たとえば、stackoverflow.com
questions / 6542537 /…を

いいえ、コマンドラインインターフェースバージョン6.0を使用しています。を使用しても-batchoutput、タイミング情報のみが出力され、フィボナッチ数出力されません
ブース

Mathematicaを持っていないため、再生できません。
FUZxxl 2011

5
curl 'http://www.wolframalpha.com/input/?i=Fibonacci%5B2+10^6%5D' | grep 'Decimal approximation:' | sed ... それはあなたのインターネット接続の速度に関して一定の時間で実行されます。;-)
ESultanik 2011

2

私のラップトップでOcaml、0.856秒

zarithライブラリが必要です。私はBig_intを使用しましたが、zarithに比べて遅い犬です。同じコードで10分かかりました!ほとんどの時間はいまいましい数(9分半くらい)を印刷するのに費やされました!

module M = Map.Make
  (struct
    type t = int
    let compare = compare
   end)

let double b = Z.shift_left b 1
let ( +. ) b1 b2 = Z.add b1 b2
let ( *. ) b1 b2 = Z.mul b1 b2

let cache = ref M.empty 
let rec fib_log n =
  if n = 0
  then Z.zero
  else if n = 1
  then Z.one
  else if n mod 2 = 0
  then
    let f_n_half = fib_log_cached (n/2)
    and f_n_half_minus_one = fib_log_cached (n/2-1)
    in f_n_half *. (f_n_half +. double f_n_half_minus_one)
  else
    let f_n_half = fib_log_cached (n/2)
    and f_n_half_plus_one = fib_log_cached (n/2+1)
    in (f_n_half *. f_n_half) +.
    (f_n_half_plus_one *. f_n_half_plus_one)
and fib_log_cached n =
    try M.find n !cache
    with Not_found ->
      let res = fib_log n
      in cache := M.add n res !cache;
      res

let () =
  let res = fib_log 20_000_000 in
  Z.print res; print_newline ()

ライブラリがどれほどの違いを生んだか、信じられません!


1
比較として、@ boothbyのソリューションを私のラップトップで実行するには0.875秒かかります。違いは無視できるようです。また、どうやら私のラップトップは高速です:o
ReyCharles

1

ハスケル

私のシステムでは、これはFUZxxlの回答とほぼ同じ速度で実行されます〜17秒ではなく〜18秒)。

main = print $ fst $ fib2 20000000

-- | fib2: Compute (fib n, fib (n+1)).
--
-- Having two adjacent Fibonacci numbers lets us
-- traverse up or down the series efficiently.
fib2 :: Int -> (Integer, Integer)

-- Guard against negative n.
fib2 n | n < 0 = error "fib2: negative index"

-- Start with a few base cases.
fib2 0 = (0, 1)
fib2 1 = (1, 1)
fib2 2 = (1, 2)
fib2 3 = (2, 3)

-- For larger numbers, derive fib2 n from fib2 (n `div` 2)
-- This takes advantage of the following identity:
--
--    fib(n) = fib(k)*fib(n-k-1) + fib(k+1)*fib(n-k)
--             where n > k
--               and k ≥ 0.
--
fib2 n =
    let (a, b) = fib2 (n `div` 2)
     in if even n
        then ((b-a)*a + a*b, a*a + b*b)
        else (a*a + b*b, a*b + b*(a+b))

いいね。私はハスケルが大好きです。
アーレン、2011

これはghciで実行しました。かなり感動しました。Haskellは、この種の数学コード問題に最適です。
Undreren

1

C、素朴なアルゴリズム

好奇心旺盛で、以前にgmpを使用したことがなかったので...

#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>

int main(int argc, char *argv[]){
    int n = (argc>1)?atoi(argv[1]):0;

    mpz_t temp,prev,result;
    mpz_init(temp);
    mpz_init_set_ui(prev, 0);
    mpz_init_set_ui(result, 1);

    for(int i = 2; i <= n; i++) {
        mpz_add(temp, result, prev);
        mpz_swap(temp, result);
        mpz_swap(temp, prev);
    }

    printf("fib(%d) = %s\n", n, mpz_get_str (NULL, 10, result));

    return 0;
}

fib(100万)は約7秒かかるので、このアルゴリズムは競争に勝ちません。


1

私はSBCLに行列乗算メソッド(sicp、http: //sicp.org.ua/sicp/Exercise1-19から)を実装しましたが、完了するまでに約30秒かかります。GMPを使用してCに移植したところ、私のマシンでは約1.36秒で正しい結果が返されました。ブースの答えと同じくらい速いです。

#include <gmp.h>
#include <stdio.h>

int main()
{
  int n = 20000000;

  mpz_t a, b, p, q, psq, qsq, twopq, bq, aq, ap, bp;
  int count = n;

  mpz_init_set_si(a, 1);
  mpz_init_set_si(b, 0);
  mpz_init_set_si(p, 0);
  mpz_init_set_si(q, 1);
  mpz_init(psq);
  mpz_init(qsq);
  mpz_init(twopq);
  mpz_init(bq);
  mpz_init(aq);
  mpz_init(ap);
  mpz_init(bp);

  while(count > 0)
    {
      if ((count % 2) == 0)
        {
          mpz_mul(psq, p, p);
          mpz_mul(qsq, q, q);
          mpz_mul(twopq, p, q);
          mpz_mul_si(twopq, twopq, 2);

          mpz_add(p, psq, qsq);    // p -> (p * p) + (q * q)
          mpz_add(q, twopq, qsq);  // q -> (2 * p * q) + (q * q) 
          count/=2;
        }

      else
       {
          mpz_mul(bq, b, q);
          mpz_mul(aq, a, q);
          mpz_mul(ap, a, p);
          mpz_mul(bp, b, p);

          mpz_add(a, bq, aq);      // a -> (b * q) + (a * q)
          mpz_add(a, a, ap);       //              + (a * p)

          mpz_add(b, bp, aq);      // b -> (b * p) + (a * q)

          count--;
       }

    }

  gmp_printf("%Zd\n", b);
  return 0;
}

1

Java:計算に8秒、書き込みに18秒

public static BigInteger fibonacci1(int n) {
    if (n < 0) explode("non-negative please");
    short charPos = 32;
    boolean[] buf = new boolean[32];
    do {
        buf[--charPos] = (n & 1) == 1;
        n >>>= 1;
    } while (n != 0);
    BigInteger a = BigInteger.ZERO;
    BigInteger b = BigInteger.ONE;
    BigInteger temp;
    do {
        if (buf[charPos++]) {
            temp = b.multiply(b).add(a.multiply(a));
            b = b.multiply(a.shiftLeft(1).add(b));
            a = temp;
        } else {
            temp = b.multiply(b).add(a.multiply(a));
            a = a.multiply(b.shiftLeft(1).subtract(a));
            b = temp;
        }
    } while (charPos < 32);
    return a;
}

public static void main(String[] args) {
    BigInteger f;
    f = fibonacci1(20000000);
    // about 8 seconds
    System.out.println(f.toString());
    // about 18 seconds
}

0

行く

恥ずかしいほど遅いです。私のコンピューターでは、3分弱かかります。ただし、120回の再帰呼び出しです(キャッシュを追加した後)。これは大量のメモリを使用する可能性があることに注意してください(1.4 GiBなど)。

package main

import (
    "math/big"
    "fmt"
)

var cache = make(map[int64] *big.Int)

func fib_log_cache(n int64) *big.Int {
    if res, ok := cache[n]; ok {
        return res
    }
    res := fib_log(n)
    cache[n] = res
    return res
}

func fib_log(n int64) *big.Int {
    if n <= 1 {
        return big.NewInt(n)
    }

    if n % 2 == 0 {
        f_n_half := fib_log_cache(n/2)
        f_n_half_minus_one := fib_log_cache(n/2-1)
        res := new(big.Int).Lsh(f_n_half_minus_one, 1)
        res.Add(f_n_half, res)
        res.Mul(f_n_half, res)
        return res
    }
    f_n_half := fib_log_cache(n/2)
    f_n_half_plus_one := fib_log_cache(n/2+1)
    res := new(big.Int).Mul(f_n_half_plus_one, f_n_half_plus_one)
    tmp := new(big.Int).Mul(f_n_half, f_n_half)
    res.Add(res, tmp)
    return res
}

func main() {
    fmt.Println(fib_log(20000000))
}

goルーチンを使用して(キャッシュを追加する前に)並列化を試み、19 GiBのメモリを使用して開始しました:/
ReyCharles

-4

疑似コード(皆さんが何を使っているのかわかりません)

product = 1
multiplier = 3 // 3 is fibonacci sequence, but this can be any number, 
      // generating an infinite amount of sequences
y = 28 // the 2^x-1 term, so 2^28-1=1,284,455,535th term
for (int i = 1; int < y; i++) {
  product= sum*multiplier-1
  multiplier= multiplier^2-2
}
multiplier=multiplier-product // 2^28+1 1,284,455,537th 

私のコンピューターがこれら2つの条件を実行するのに56時間かかりました。私のコンピュータは一種の安っぽいです。10月22日に番号をテキストファイルに記載します。1.2ギグは、私の接続で共有するには少し大きいです。


1
私は君の答えに戸惑う。疑似コード?まだタイミングがありますか?コードを投稿してください!言語は関係ありません!
ブースバイ

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.