सबसे तेज फिबोनाची लिखें


10

यह फिबोनाची संख्या के बारे में एक और चुनौती है।

लक्ष्य के रूप में उपवास के रूप में 20'000'000 वें Fibonacii संख्या की गणना करना है। दशमलव आउटपुट लगभग 4 MiB बड़ा है; इसके साथ शुरू होता है:

28543982899108793710435526490684533031144309848579

आउटपुट का एमडी 5 योग है

fa831ff5dd57a830792d8ded4c24c2cb

आपको एक प्रोग्राम सबमिट करना होगा जो दौड़ते समय संख्या की गणना करता है और परिणाम को डालता है stdout। सबसे तेज कार्यक्रम, जैसा कि मेरी अपनी मशीन पर मापा जाता है, जीतता है।

यहाँ कुछ अतिरिक्त नियम दिए गए हैं:

  • आपको स्रोत कोड और बाइनरी रनवेबल को x64 लिनक्स पर सबमिट करना होगा
  • स्रोत कोड 1 MiB से कम होना चाहिए, विधानसभा के मामले में यह भी स्वीकार्य है यदि केवल बाइनरी वह छोटा है।
  • आपको अपने बाइनरी में गणना की जाने वाली संख्या को शामिल नहीं करना चाहिए, यहां तक ​​कि प्रच्छन्न शैली में भी। संख्या की गणना रनटाइम पर की जानी है।
  • मेरे कंप्यूटर में दो कोर हैं; आपको समानता का उपयोग करने की अनुमति है

मैंने इंटरनेट से एक छोटा कार्यान्वयन लिया जो लगभग 4.5 सेकंड में चलता है। इसे हरा पाना बहुत मुश्किल नहीं होना चाहिए, यह मानते हुए कि आपके पास एक अच्छा एल्गोरिथ्म है।


1
यार, ऋषि की तरह कुछ भी है जो अनिश्चित फ्लोट सटीक है उस चीज को सेकंड के कम thant 1 / 10th में चलाएगा। यह सिर्फ एक सरल अभिव्यक्ति हैphi = (1+sqrt(5))/2
जेबर्नार्डो

4
क्या हम हेक्स में संख्या का उत्पादन कर सकते हैं?
कीथ रान्डेल

2
@ कीथ नोप। यह कल्पना का हिस्सा है।
फ़ूजएक्सएक्सएल

3
चूंकि यह आपके सीपीयू पर मापा जाना है , इसलिए हमें इसके बारे में कुछ और जानकारी हो सकती है, क्या हम नहीं? इंटेल या एएमडी? L1 और अनुदेश कैश का आकार? निर्देश सेट एक्सटेंशन?
जेबी

2
जैसा कि मैं इसकी गणना करता हूं, आपका स्टार्ट स्ट्रिंग और MD5 20'000'000 वें नंबर के लिए है, मात्र 2'000'000 वें नंबर पर नहीं।
जेबी

जवाबों:


4

C जीएमपी के साथ, 3.6 एस

भगवान, लेकिन GMP कोड को बदसूरत बना देता है। करत्सुबा-शैली की चाल के साथ, मैंने दोगुना कदम प्रति 2 गुणक में कटौती करने में कामयाब रहा। अब जब कि मैं फ़ूजएक्सल का समाधान पढ़ रहा हूं, तो मुझे यह विचार करने वाला पहला नहीं है। मैं अपनी आस्तीन ऊपर एक और चाल है ... शायद मैं बाद में उन्हें बाहर की कोशिश करेंगे।

#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: FIRST! : D
बूथबीज

इसे रखो;) मेरी आस्तीन ऊपर अगली चाल सी से अधिक हास्केल से लाभान्वित होगी
जेबी

पहली चाल मेरी आस्तीन एक GHC बग में टकरा गई। Drat। मुझे दूसरे नंबर पर वापस आना होगा, जिसे लागू करने में उतना मज़ा नहीं आता है, इसलिए इसमें समय लगेगा और प्रेरणा मिलेगी।
जेबी

मेरी मशीन पर 3.6 सेकेंड।
FUZxxl

11

साधू

हम्म, आपको लगता है कि सबसे तेज एक संकलित कार्यक्रम होने जा रहा है। आपके लिए कोई बाइनरी नहीं!

print fibonacci(2000000)

मेरी मशीन पर, 0.10 सीपीयू सेकंड, 0.15 दीवार सेकंड लगते हैं।

संपादित करें: नोटबुक के बजाय कंसोल पर समयबद्ध


1
मेरा विचार यह जानने का नहीं था कि आपकी कैस कितनी तेजी से ऐसा कर सकती है, बल्कि आप कितनी तेजी से इसे खुद से कोड कर सकते हैं।
फ़ूजएक्सएक्सएल

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 तक बदलने की आवश्यकता थी।
जेबी

पकड़ लिया। enumFromStepN (s-1)इसके बजायenumFromStepN s
JB

@JB इस सभी भ्रम के लिए क्षमा करें। मैंने शुरू में विभिन्न मूल्यों के साथ कार्यक्रम का परीक्षण किया ताकि एक बड़ी संख्या प्राप्त की जा सके और आउटपुट को विभिन्न फाइलों में सहेजा जा सके। लेकिन कुछ ने मुझे कैसे भ्रमित किया। मैंने वांछित परिणाम से मिलान करने के लिए नंबर अपडेट किया है।
फ़ूजएक्सएक्सएल

@boothby नहीं, मैंने वांछित रिट्रेसमेंट नंबर नहीं बदला, बल्कि संदर्भ आउटपुट, जो कि गलत था।
फ़ूजएक्सएक्सएल

साइड नोट: यह मेरी मशीन पर लगभग 1.5s है, लेकिन न तो LLVM डेटा नहीं। कोई भी महत्वपूर्ण लाभ नहीं ले रहा है।
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 तक कभी नहीं पहुंचेगा ...
टाइमटेक

2

गणितज्ञ, व्याख्या की गई:

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

समय समाप्त हो गया:

0.032 secs on my poor man's laptop.

और हां, कोई बाइनरी नहीं।


तक नहीं छपता stdout
बूथ

@ बाबोबी गलत। यदि आप कमांड लाइन इंटरफ़ेस का उपयोग करते हैं तो यह मानक आउटपुट को लिखता है। उदाहरण के लिए देखें stackoverflow.com/questions/6542537/…
डॉ। बेलिसरियस

नहीं, मैं कमांडलाइन इंटरफ़ेस, संस्करण 6.0 का उपयोग कर रहा हूं। यहां तक ​​कि उपयोग करते हुए -batchoutput, यह केवल समय की जानकारी प्रिंट करता है और कि फाइबोनैचि संख्या।
बूथ

क्षमा करें, जब से मेरे पास गणित नहीं है पुन: पेश नहीं कर सकता।
फ़ूजएक्सएक्सएल

5
curl 'http://www.wolframalpha.com/input/?i=Fibonacci%5B2+10^6%5D' | grep 'Decimal approximation:' | sed ... यह आपके इंटरनेट कनेक्शन की गति के संबंध में निरंतर समय में चलता है। ;-)
17ult में ESultanik

2

मेरे लैपटॉप पर Ocaml, 0.856s

ज़ारिथ पुस्तकालय की आवश्यकता है। मैंने Big_int का उपयोग किया लेकिन यह zarith की तुलना में कुत्ता धीमा है। एक ही कोड के साथ 10 मिनट लग गए! अधिकांश समय लानत संख्या (9 or मिनट या तो) को प्रिंट करने में बिताया गया था !

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
तुलना के लिए @ बूथबी का समाधान मेरे लैपटॉप पर चलने के लिए 0.875s लेता है। ऐसा लगता है कि अंतर नगण्य है। इसके अलावा, जाहिरा तौर पर मेरा लैपटॉप तेज है : o
ReyCharles

1

हास्केल

मेरे सिस्टम पर, यह लगभग FZxxl के उत्तर के रूप में तेजी से चलता है (~ 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))

अच्छा लगा। मुझे हास्केल बहुत पसंद है।
अर्ल

मैंने इसे गश में दौड़ाया। मैं बहुत प्रभावित हुआ। इस प्रकार की गणितीय कोड समस्याओं के लिए हास्केल महान है।
अंडरट्रॉन

1

सी, भोली एल्गोरिथ्म

उत्सुक था, और मैंने पहले 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;
}

फाइब (1 मिलियन) लगभग 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

जावा: गणना करने के लिए 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))
}

मैंने गो रूटीन का उपयोग करते हुए इसे (कैश जोड़ने से पहले) समानांतर करने की कोशिश की और यह 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 

उन दो शब्दों को करने में मेरे कंप्यूटर को 56 घंटे लगे। मेरा कंप्यूटर भद्दा है। मेरे पास 22 अक्टूबर को एक टेक्स्ट फाइल में नंबर होगा। 1.2 गिग्स मेरे कनेक्शन पर साझा करने के लिए थोड़ा बड़ा है।


1
मैं आपके उत्तर से भ्रमित हूं। स्यूडोकोड? और फिर भी आपके पास समय है? कोड पोस्ट करें! भाषा कोई फर्क नहीं पड़ता!
5

यह, और आउटपुट केवल 4 मिलियन या इतने अंकों का माना जाता है ...
21
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.