परियोजना यूलर के साथ गति की तुलना: सी बनाम पायथन बनाम एरलंग बनाम हास्केल


672

मैंने प्रॉजेक्ट Euler से प्रॉब्लम # 12 लिया है को एक प्रोग्रामिंग एक्सरसाइज के रूप में लिया है और सी, पायथन, एरलैंग और हास्केल में मेरी (निश्चित रूप से इष्टतम नहीं) कार्यान्वयन की तुलना करने के लिए। कुछ उच्च निष्पादन समय प्राप्त करने के लिए, मैं मूल समस्या में बताए अनुसार 500 के बजाय 1000 से अधिक विभाजकों के साथ पहले त्रिकोण संख्या की खोज करता हूं।

परिणाम निम्नलिखित है:

सी:

lorenzo@enzo:~/erlang$ gcc -lm -o euler12.bin euler12.c
lorenzo@enzo:~/erlang$ time ./euler12.bin
842161320

real    0m11.074s
user    0m11.070s
sys 0m0.000s

अजगर:

lorenzo@enzo:~/erlang$ time ./euler12.py 
842161320

real    1m16.632s
user    1m16.370s
sys 0m0.250s

अजगर के साथ अजगर:

lorenzo@enzo:~/Downloads/pypy-c-jit-43780-b590cf6de419-linux64/bin$ time ./pypy /home/lorenzo/erlang/euler12.py 
842161320

real    0m13.082s
user    0m13.050s
sys 0m0.020s

Erlang:

lorenzo@enzo:~/erlang$ erlc euler12.erl 
lorenzo@enzo:~/erlang$ time erl -s euler12 solve
Erlang R13B03 (erts-5.7.4) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.4  (abort with ^G)
1> 842161320

real    0m48.259s
user    0m48.070s
sys 0m0.020s

हास्केल:

lorenzo@enzo:~/erlang$ ghc euler12.hs -o euler12.hsx
[1 of 1] Compiling Main             ( euler12.hs, euler12.o )
Linking euler12.hsx ...
lorenzo@enzo:~/erlang$ time ./euler12.hsx 
842161320

real    2m37.326s
user    2m37.240s
sys 0m0.080s

सारांश:

  • सी: 100%
  • अजगर: 692% (PyPy के साथ 118%)
  • एरलैंग: 436% (रिचर्डस के लिए 135% धन्यवाद)
  • हास्केल: 1421%

मुझे लगता है कि सी का एक बड़ा फायदा है क्योंकि यह गणना के लिए लंबे समय तक उपयोग करता है और अन्य तीन के रूप में मनमाना लंबाई पूर्णांक नहीं है। इसके अलावा इसे पहले एक रनटाइम लोड करने की आवश्यकता नहीं है (दूसरों को?)।

प्रश्न 1: क्या एरलांग, पायथन और हास्केल मनमाने ढंग से लंबाई पूर्णांक का उपयोग करने के कारण गति खो देते हैं या जब तक मान कम से कम नहीं होते हैंMAXINT ?

प्रश्न 2: हास्केल इतना धीमा क्यों है? क्या कोई कंपाइलर ध्वज है जो ब्रेक को बंद कर देता है या यह मेरा कार्यान्वयन है? (उत्तरार्द्ध काफी संभावित है क्योंकि हास्केल मेरे लिए सात मुहरों वाली एक पुस्तक है।)

प्रश्न 3: क्या आप मुझे कुछ संकेत दे सकते हैं कि मैं कारकों को निर्धारित करने के तरीके को बदले बिना इन कार्यान्वयनों को कैसे अनुकूलित करूं? किसी भी तरह से अनुकूलन: अच्छे, तेज, भाषा के लिए अधिक "देशी"।

संपादित करें:

प्रश्न 4: क्या मेरे कार्यात्मक कार्यान्वयन एलसीओ (अंतिम कॉल ऑप्टिमाइज़ेशन, उर्फ ​​टेल रिकर्शन एलिमिनेशन) की अनुमति देते हैं और इसलिए कॉल स्टैक पर अनावश्यक फ़्रेम जोड़ने से बचते हैं?

मैंने वास्तव में चार भाषाओं में समान एल्गोरिथ्म को यथासंभव लागू करने की कोशिश की, हालांकि मुझे यह स्वीकार करना होगा कि मेरा हास्केल और एर्लैंग ज्ञान बहुत सीमित है।


उपयोग किए गए स्रोत कोड:

#include <stdio.h>
#include <math.h>

int factorCount (long n)
{
    double square = sqrt (n);
    int isquare = (int) square;
    int count = isquare == square ? -1 : 0;
    long candidate;
    for (candidate = 1; candidate <= isquare; candidate ++)
        if (0 == n % candidate) count += 2;
    return count;
}

int main ()
{
    long triangle = 1;
    int index = 1;
    while (factorCount (triangle) < 1001)
    {
        index ++;
        triangle += index;
    }
    printf ("%ld\n", triangle);
}

#! /usr/bin/env python3.2

import math

def factorCount (n):
    square = math.sqrt (n)
    isquare = int (square)
    count = -1 if isquare == square else 0
    for candidate in range (1, isquare + 1):
        if not n % candidate: count += 2
    return count

triangle = 1
index = 1
while factorCount (triangle) < 1001:
    index += 1
    triangle += index

print (triangle)

-module (euler12).
-compile (export_all).

factorCount (Number) -> factorCount (Number, math:sqrt (Number), 1, 0).

factorCount (_, Sqrt, Candidate, Count) when Candidate > Sqrt -> Count;

factorCount (_, Sqrt, Candidate, Count) when Candidate == Sqrt -> Count + 1;

factorCount (Number, Sqrt, Candidate, Count) ->
    case Number rem Candidate of
        0 -> factorCount (Number, Sqrt, Candidate + 1, Count + 2);
        _ -> factorCount (Number, Sqrt, Candidate + 1, Count)
    end.

nextTriangle (Index, Triangle) ->
    Count = factorCount (Triangle),
    if
        Count > 1000 -> Triangle;
        true -> nextTriangle (Index + 1, Triangle + Index + 1)  
    end.

solve () ->
    io:format ("~p~n", [nextTriangle (1, 1) ] ),
    halt (0).

factorCount number = factorCount' number isquare 1 0 - (fromEnum $ square == fromIntegral isquare)
    where square = sqrt $ fromIntegral number
          isquare = floor square

factorCount' number sqrt candidate count
    | fromIntegral candidate > sqrt = count
    | number `mod` candidate == 0 = factorCount' number sqrt (candidate + 1) (count + 2)
    | otherwise = factorCount' number sqrt (candidate + 1) count

nextTriangle index triangle
    | factorCount triangle > 1000 = triangle
    | otherwise = nextTriangle (index + 1) (triangle + index + 1)

main = print $ nextTriangle 1 1

55
@ जोचेन (और सेठ) वास्तव में यह नहीं है कि सी तेज या भयानक है, लेकिन इसे निष्पादन कोड लिखने में आसान माना जाता है (यह सच नहीं हो सकता है, लेकिन अधिकांश कार्यक्रम सक्षम होते हैं, इसलिए यह पर्याप्त है)। जैसा कि मैंने अपने उत्तर में खोज की है, और समय के साथ सही पाया है, प्रोग्रामर कौशल और चुने हुए भाषा के लिए सामान्य अनुकूलन के ज्ञान का बहुत महत्व है (विशेषकर हास्केल के लिए)।
थॉमस एम। ड्यूबिसन

52
बस के साथ की जाँच मेथेमेटिका - यह 0.25sec लेता है (सी के साथ इसे यहाँ 6sec लेता है), और कोड सिर्फ यह है: Euler12[x_Integer] := Module[{s = 1}, For[i = 2, DivisorSigma[0, s] < x, i++, s += i]; s]। हुर्रे!
tsvikas

35
क्या सी और असेंबली के बीच इन युद्धों को याद करने वाला कोई और है? "ज़रूर! आप अपने कोड को C में 10x तेज़ी से लिख सकते हैं, लेकिन क्या आपका सी कोड इस तेज़ी से चला सकता है? ..." मुझे यकीन है कि मशीन-कोड और असेंबली के बीच एक ही लड़ाई लड़ी गई थी।
जेएस।

39
@ जेएस: संभवत: नहीं, क्योंकि असेंबली केवल एमनॉमिक्स का एक सेट है जिसे आप कच्चे बाइनरी मशीन कोड के बजाय टाइप करते हैं - आम तौर पर उनके बीच 1-1 पत्राचार होता है।
कैलम रोजर्स

9
हास्केल के लिए निष्कर्ष: -O2 इसे 3x के बारे में स्पीडअप देता है, और 12x-14x के कुल स्पीड के लिए 4x-6x के बारे में इंटर्जर के बजाय Int का उपयोग करता है।
विल नेस

जवाबों:


794

का उपयोग करते हुए GHC 7.0.3, gcc 4.4.6, Linux 2.6.29, एक x86_64 Core2 जोड़ी (2.5GHz) मशीन पर का उपयोग कर संकलन ghc -O2 -fllvm -fforce-recompहास्केल के लिए और gcc -O3 -lmसी के लिए

  • आपकी सी दिनचर्या 8.4 सेकंड में चलती है (शायद आपके रन की तुलना में तेज़) -O3 )
  • हास्केल समाधान 36 सेकंड (के कारण) में चलता है -O2 ध्वज के )
  • आपका factorCount'कोड स्पष्ट रूप से टाइप नहीं किया गया है और डिफ़ॉल्ट रूप से Integer(यहां मेरे गलत काम को सुधारने के लिए डैनियल के लिए धन्यवाद!)। एक स्पष्ट प्रकार का हस्ताक्षर (जो कि वैसे भी मानक अभ्यास है) देते हुए Intऔर 11.1 सेकंड में समय बदल जाता है
  • में factorCount'आप को बुलाया है fromIntegral। एक बदलाव का कोई परिणाम नहीं होता है (हालांकि कंपाइलर आपके लिए भाग्यशाली है)।
  • आपने उपयोग किया है modजहां remतेज और पर्याप्त है। इससे समय बदलकर 8.5 सेकंड हो जाता है
  • factorCount'लगातार दो अतिरिक्त तर्क लागू कर रहा है जो कभी नहीं बदलते ( number, sqrt)। एक कार्यकर्ता / रैपर परिवर्तन हमें देता है:
 $ time ./so
 842161320  

 real    0m7.954s  
 user    0m7.944s  
 sys     0m0.004s  

यह सही है, 7.95 सेकंड । लगातार सी समाधान की तुलना में आधा सेकंड तेज-fllvmध्वज के बिना मैं अभी भी मिल रहा हूं 8.182 seconds, इसलिए एनसीजी बैकेंड इस मामले में भी अच्छा कर रहा है।

निष्कर्ष: हास्केल कमाल है।

परिणाम कोड

factorCount number = factorCount' number isquare 1 0 - (fromEnum $ square == fromIntegral isquare)
    where square = sqrt $ fromIntegral number
          isquare = floor square

factorCount' :: Int -> Int -> Int -> Int -> Int
factorCount' number sqrt candidate0 count0 = go candidate0 count0
  where
  go candidate count
    | candidate > sqrt = count
    | number `rem` candidate == 0 = go (candidate + 1) (count + 2)
    | otherwise = go (candidate + 1) count

nextTriangle index triangle
    | factorCount triangle > 1000 = triangle
    | otherwise = nextTriangle (index + 1) (triangle + index + 1)

main = print $ nextTriangle 1 1

संपादित करें: तो अब जब हमने पता लगाया है कि, सवालों को हल करने देता है

प्रश्न 1: क्या मनमाना लंबाई पूर्णांक का उपयोग करने के कारण एरंग, पायथन और हैसेल की गति कम हो जाती है या क्या वे तब तक नहीं होते हैं जब तक मान MAXINT से कम होते हैं?

हास्केल में, का उपयोग करने Integerकी तुलना में धीमी है, Intलेकिन प्रदर्शन किए गए गणना पर कितना धीमा निर्भर करता है। सौभाग्य से (64 बिट मशीनों के लिए) Intपर्याप्त है। पोर्टेबिलिटी की खातिर आपको उपयोग करने के लिए मेरे कोड को फिर से लिखना चाहिए Int64या Word64(सी केवल भाषा नहीं है long)।

प्रश्न 2: हैसेल इतना धीमा क्यों है? क्या कोई कंपाइलर ध्वज है जो ब्रेक को बंद कर देता है या यह मेरा कार्यान्वयन है? (उत्तरार्द्ध काफी संभावित है क्योंकि हस्केल मेरे लिए सात मुहरों वाली एक पुस्तक है।)

प्रश्न 3: क्या आप मुझे कुछ संकेत दे सकते हैं कि मैं कारकों को निर्धारित करने के तरीके को बदलने के बिना इन कार्यान्वयनों को कैसे अनुकूलित करूं? किसी भी तरह से अनुकूलन: अच्छे, तेज, भाषा के लिए अधिक "देशी"।

यही मैंने ऊपर उत्तर दिया था। जवाब था

  • 0) के माध्यम से अनुकूलन का उपयोग करें -O2
  • 1) जब संभव हो तो तेज (विशेष रूप से: अनबॉक्स-सक्षम) प्रकारों का उपयोग करें
  • 2) remनहींmod (एक अक्सर भूल अनुकूलन) और
  • 3) कार्यकर्ता / आवरण परिवर्तन (शायद सबसे आम अनुकूलन)।

प्रश्न 4: क्या मेरे कार्यात्मक कार्यान्वयन एलसीओ को अनुमति देते हैं और इसलिए कॉल स्टैक पर अनावश्यक फ्रेम जोड़ने से बचते हैं?

हाँ, यह मुद्दा नहीं था। अच्छा काम और खुशी है कि आपने इस पर विचार किया।


25
@ कर्ल क्योंकि remवास्तव में modऑपरेशन का एक उप-घटक है (वे समान नहीं हैं)। यदि आप GHC बेस लाइब्रेरी में देखते हैं तो आप modकई स्थितियों के लिए परीक्षण देखते हैं और तदनुसार साइन को समायोजित करते हैं। (देखें modInt#में Base.lhs)
थॉमस एम Dubuisson

20
एक अन्य डेटा बिंदु: मैंने @ हाइपरबोरस के हास्केल को देखे बिना सी प्रोग्राम का त्वरित हास्केल अनुवाद लिखा । तो यह मानक मुहावरेदार हास्केल के लिए थोड़ा और करीब है, और केवल अनुकूलन मैं जानबूझ कर जोड़ा की जगह modके साथ remइस उत्तर (हे, उफ़) को पढ़ने के बाद। मेरे समय के लिए लिंक देखें, लेकिन लघु संस्करण "लगभग सी के समान" है।
सीए मैक्कैन

106
यहां तक ​​कि सोचा कि सी संस्करण मेरी मशीन पर तेजी से भाग गया, मेरे पास अब हास्केल के लिए एक नया सम्मान है। +1
सेठ कार्नेगी 14

11
यह मेरे लिए काफी आश्चर्यजनक है, हालांकि मुझे अभी तक इसे आज़माना है। चूंकि मूल factorCount'पूंछ पुनरावर्ती थी , इसलिए मैंने सोचा होगा कि संकलक अतिरिक्त मापदंडों को बदल नहीं सकता है और पूंछ पुनर्संयोजन को केवल बदलते मापदंडों के लिए अनुकूलित कर सकता है (हास्केल एक शुद्ध भाषा होने के बाद भी, यह आसान होना चाहिए)। किसी को भी लगता है कि संकलक ऐसा कर सकता है या मुझे और अधिक सिद्धांत पत्र पढ़ने के लिए वापस जाना चाहिए?
kizzx2

22
@ kizzx2: इसमें जोड़ा गया GHC टिकट है। मैंने जो कुछ भी समझा है, उससे यह परिवर्तन बंद वस्तुओं के अतिरिक्त आवंटन के परिणामस्वरूप हो सकता है। इसका मतलब कुछ मामलों में खराब प्रदर्शन है, लेकिन जैसा कि जोहान टिबेल अपने ब्लॉग पोस्ट में बताते हैं कि इससे बचा जा सकता है अगर परिणामस्वरूप आवरण को इनलाइन किया जा सकता है।
हमर

224

Erlang कार्यान्वयन के साथ कुछ समस्याएं हैं। निम्नलिखित के लिए आधारभूत के रूप में, आपके अनमॉडिफाइड एर्लांग प्रोग्राम के लिए मेरा मापा गया निष्पादन समय 47.6 सेकंड था, जबकि सी कोड के लिए 12.7 सेकंड था।

यदि आप कम्प्यूटेशनल रूप से गहन Erlang कोड चलाना चाहते हैं, तो पहली बात आपको देशी कोड का उपयोग करना चाहिए। संकलन erlc +native euler12को 41.3 सेकंड तक का समय मिला। हालांकि यह इस तरह के कोड पर देशी संकलन से उम्मीद से बहुत कम स्पीडअप (सिर्फ 15%) है, और समस्या आपके उपयोग की है -compile(export_all)। यह प्रयोग के लिए उपयोगी है, लेकिन यह तथ्य कि सभी कार्य संभावित रूप से बाहर से उपलब्ध हैं, देशी कंपाइलर बहुत रूढ़िवादी होते हैं। (सामान्य BEAM एमुलेटर इतना प्रभावित नहीं है।) इस घोषणा को बदलकर-export([solve/0]). बहुत बेहतर स्पीडअप मिलता है: 31.5 सेकंड (बेसलाइन से लगभग 35%)।

लेकिन कोड में ही एक समस्या है: प्रत्येक कारक के लिए कारक कारक लूप में, आप यह परीक्षण करते हैं:

factorCount (_, Sqrt, Candidate, Count) when Candidate == Sqrt -> Count + 1;

C कोड ऐसा नहीं करता है। सामान्य तौर पर, एक ही कोड के विभिन्न कार्यान्वयन के बीच उचित तुलना करना मुश्किल हो सकता है, और विशेष रूप से अगर एल्गोरिथ्म संख्यात्मक है, क्योंकि आपको यह सुनिश्चित करने की आवश्यकता है कि वे वास्तव में एक ही काम कर रहे हैं। कुछ टाइपकास्ट के कारण एक कार्यान्वयन में थोड़ी सी भी गोल त्रुटि, इसके कारण दूसरे की तुलना में कई अधिक पुनरावृत्तियों का कारण बन सकती है, हालांकि दोनों अंततः एक ही परिणाम तक पहुंचते हैं।

इस संभावित त्रुटि स्रोत को खत्म करने के लिए (और प्रत्येक पुनरावृत्ति में अतिरिक्त परीक्षण से छुटकारा पाने के लिए), मैं कारक कोड को निम्नानुसार फिर से लिखता हूं, सी कोड पर बारीकी से मॉडलिंग की गई है:

factorCount (N) ->
    Sqrt = math:sqrt (N),
    ISqrt = trunc(Sqrt),
    if ISqrt == Sqrt -> factorCount (N, ISqrt, 1, -1);
       true          -> factorCount (N, ISqrt, 1, 0)
    end.

factorCount (_N, ISqrt, Candidate, Count) when Candidate > ISqrt -> Count;
factorCount ( N, ISqrt, Candidate, Count) ->
    case N rem Candidate of
        0 -> factorCount (N, ISqrt, Candidate + 1, Count + 2);
        _ -> factorCount (N, ISqrt, Candidate + 1, Count)
    end.

इस पुन: लिखना, नहीं export_all, और मूल संकलन, ने मुझे निम्नलिखित रन समय दिया:

$ erlc +native euler12.erl
$ time erl -noshell -s euler12 solve
842161320

real    0m19.468s
user    0m19.450s
sys 0m0.010s

जो सी कोड की तुलना में बहुत बुरा नहीं है:

$ time ./a.out 
842161320

real    0m12.755s
user    0m12.730s
sys 0m0.020s

यह मानते हुए कि एर्लैंग संख्यात्मक कोड लिखने की दिशा में बिल्कुल भी सक्षम नहीं है, इस तरह के कार्यक्रम पर सी की तुलना में केवल 50% धीमा होना बहुत अच्छा है।

अंत में, अपने प्रश्नों के बारे में:

प्रश्न 1: क्या मनमाना लंबाई पूर्णांक का उपयोग करने के कारण एरंग, पायथन और हैस्केल ढीली गति करते हैं या जब तक मान MAXINT से कम नहीं होते हैं?

हाँ, कुछ हद तक। एर्लैंग में, "रैप-अराउंड के साथ 32/64-बिट अंकगणित का उपयोग करें" कहने का कोई तरीका नहीं है, इसलिए जब तक कंपाइलर आपके पूर्णांक पर कुछ सीमाएं साबित नहीं कर सकता (और यह आमतौर पर नहीं हो सकता), यह देखने के लिए सभी अभिकलन की जांच करनी चाहिए यदि वे एक ही टैग किए गए शब्द में फिट हो सकते हैं या यदि उन्हें ढेर-आवंटित बाइनम में बदलना है। यहां तक ​​कि अगर रनटाइम के दौरान कभी भी कोई बोली नहीं लगाई जाती है, तो इन चेकों का प्रदर्शन करना होगा। दूसरी ओर, इसका मतलब है कि आप जानते हैं कि एक अप्रत्याशित पूर्णांक आवरण के कारण एल्गोरिथ्म कभी भी विफल नहीं होगा यदि आप अचानक इसे पहले से बड़े इनपुट देते हैं।

प्रश्न 4: क्या मेरे कार्यात्मक कार्यान्वयन एलसीओ को अनुमति देते हैं और इसलिए कॉल स्टैक पर अनावश्यक फ्रेम जोड़ने से बचते हैं?

हां, अंतिम कॉल अनुकूलन के संबंध में आपका Erlang कोड सही है।


2
मैं आपसे सहमत हुँ। यह बेंचमार्क विशेष रूप से Erlang के लिए कई कारणों से सटीक नहीं था
मुगया जोशुआ

156

पायथन अनुकूलन के संबंध में, PyPy (अपने कोड में शून्य परिवर्तन के साथ बहुत प्रभावशाली गति-अप के लिए) का उपयोग करने के अलावा, आप RPPthon-compliant संस्करण को संकलित करने के लिए Pythy के अनुवाद टूलचिन का उपयोग कर सकते हैं , या साइथन को एक्सटेंशन मॉड्यूल बनाने के लिए, दोनों का उपयोग कर सकते हैं। मेरे परीक्षण में C संस्करण की तुलना में तेज़ हैं, साइथन मॉड्यूल के साथ लगभग दोगुना तेज़ है । संदर्भ के लिए मैं C और PyPy बेंचमार्क परिणाम शामिल करता हूं:

सी (के साथ संकलित gcc -O3 -lm)

% time ./euler12-c 
842161320

./euler12-c  11.95s 
 user 0.00s 
 system 99% 
 cpu 11.959 total

PyPy 1.5

% time pypy euler12.py
842161320
pypy euler12.py  
16.44s user 
0.01s system 
99% cpu 16.449 total

RPython (नवीनतम PyPy संशोधन का उपयोग करके c2f583445aee)

% time ./euler12-rpython-c
842161320
./euler12-rpy-c  
10.54s user 0.00s 
system 99% 
cpu 10.540 total

साइथॉन 0.15

% time python euler12-cython.py
842161320
python euler12-cython.py  
6.27s user 0.00s 
system 99% 
cpu 6.274 total

RPython संस्करण में कुछ महत्वपूर्ण बदलाव हुए हैं। एक स्टैंडअलोन प्रोग्राम में अनुवाद करने के लिए आपको अपने को परिभाषित करने की आवश्यकता है target, जो इस मामले में mainफ़ंक्शन है। इसे स्वीकार करने की उम्मीद है sys.argvक्योंकि यह केवल तर्क है, और एक इंट वापस करने के लिए आवश्यक है। आप इसका अनुवाद Transl.py द्वारा कर सकते हैं, % translate.py euler12-rpython.pyजो C का अनुवाद करता है और इसे आपके लिए संकलित करता है।

# euler12-rpython.py

import math, sys

def factorCount(n):
    square = math.sqrt(n)
    isquare = int(square)
    count = -1 if isquare == square else 0
    for candidate in xrange(1, isquare + 1):
        if not n % candidate: count += 2
    return count

def main(argv):
    triangle = 1
    index = 1
    while factorCount(triangle) < 1001:
        index += 1
        triangle += index
    print triangle
    return 0

if __name__ == '__main__':
    main(sys.argv)

def target(*args):
    return main, None

साइथन संस्करण को एक विस्तार मॉड्यूल के रूप में फिर से लिखा गया था _euler12.pyx, जिसे मैं एक सामान्य अजगर फ़ाइल से आयात और कॉल करता हूं। _euler12.pyxअनिवार्य रूप से, अपने संस्करण के रूप में ही कुछ अतिरिक्त स्थिर प्रकार घोषणाओं के साथ है। Setup.py में एक्सटेंशन का उपयोग करने के लिए सामान्य बायलरप्लेट है, का उपयोग करके python setup.py build_ext --inplace

# _euler12.pyx
from libc.math cimport sqrt

cdef int factorCount(int n):
    cdef int candidate, isquare, count
    cdef double square
    square = sqrt(n)
    isquare = int(square)
    count = -1 if isquare == square else 0
    for candidate in range(1, isquare + 1):
        if not n % candidate: count += 2
    return count

cpdef main():
    cdef int triangle = 1, index = 1
    while factorCount(triangle) < 1001:
        index += 1
        triangle += index
    print triangle

# euler12-cython.py
import _euler12
_euler12.main()

# setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("_euler12", ["_euler12.pyx"])]

setup(
  name = 'Euler12-Cython',
  cmdclass = {'build_ext': build_ext},
  ext_modules = ext_modules
)

मुझे ईमानदारी से आरपीथॉन या साइथन के साथ बहुत कम अनुभव है, और परिणामों पर सुखद आश्चर्य हुआ। यदि आप CPython का उपयोग कर रहे हैं, तो साइथॉन एक्सटेंशन मॉड्यूल में कोड के अपने सीपीयू-गहन बिट्स को लिखना आपके प्रोग्राम को ऑप्टिमाइज़ करने का एक बहुत आसान तरीका लगता है।


6
मैं उत्सुक हूं, क्या सी संस्करण को सीपीथॉन के रूप में कम से कम तेज़ होने के लिए अनुकूलित किया जा सकता है?
नाम

4
@SargeBorsch कि साइथन संस्करण इतना तेज़ है, क्योंकि यह एक उच्च अनुकूलित सी स्रोत के लिए संकलित है, जिसका अर्थ है कि आप सुनिश्चित कर सकते हैं कि प्रदर्शन सी से बाहर हो जाए
एली कोरविगो

72

प्रश्न 3: क्या आप मुझे कुछ संकेत दे सकते हैं कि मैं कारकों को निर्धारित करने के तरीके को बदले बिना इन कार्यान्वयनों को कैसे अनुकूलित करूं? किसी भी तरह से अनुकूलन: अच्छे, तेज, भाषा के लिए अधिक "देशी"।

सी कार्यान्वयन उप-रूपी है (जैसा कि थॉमस एम। डुबिसन द्वारा संकेत दिया गया है), संस्करण 64-बिट पूर्णांक (यानी लंबे डेटाटाइप) का उपयोग करता है । मैं बाद में असेंबली लिस्टिंग की जाँच करूँगा, लेकिन एक शिक्षित अनुमान के साथ, संकलित कोड में कुछ मेमोरी एक्सेस चल रहे हैं, जो 64-बिट पूर्णांकों का उपयोग करते हुए काफी धीमी बनाते हैं। यह वह या जेनरेट किया गया कोड है (यह तथ्य है कि आप SSE रजिस्टर में 64-बिट इनट को फिट कर सकते हैं या एक डबल से 64-बिट पूर्णांक धीमा है)।

यहां संशोधित कोड है (बस इंट के साथ लंबे समय तक बदलें और मैं स्पष्ट रूप से इनबिल्ड कारक हूं, हालांकि मुझे नहीं लगता है कि यह gcc -O3 के साथ आवश्यक है):

#include <stdio.h>
#include <math.h>

static inline int factorCount(int n)
{
    double square = sqrt (n);
    int isquare = (int)square;
    int count = isquare == square ? -1 : 0;
    int candidate;
    for (candidate = 1; candidate <= isquare; candidate ++)
        if (0 == n % candidate) count += 2;
    return count;
}

int main ()
{
    int triangle = 1;
    int index = 1;
    while (factorCount (triangle) < 1001)
    {
        index++;
        triangle += index;
    }
    printf ("%d\n", triangle);
}

रनिंग + टाइमिंग यह देता है:

$ gcc -O3 -lm -o euler12 euler12.c; time ./euler12
842161320
./euler12  2.95s user 0.00s system 99% cpu 2.956 total

संदर्भ के लिए, पहले उत्तर में थॉमस द्वारा haskell कार्यान्वयन देता है:

$ ghc -O2 -fllvm -fforce-recomp euler12.hs; time ./euler12                                                                                      [9:40]
[1 of 1] Compiling Main             ( euler12.hs, euler12.o )
Linking euler12 ...
842161320
./euler12  9.43s user 0.13s system 99% cpu 9.602 total

निष्कर्ष: gcc से दूर कुछ भी नहीं, इसका एक महान संकलक, लेकिन gcc सामान्य रूप से तेज कोड उत्पन्न करता है।


22
बहुत अच्छा! तुलना के लिए, मेरी मशीन पर आपका सी समाधान चलता है, 2.5 secondsजबकि हास्केल कोड (Word32 में जाने, INLINE प्राग्मा को जोड़ने) के समान संशोधन के परिणामस्वरूप रनटाइम होता है 4.8 seconds। शायद कुछ किया जा सकता है (ट्रिवेली नहीं, ऐसा लगता है) - जीसीसी परिणाम निश्चित रूप से प्रभावशाली है।
थॉमस एम। डुबिसन

1
धन्यवाद! शायद सवाल वास्तविक भाषा के बजाय विभिन्न संकलक द्वारा संकलित आउटपुट की गति होना चाहिए। फिर से, इंटेल मैनुअल को बाहर निकालना और हाथ से अनुकूलन करना अभी भी एकमुश्त जीत जाएगा (बशर्ते आपके पास ज्ञान और समय हो (बहुत कुछ))।
राडवुल्फ

56

इस ब्लॉग पर एक नज़र डालें । पिछले एक साल में या तो उसने हास्केल और पायथन में प्रोजेक्ट यूलर की कुछ समस्याएं की हैं, और वह आमतौर पर हास्केल को बहुत तेज पाया है । मुझे लगता है कि उन भाषाओं के बीच यह आपके प्रवाह और कोडिंग शैली के साथ अधिक है।

जब यह पायथन गति की बात आती है, तो आप गलत कार्यान्वयन का उपयोग कर रहे हैं! PyPy का प्रयास करें , और इस तरह की चीजों के लिए आप इसे बहुत अधिक, बहुत तेज पाएंगे।


32

हास्केल पैकेज के कुछ फ़ंक्शंस का उपयोग करके आपके हास्केल कार्यान्वयन को बहुत कम किया जा सकता है। इस मामले में मैंने प्राइम्स का इस्तेमाल किया, जो कि सिर्फ 'कैबेल इन्स्टॉल प्राइम्स' के साथ स्थापित है;)

import Data.Numbers.Primes
import Data.List

triangleNumbers = scanl1 (+) [1..]
nDivisors n = product $ map ((+1) . length) (group (primeFactors n))
answer = head $ filter ((> 500) . nDivisors) triangleNumbers

main :: IO ()
main = putStrLn $ "First triangle number to have over 500 divisors: " ++ (show answer)

समय:

आपका मूल कार्यक्रम:

PS> measure-command { bin\012_slow.exe }

TotalSeconds      : 16.3807409
TotalMilliseconds : 16380.7409

बेहतर क्रियान्वयन

PS> measure-command { bin\012.exe }

TotalSeconds      : 0.0383436
TotalMilliseconds : 38.3436

जैसा कि आप देख सकते हैं, यह एक 38 मिलीसेकंड में उसी मशीन पर चलता है, जहां आपका भाग 16 सेकंड में है :)

संकलन आदेश:

ghc -O2 012.hs -o bin\012.exe
ghc -O2 012_slow.hs -o bin\012_slow.exe

5
पिछली बार मैंने हास्केल "प्राइम्स" की जाँच की थी प्री-कॉम्प्रिम्ड प्राइम्स की एक बहुत बड़ी सूची थी - कोई संगणना नहीं, बस लुकअप। तो हाँ, बेशक यह तेज़ होगा, लेकिन यह आपको हास्केल में अपराधों को प्राप्त करने की कम्प्यूटेशनल गति के बारे में कुछ नहीं बताता है ।
zxq9

21
@ zxq9 क्या आप मुझे इंगित कर सकते हैं कि प्राइम पैकेज स्रोत ( hackage.haskell.org/package/primes-0.2.1.0/docs/src/… ) में प्राइम नंबर की सूची कहाँ स्थित है?
फ्रेजर

4
जबकि स्रोत से पता चलता है कि अपराध पूर्ववर्ती नहीं हैं, यह गति पूरी तरह से पागल है, सी संस्करण की तुलना में मीलों तेज है, इसलिए बिल्ली क्या चल रही है?
अर्धविराम

1
@ सिमिसोलोन संस्मरण। इस मामले में मुझे लगता है कि हास्केल ने सभी अपराधों को रनटाइम पर याद किया, इसलिए उन्हें प्रत्येक पुनरावृत्ति की आवश्यकता नहीं है।
हौलेथ

5
यह १०००
डिवाइडर है

29

सिर्फ मनोरंजन के लिए। निम्नलिखित एक अधिक 'देशी' हास्केल कार्यान्वयन है:

import Control.Applicative
import Control.Monad
import Data.Either
import Math.NumberTheory.Powers.Squares

isInt :: RealFrac c => c -> Bool
isInt = (==) <$> id <*> fromInteger . round

intSqrt :: (Integral a) => a -> Int
--intSqrt = fromIntegral . floor . sqrt . fromIntegral
intSqrt = fromIntegral . integerSquareRoot'

factorize :: Int -> [Int]
factorize 1 = []
factorize n = first : factorize (quot n first)
  where first = (!! 0) $ [a | a <- [2..intSqrt n], rem n a == 0] ++ [n]

factorize2 :: Int -> [(Int,Int)]
factorize2 = foldl (\ls@((val,freq):xs) y -> if val == y then (val,freq+1):xs else (y,1):ls) [(0,0)] . factorize

numDivisors :: Int -> Int
numDivisors = foldl (\acc (_,y) -> acc * (y+1)) 1 <$> factorize2

nextTriangleNumber :: (Int,Int) -> (Int,Int)
nextTriangleNumber (n,acc) = (n+1,acc+n+1)

forward :: Int -> (Int, Int) -> Either (Int, Int) (Int, Int)
forward k val@(n,acc) = if numDivisors acc > k then Left val else Right (nextTriangleNumber val)

problem12 :: Int -> (Int, Int)
problem12 n = (!!0) . lefts . scanl (>>=) (forward n (1,1)) . repeat . forward $ n

main = do
  let (n,val) = problem12 1000
  print val

उपयोग करते हुए ghc -O3, यह मेरी मशीन (0.73GHz Core i7) पर लगातार 0.55-0.58 सेकंड में चलता है।

C संस्करण के लिए एक अधिक कुशल कारक फ़ंक्शन:

int factorCount (int n)
{
  int count = 1;
  int candidate,tmpCount;
  while (n % 2 == 0) {
    count++;
    n /= 2;
  }
    for (candidate = 3; candidate < n && candidate * candidate < n; candidate += 2)
    if (n % candidate == 0) {
      tmpCount = 1;
      do {
        tmpCount++;
        n /= candidate;
      } while (n % candidate == 0);
       count*=tmpCount;
      }
  if (n > 1)
    count *= 2;
  return count;
}

मुख्य रूप gcc -O3 -lmसे चींटियों में लोंगों को बदलना, उपयोग करना , यह लगातार 0.31-0.35 सेकंड में चलता है।

दोनों को तेजी से चलाने के लिए बनाया जा सकता है यदि आप इस तथ्य का लाभ उठाते हैं कि nth त्रिकोण संख्या = n * (n + 1) / 2, और n और (n + 1) में पूरी तरह से प्रधान कारक हैं, तो कारकों की संख्या प्रत्येक आधे को पूरे के कारकों की संख्या ज्ञात करने के लिए गुणा किया जा सकता है। निम्नलिखित:

int main ()
{
  int triangle = 0,count1,count2 = 1;
  do {
    count1 = count2;
    count2 = ++triangle % 2 == 0 ? factorCount(triangle+1) : factorCount((triangle+1)/2);
  } while (count1*count2 < 1001);
  printf ("%lld\n", ((long long)triangle)*(triangle+1)/2);
}

सी कोड चलाने के समय को 0.17-0.19 सेकंड तक कम कर देगा, और यह बहुत बड़ी खोजों को संभाल सकता है - 10000 से अधिक कारकों को मेरी मशीन पर लगभग 43 सेकंड लगते हैं। मैं रुचि रखने वाले पाठक के लिए एक समान हैस्केल स्पीडअप छोड़ता हूं।


3
बस तुलना के लिए: मूल सी संस्करण: 9.1690, थाउमकिड का संस्करण: 0.1060 86x सुधार।
थानोस

वाह। एक बार जब आप हीन प्रकार से बचते हैं तो हस्केल बहुत अच्छा करता है
पीयूष कटारिया

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

c संस्करण 0.11 s पर इंटेल खोपड़ी कैनियन
कोडेशॉट

13
प्रश्न 1: क्या मनमाना लंबाई पूर्णांक का उपयोग करने के कारण एरंग, पायथन और हैस्केल ढीली गति करते हैं या जब तक मान MAXINT से कम नहीं होते हैं?

इसकी संभावना नहीं है। मैं एरलांग और हास्केल के बारे में बहुत कुछ नहीं कह सकता (अच्छी तरह से, शायद नीचे हास्केल के बारे में थोड़ा सा), लेकिन मैं पायथन में कई अन्य बाधाओं को इंगित कर सकता हूं। हर बार जब कार्यक्रम पायथन में कुछ मूल्यों के साथ एक ऑपरेशन को निष्पादित करने की कोशिश करता है, तो यह सत्यापित करना चाहिए कि क्या मान उचित प्रकार से हैं, और इसमें थोड़ा समय लगता है। आपका factorCountकार्य केवल range (1, isquare + 1)विभिन्न समयों के साथ एक सूची आवंटित करता है, और रनटाइम, malloc-स्टाइल किया गया मेमोरी आवंटन एक सीमा पर पुनरावृत्ति की तुलना में धीमा है जैसा कि आप सी। में करते हैं, विशेष रूप से, factorCount()कई बार कहा जाता है और इसलिए बहुत सारी सूचियां आवंटित करता है। इसके अलावा, हमें यह नहीं भूलना चाहिए कि पायथन की व्याख्या की गई है और सीपीथॉन दुभाषिया का अनुकूलित होने पर कोई बड़ा ध्यान नहीं है।

संपादित करें : ओह, ठीक है, मैं ध्यान देता हूं कि आप पायथन 3 का उपयोग कर रहे हैं, इसलिए range()एक सूची नहीं है, लेकिन एक जनरेटर। इस मामले में, सूचियों को आवंटित करने के बारे में मेरी बात आधी-अधूरी है: फ़ंक्शन सिर्फ rangeवस्तुओं को आवंटित करता है, जो कि अक्षम हैं, लेकिन बहुत सारे आइटमों के साथ सूची आवंटित करने के रूप में अक्षम नहीं हैं।

प्रश्न 2: हैसेल इतना धीमा क्यों है? क्या कोई कंपाइलर ध्वज है जो ब्रेक को बंद कर देता है या यह मेरा कार्यान्वयन है? (उत्तरार्द्ध काफी संभावित है क्योंकि हस्केल मेरे लिए सात मुहरों वाली एक पुस्तक है।)

क्या आप Hugs का उपयोग कर रहे हैं ? हग्स एक काफी धीमा दुभाषिया है। यदि आप इसका उपयोग कर रहे हैं, तो शायद आप जीएचसी के साथ एक बेहतर समय प्राप्त कर सकते हैं - लेकिन मैं केवल हाइपोटिसिस कर रहा हूं, एक अच्छा हास्केल कंपाइलर जिस तरह का सामान करता है वह हुड के नीचे बहुत आकर्षक और मेरी समझ से परे है :)

प्रश्न 3: क्या आप मुझे कुछ संकेत दे सकते हैं कि मैं कारकों को निर्धारित करने के तरीके को बदले बिना इन कार्यान्वयनों को कैसे अनुकूलित करूं? किसी भी तरह से अनुकूलन: अच्छे, तेज, भाषा के लिए अधिक "देशी"।

मैं कहता हूँ कि आप एक निराला खेल खेल रहे हैं। विभिन्न भाषाओं को जानने का सबसे अच्छा हिस्सा उन्हें सबसे अलग तरीके से उपयोग करना है :) लेकिन मैं पचाता हूं, मेरे पास इस बिंदु पर कोई सिफारिश नहीं है। क्षमा करें, मुझे आशा है कि कोई इस मामले में आपकी मदद कर सकता है :)

प्रश्न 4: क्या मेरे कार्यात्मक कार्यान्वयन एलसीओ को अनुमति देते हैं और इसलिए कॉल स्टैक पर अनावश्यक फ्रेम जोड़ने से बचते हैं?

जहाँ तक मुझे याद है, आपको केवल यह सुनिश्चित करने की आवश्यकता है कि मान वापस करने से पहले आपका पुनरावर्ती कॉल अंतिम आदेश है। दूसरे शब्दों में, नीचे दिया गया एक फ़ंक्शन इस तरह के अनुकूलन का उपयोग कर सकता है:

def factorial(n, acc=1):
    if n > 1:
        acc = acc * n
        n = n - 1
        return factorial(n, acc)
    else:
        return acc

हालाँकि, यदि आपका फ़ंक्शन नीचे वाले के समान था, तो आपके पास ऐसा अनुकूलन नहीं होगा, क्योंकि पुनरावर्ती कॉल के बाद एक ऑपरेशन (गुणा) होता है:

def factorial2(n):
    if n > 1:
        f = factorial2(n-1)
        return f*n
    else:
        return 1

मैंने कुछ स्थानीय चरों में परिचालन को अलग कर दिया ताकि यह स्पष्ट हो सके कि कौन से संचालन निष्पादित किए जाते हैं। हालाँकि, सबसे आम तौर पर इन कार्यों को नीचे के रूप में देखना है, लेकिन वे उस बिंदु के बराबर हैं जो मैं कर रहा हूं:

def factorial(n, acc=1):
    if n > 1:
        return factorial(n-1, acc*n)
    else:
        return acc

def factorial2(n):
    if n > 1:
        return n*factorial(n-1)
    else:
        return 1

ध्यान दें कि यह संकलक / दुभाषिया पर निर्भर है कि वह पूंछ पुनरावृत्ति करेगा या नहीं। उदाहरण के लिए, पायथन दुभाषिया ऐसा नहीं करता है अगर मुझे अच्छी तरह से याद है (मैंने अपने उदाहरण में केवल अपने धाराप्रवाह सिंटैक्स के कारण पायथन का इस्तेमाल किया)। वैसे भी, आप इस तरह के दो पैरामीटर (और पैरामीटर में से एक जैसे नाम के साथ भाज्य कार्यों के रूप में अजीब सामान पाते हैं acc, accumulatorआदि) अब आप जानते हैं कि लोगों को यह कर :)


@ हाइपरबोरस धन्यवाद! इसके अलावा, मैं वास्तव में आपके अगले सवालों के बारे में उत्सुक हूं। हालाँकि, मैं आपको चेतावनी देता हूं कि मेरा ज्ञान सीमित है, इसलिए मैं आपके हर सवाल का जवाब नहीं दे सका। इसकी भरपाई करने की कोशिश के लिए मैंने अपना उत्तर समुदाय विकि बना दिया ताकि लोग अधिक आसानी से इसके पूरक बन सकें।
Brandizzi

रेंज का उपयोग करने के बारे में। जब मैं कुछ समय लूप के साथ वेतन वृद्धि (सी के लूप के लिए नकल) के साथ प्रतिस्थापित करता हूं, तो निष्पादन समय वास्तव में दोगुना हो जाता है। मुझे लगता है कि जनरेटर काफी अनुकूलित हैं।
हाइपरबोरस

12

हास्केल के साथ, आपको वास्तव में पुनर्विचारों में स्पष्ट रूप से सोचने की आवश्यकता नहीं है।

factorCount number = foldr factorCount' 0 [1..isquare] -
                     (fromEnum $ square == fromIntegral isquare)
    where
      square = sqrt $ fromIntegral number
      isquare = floor square
      factorCount' candidate
        | number `rem` candidate == 0 = (2 +)
        | otherwise = id

triangles :: [Int]
triangles = scanl1 (+) [1,2..]

main = print . head $ dropWhile ((< 1001) . factorCount) triangles

उपरोक्त कोड में, मैंने सामान्य सूची संचालन के साथ @Thomas के उत्तर में स्पष्ट पुनरावर्ती प्रतिस्थापित किया है। पूंछ पुनरावृत्ति के बारे में चिंता किए बिना कोड अभी भी ठीक वैसा ही करता है। यह (~ चलाता 7.49s के बारे में) 6% @Thomas 'जवाब में संस्करण (~ की तुलना में धीमी 7.04s ,) GHC 7.6.2 के साथ अपने मशीन पर जबकि @Raedwulf से सी संस्करण ~ चलाता 3.15s । ऐसा लगता है कि जीएचसी में साल भर सुधार हुआ है।

पुनश्च। मुझे पता है कि यह एक पुराना सवाल है, और मैं इसे Google खोजों से ठोकर खाता हूं (मैं भूल गया था कि मैं क्या खोज रहा था, अब ...)। बस एलसीओ के बारे में सवाल पर टिप्पणी करना चाहता था और हास्केल के बारे में अपनी भावनाओं को सामान्य रूप से व्यक्त करना चाहता था। मैं शीर्ष उत्तर पर टिप्पणी करना चाहता था, लेकिन टिप्पणियाँ कोड ब्लॉक की अनुमति नहीं देती हैं।


9

सी संस्करण के लिए कुछ और संख्याएँ और स्पष्टीकरण। जाहिर है कि उन सभी वर्षों के दौरान किसी ने ऐसा नहीं किया। इस उत्तर को याद रखना ताकि यह सभी को देखने और सीखने के लिए शीर्ष पर मिल सके।

चरण एक: लेखक के कार्यक्रमों की बेंचमार्क

लैपटॉप विनिर्देशों:

  • CPU i3 M380 (931 MHz - अधिकतम बैटरी बचत मोड)
  • 4GB मेमोरी
  • विन 7 64 बिट्स
  • माइक्रोसॉफ्ट विजुअल स्टूडियो 2012 अल्टीमेट
  • 4.9.3 के साथ सिगविन
  • पायथन 2.7.10

आदेश:

compiling on VS x64 command prompt > `for /f %f in ('dir /b *.c') do cl /O2 /Ot /Ox %f -o %f_x64_vs2012.exe`
compiling on cygwin with gcc x64   > `for f in ./*.c; do gcc -m64 -O3 $f -o ${f}_x64_gcc.exe ; done`
time (unix tools) using cygwin > `for f in ./*.exe; do  echo "----------"; echo $f ; time $f ; done`

----------
$ time python ./original.py

real    2m17.748s
user    2m15.783s
sys     0m0.093s
----------
$ time ./original_x86_vs2012.exe

real    0m8.377s
user    0m0.015s
sys     0m0.000s
----------
$ time ./original_x64_vs2012.exe

real    0m8.408s
user    0m0.000s
sys     0m0.015s
----------
$ time ./original_x64_gcc.exe

real    0m20.951s
user    0m20.732s
sys     0m0.030s

फ़ाइलनाम हैं: integertype_architecture_compiler.exe

  • पूर्णांक अब के लिए मूल कार्यक्रम के समान है (उस पर अधिक बाद में)
  • कंपाइलर सेटिंग्स के आधार पर आर्किटेक्चर x86 या x64 है
  • संकलक gcc या vs2012 है

चरण दो: फिर से जांच, सुधार और बेंचमार्क

VS, gcc से 250% तेज है। दो संकलक को एक समान गति देनी चाहिए। जाहिर है, कोड या कंपाइलर विकल्पों में से कुछ गलत है। चलिए जांच करते हैं!

ब्याज का पहला बिंदु पूर्णांक प्रकार है। रूपांतरण महंगे हो सकते हैं और बेहतर कोड जनरेशन और ऑप्टिमाइज़ेशन के लिए निरंतरता महत्वपूर्ण है। सभी पूर्णांक एक ही प्रकार के होने चाहिए।

यह अभी intऔर का एक मिश्रित गड़बड़ है long। हम इसमें सुधार करने जा रहे हैं। किस प्रकार का उपयोग करें? सबसे तेज़। उन्हें बेंचमार्क होगा!

----------
$ time ./int_x86_vs2012.exe

real    0m8.440s
user    0m0.016s
sys     0m0.015s
----------
$ time ./int_x64_vs2012.exe

real    0m8.408s
user    0m0.016s
sys     0m0.015s
----------
$ time ./int32_x86_vs2012.exe

real    0m8.408s
user    0m0.000s
sys     0m0.015s
----------
$ time ./int32_x64_vs2012.exe

real    0m8.362s
user    0m0.000s
sys     0m0.015s
----------
$ time ./int64_x86_vs2012.exe

real    0m18.112s
user    0m0.000s
sys     0m0.015s
----------
$ time ./int64_x64_vs2012.exe

real    0m18.611s
user    0m0.000s
sys     0m0.015s
----------
$ time ./long_x86_vs2012.exe

real    0m8.393s
user    0m0.015s
sys     0m0.000s
----------
$ time ./long_x64_vs2012.exe

real    0m8.440s
user    0m0.000s
sys     0m0.015s
----------
$ time ./uint32_x86_vs2012.exe

real    0m8.362s
user    0m0.000s
sys     0m0.015s
----------
$ time ./uint32_x64_vs2012.exe

real    0m8.393s
user    0m0.015s
sys     0m0.015s
----------
$ time ./uint64_x86_vs2012.exe

real    0m15.428s
user    0m0.000s
sys     0m0.015s
----------
$ time ./uint64_x64_vs2012.exe

real    0m15.725s
user    0m0.015s
sys     0m0.015s
----------
$ time ./int_x64_gcc.exe

real    0m8.531s
user    0m8.329s
sys     0m0.015s
----------
$ time ./int32_x64_gcc.exe

real    0m8.471s
user    0m8.345s
sys     0m0.000s
----------
$ time ./int64_x64_gcc.exe

real    0m20.264s
user    0m20.186s
sys     0m0.015s
----------
$ time ./long_x64_gcc.exe

real    0m20.935s
user    0m20.809s
sys     0m0.015s
----------
$ time ./uint32_x64_gcc.exe

real    0m8.393s
user    0m8.346s
sys     0m0.015s
----------
$ time ./uint64_x64_gcc.exe

real    0m16.973s
user    0m16.879s
sys     0m0.030s

पूर्णांक प्रकार int long int32_t uint32_t int64_tऔर uint64_tहैं#include <stdint.h>

C में पूर्णांक प्रकार के बहुत सारे हैं, प्लस के साथ खेलने के लिए कुछ हस्ताक्षरित / अहस्ताक्षरित हैं, साथ ही x86 या x64 के रूप में संकलन करने का विकल्प (वास्तविक पूर्णांक आकार के साथ भ्रमित नहीं होना)। यह ^ ^ को संकलित करने और चलाने के लिए बहुत सारे संस्करण हैं

चरण तीन: संख्याओं को समझना

निश्चित निष्कर्ष:

  • 32 बिट पूर्णांक 64 बिट समकक्षों की तुलना में ~ 200% तेज हैं
  • अहस्ताक्षरित 64 बिट्स पूर्णांक हस्ताक्षरित 64 बिट्स की तुलना में 25% तेज हैं (दुर्भाग्य से, मेरे पास इसके लिए कोई स्पष्टीकरण नहीं है)

ट्रिक प्रश्न: "C में int और long का आकार क्या है?"
सही उत्तर है: C में int और long का आकार अच्छी तरह से परिभाषित नहीं है!

सी कल्पना से:

int कम से कम 32 बिट
लंबा है, कम से कम एक int है

Gcc मैन पेज से (-m32 और -m64 झंडे):

32-बिट वातावरण 32 बिट्स के लिए इंट, लॉन्ग और पॉइंटर सेट करता है और कोड बनाता है जो किसी भी i386 सिस्टम पर चलता है।
64-बिट वातावरण 32 से लंबे बिट्स और 64 बिट्स के लिए सूचक और AMD के x86-64 आर्किटेक्चर के लिए कोड उत्पन्न करता है।

MSDN दस्तावेज़ीकरण (डेटा प्रकार रेंज) से https://msdn.microsoft.com/en-us/library/s3f49ktz%28v=vs.110%29.aspx :

int, 4 बाइट्स, जिसे हस्ताक्षरित
लंबे, 4 बाइट्स के रूप में भी जाना जाता है, को भी लंबे इंट के रूप में जाना जाता है और लंबे इंट को हस्ताक्षरित किया जाता है

यह जानने के लिए: सबक सीखा

  • 32 बिट पूर्णांक 64 बिट पूर्णांक से अधिक तेज़ हैं।

  • मानक पूर्णांक प्रकारों को सी या सी ++ में अच्छी तरह से परिभाषित नहीं किया गया है, वे संकलक और आर्किटेक्चर के आधार पर भिन्न होते हैं। जब आपको स्थिरता और पूर्वानुमान की आवश्यकता होती है, तो uint32_tपूर्णांक परिवार का उपयोग करें #include <stdint.h>

  • गति के मुद्दों को हल किया। अन्य सभी भाषाएँ सैकड़ों प्रतिशत पीछे हैं, C & C ++ फिर से जीतेंगे! वे हमेशा करते हैं। अगला सुधार OpenMP: D का उपयोग करके किया जाएगा


जिज्ञासा से बाहर, इंटेल कंपाइलर कैसे करते हैं? वे आमतौर पर संख्यात्मक कोड के अनुकूलन में वास्तव में अच्छे हैं।
kirbyfan64sos

आपको सी रेफ़रेंस में यह कहते हुए एक संदर्भ कहाँ मिलता है कि "इंट कम से कम 32 बिट्स है"? केवल गारंटी देता है मैं के बारे में पता की न्यूनतम परिमाण हैं INT_MINऔर INT_MAX(-32,767 और 32767 है, जो व्यावहारिक रूप से एक आवश्यकता है कि थोपना intकम से कम 16 बिट हो)। longकम से कम एक के रूप में बड़ा होना आवश्यक है int, और सीमा आवश्यकताओं का मतलब longकम से कम 32 बिट्स है।
शैडो रेंजर

आप सही प्रतीत होते हैं। stackoverflow.com/questions/1231147/is-int-in-c-always-32-bit
user5994461

8

आपके Erlang कार्यान्वयन को देखते हुए। टाइमिंग में पूरी वर्चुअल मशीन का स्टार्ट अप, आपके प्रोग्राम को चलाने और वर्चुअल मशीन को बंद करना शामिल है। यकीन है कि erlang vm को स्थापित करने और रोकने में कुछ समय लगता है।

यदि समय एरलांग वर्चुअल मशीन के भीतर ही किया जाता है, तो परिणाम भिन्न होंगे क्योंकि उस मामले में हमारे पास केवल कार्यक्रम के लिए वास्तविक समय होगा। अन्यथा, मेरा मानना ​​है कि एर्लांग वांग प्लस को शुरू करने और लोड करने की प्रक्रिया द्वारा लिया गया कुल समय (जैसा कि आप इसे अपने कार्यक्रम में डालते हैं) सभी कुल समय में शामिल हैं, जिस पद्धति का आप समय के लिए उपयोग कर रहे हैं कार्यक्रम आउटपुट है। एरलैंग टाइमिंग का उपयोग करने पर विचार करें जिसका उपयोग हम तब करते हैं जब हम वर्चुअल मशीन के भीतर ही अपने कार्यक्रमों को समय देना चाहते हैं timer:tc/1 or timer:tc/2 or timer:tc/3। इस तरह, एरलैंग के परिणाम वर्चुअल मशीन को शुरू करने और रोकने / मारने / रोकने के लिए लगने वाले समय को बाहर कर देंगे। यह मेरा तर्क है, इसके बारे में सोचें, और फिर अपनी बेंच मार्क की कोशिश करें।

मैं वास्तव में सुझाव देता हूं कि हम सटीक मान प्राप्त करने के लिए उन भाषाओं के रनटाइम के भीतर कार्यक्रम (उन भाषाओं के लिए जो एक रनटाइम है) के समय का प्रयास करें। उदाहरण के लिए C में एर्लांग, पाइथन और हास्केल (98% इस के बारे में निश्चित है - i स्टैंड करेक्शन) के रूप में रनटाइम सिस्टम शुरू करने और बंद करने का कोई ओवरहेड नहीं है। इसलिए (इस तर्क के आधार पर) मैं यह कहकर निष्कर्ष निकालता हूं कि यह बेंचमार्क किसी रन सिस्टम के शीर्ष पर चलने वाली भाषाओं के लिए सटीक / निष्पक्ष नहीं है। इन परिवर्तनों के साथ फिर से करें।

संपादित करें: इसके अलावा, भले ही सभी भाषाओं में रनटाइम सिस्टम था, प्रत्येक को शुरू करने और इसे रोकने का ओवरहेड अलग होगा। इसलिए मैं सुझाव देता हूं कि हम रनटाइम सिस्टम के भीतर से (उन भाषाओं के लिए जिसके लिए यह लागू होता है)। Erlang VM को स्टार्ट अप में काफी अधिक बढ़त हासिल करने के लिए जाना जाता है!


मैं अपनी पोस्ट में इसका उल्लेख करना भूल गया, लेकिन मैंने सिस्टम को शुरू करने में लगने वाले समय को मापा (erl -noshell -s erlang halt) - मेरी मशीन पर लगभग 0.1 सेकंड। यह कार्यक्रम के रन समय (लगभग 10 सेकंड) की तुलना में काफी छोटा है जिसके बारे में यह बताने लायक नहीं है।
रिचर्ड 23

आपकी मशीन पर! हम नहीं जानते कि आप सन फायर सर्वर पर काम कर रहे हैं या नहीं! चूँकि समय मशीन के चश्मे के समानुपातिक होता है, इसलिए इसे ध्यान में रखा जाना चाहिए।
मुजैया जोशुआ

2
@ रिचर्ड ने कहीं भी उल्लेख नहीं किया कि एर्लैंग तेज है :) इसके अलग-अलग लक्ष्य हैं, गति नहीं!
अपवाद

7

प्रश्न 1: क्या एरलांग, पायथन और हास्केल, मनमाने ढंग से लंबाई पूर्णांक का उपयोग करने के कारण गति खो देते हैं या क्या वे तब तक नहीं होते हैं जब तक मान MAXINT से कम होते हैं?

प्रश्न एक Erlang के लिए नकारात्मक में उत्तर दिया जा सकता है। अंतिम प्रश्न का उत्तर एर्लैंग का उपयोग करके उचित रूप में दिया गया है:

http://bredsaal.dk/learning-erlang-using-projecteuler-net

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

यह Erlang मॉड्यूल लगभग 5 सेकंड में एक सस्ते नेटबुक पर निष्पादित होता है ... यह erlang में नेटवर्क थ्रेड्स मॉडल का उपयोग करता है और, जैसे कि यह दर्शाता है कि इवेंट मॉडल का लाभ कैसे उठाया जाए। इसे कई नोड्स पर वितरित किया जा सकता है। और यह तेज है। मेरा कोड नहीं।

-module(p12dist).  
-author("Jannich Brendle, jannich@bredsaal.dk, http://blog.bredsaal.dk").  
-compile(export_all).

server() ->  
  server(1).

server(Number) ->  
  receive {getwork, Worker_PID} -> Worker_PID ! {work,Number,Number+100},  
  server(Number+101);  
  {result,T} -> io:format("The result is: \~w.\~n", [T]);  
  _ -> server(Number)  
  end.

worker(Server_PID) ->  
  Server_PID ! {getwork, self()},  
  receive {work,Start,End} -> solve(Start,End,Server_PID)  
  end,  
  worker(Server_PID).

start() ->  
  Server_PID = spawn(p12dist, server, []),  
  spawn(p12dist, worker, [Server_PID]),  
  spawn(p12dist, worker, [Server_PID]),  
  spawn(p12dist, worker, [Server_PID]),  
  spawn(p12dist, worker, [Server_PID]).

solve(N,End,_) when N =:= End -> no_solution;

solve(N,End,Server_PID) ->  
  T=round(N*(N+1)/2),
  case (divisor(T,round(math:sqrt(T))) > 500) of  
    true ->  
      Server_PID ! {result,T};  
    false ->  
      solve(N+1,End,Server_PID)  
  end.

divisors(N) ->  
  divisor(N,round(math:sqrt(N))).

divisor(_,0) -> 1;  
divisor(N,I) ->  
  case (N rem I) =:= 0 of  
  true ->  
    2+divisor(N,I-1);  
  false ->  
    divisor(N,I-1)  
  end.

नीचे परीक्षण एक: Intel (R) एटम (TM) CPU N270 @ 1.60GHz पर हुआ

~$ time erl -noshell -s p12dist start

The result is: 76576500.

^C

BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution
a

real    0m5.510s
user    0m5.836s
sys 0m0.152s

मूल्य को 1000 तक बढ़ाने से नीचे सही परिणाम प्राप्त नहीं होता है। ऊपर के साथ> 500 के रूप में, नवीनतम परीक्षण: IntelCore2 CPU 6600 @ 2.40GHz असली 0m2.370s में कॉम्प्लेक्स
मार्क वासिम

आपका परिणाम: 76576500 अन्य सभी: 842161320 चिकित्सा आपके परिणाम के साथ कुछ गलत है
davidDavidson

चूंकि मैं कुछ अन्य यूलर समस्याओं के बारे में था, इसलिए मैंने अपना परिणाम देखा। Projecteuler.net/problem=12 का जवाब है 76576500 इसके बारे में कोई सवाल नहीं। मुझे पता है कि यह अजीब लगता है, लेकिन मैंने अभी जाँच की है।
मार्क वासिम

तुलना के लिए मुझे मार्क के कोड के साथ Erlang 19 का उपयोग करते हुए मूल सी संस्करण के साथ 9.03 मिलता है, मुझे 5.406, 167.0366% तेजी से मिलता है।
थानोस

5

C ++ 11, <20ms मेरे लिए - इसे यहाँ चलाएँ

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

यह उत्तर मुख्य रूप से लोगों को आपके प्रश्न / अन्य उत्तर में कोड का मूल्यांकन करने में सहायता करने के लिए संदर्भ प्रदान करने के लिए है।

यह कोड केवल (बदसूरत) अनुकूलन के एक जोड़े का उपयोग करता है, इस पर आधारित भाषा के लिए असंबंधित, पर आधारित:

  1. हर ट्रिंगल नंबर फॉर्म n (n + 1) / 2 का होता है
  2. n और n + 1 कोप्राइम हैं
  3. भाजक की संख्या एक गुणक कार्य है

#include <iostream>
#include <cmath>
#include <tuple>
#include <chrono>

using namespace std;

// Calculates the divisors of an integer by determining its prime factorisation.

int get_divisors(long long n)
{
    int divisors_count = 1;

    for(long long i = 2;
        i <= sqrt(n);
        /* empty */)
    {
        int divisions = 0;
        while(n % i == 0)
        {
            n /= i;
            divisions++;
        }

        divisors_count *= (divisions + 1);

        //here, we try to iterate more efficiently by skipping
        //obvious non-primes like 4, 6, etc
        if(i == 2)
            i++;
        else
            i += 2;
    }

    if(n != 1) //n is a prime
        return divisors_count * 2;
    else
        return divisors_count;
}

long long euler12()
{
    //n and n + 1
    long long n, n_p_1;

    n = 1; n_p_1 = 2;

    // divisors_x will store either the divisors of x or x/2
    // (the later iff x is divisible by two)
    long long divisors_n = 1;
    long long divisors_n_p_1 = 2;

    for(;;)
    {
        /* This loop has been unwound, so two iterations are completed at a time
         * n and n + 1 have no prime factors in common and therefore we can
         * calculate their divisors separately
         */

        long long total_divisors;                 //the divisors of the triangle number
                                                  // n(n+1)/2

        //the first (unwound) iteration

        divisors_n_p_1 = get_divisors(n_p_1 / 2); //here n+1 is even and we

        total_divisors =
                  divisors_n
                * divisors_n_p_1;

        if(total_divisors > 1000)
            break;

        //move n and n+1 forward
        n = n_p_1;
        n_p_1 = n + 1;

        //fix the divisors
        divisors_n = divisors_n_p_1;
        divisors_n_p_1 = get_divisors(n_p_1);   //n_p_1 is now odd!

        //now the second (unwound) iteration

        total_divisors =
                  divisors_n
                * divisors_n_p_1;

        if(total_divisors > 1000)
            break;

        //move n and n+1 forward
        n = n_p_1;
        n_p_1 = n + 1;

        //fix the divisors
        divisors_n = divisors_n_p_1;
        divisors_n_p_1 = get_divisors(n_p_1 / 2);   //n_p_1 is now even!
    }

    return (n * n_p_1) / 2;
}

int main()
{
    for(int i = 0; i < 1000; i++)
    {
        using namespace std::chrono;
        auto start = high_resolution_clock::now();
        auto result = euler12();
        auto end = high_resolution_clock::now();

        double time_elapsed = duration_cast<milliseconds>(end - start).count();

        cout << result << " " << time_elapsed << '\n';
    }
    return 0;
}

यह मेरे डेस्कटॉप के लिए औसतन लगभग 19ms और मेरे लैपटॉप के लिए 80ms है, जो मैंने यहां देखी गई अधिकांश कोड से बहुत दूर रोई है। और इसमें कोई संदेह नहीं है, कई अनुकूलन अभी भी उपलब्ध हैं।


7
यह स्पष्ट रूप से नहीं है कि पूछने वाले ने क्या अनुरोध किया, "मैंने वास्तव में चार भाषाओं में समान एल्गोरिथ्म को लागू करने का प्रयास किया"। आपके समान कई हटाए गए उत्तरों में से एक पर एक टिप्पणी उद्धृत करने के लिए "यह बहुत स्पष्ट है कि आप भाषा की परवाह किए बिना बेहतर एल्गोरिदम के साथ तेज गति प्राप्त कर सकते हैं।"
थॉमस एम। डुबिसन

2
@ ThomasM.DuBuisson। मैं यही कर रहा हूं। प्रश्न \ उत्तर का भारी अर्थ है कि एल्गोरिदमिक गति अप महत्वपूर्ण हैं (और निश्चित रूप से ओपी उनके लिए नहीं पूछ रहा है), लेकिन कोई स्पष्ट उदाहरण नहीं है। मुझे लगता है कि यह उत्तर - जो बिल्कुल भारी अनुकूलित कोड नहीं है - अपने जैसे किसी के लिए थोड़ा उपयोगी संदर्भ प्रदान करता है, जो आश्चर्यचकित था कि ओपी कोड कितना धीमा / तेज़ था।
user3125280

जीसीसी भी बहुत से पैटर्न की पूर्व-गणना कर सकता है। int a = 0; for (int i = 0; i <10000000; ++ i) {a + = i;} का संकलन समय पर किया जाएगा, इसलिए रनटाइम पर <1ms लें। यह भी मायने रखता है
आर्थर

@Thomas: मुझे user3125280 से सहमत होना चाहिए - भाषाओं की तुलना इस बात से की जानी चाहिए कि कैसे वे कुछ स्मार्ट करने की बजाय किराया लेते हैं कि कैसे वे एक वास्तविक प्रोग्रामिंग भाषा को गूंगा बनाने में विफल रहते हैं। स्मार्ट एल्गोरिदम आमतौर पर लचीलेपन की तुलना में सूक्ष्म क्षमता के बारे में कम देखभाल करते हैं, चीजों को तार करने की क्षमता (उन्हें गठबंधन) और बुनियादी ढाँचा। मुद्दा इतना नहीं है कि किसी को 20 एमएस या 50 एमएस मिलता है, यह 8 सेकंड या 8 मिनट नहीं मिल रहा है।
दर्थजीका

5

कोशिश कर रहा है:

package main

import "fmt"
import "math"

func main() {
    var n, m, c int
    for i := 1; ; i++ {
        n, m, c = i * (i + 1) / 2, int(math.Sqrt(float64(n))), 0
        for f := 1; f < m; f++ {
            if n % f == 0 { c++ }
    }
    c *= 2
    if m * m == n { c ++ }
    if c > 1001 {
        fmt.Println(n)
        break
        }
    }
}

मुझे मिला:

मूल सी संस्करण: 9.1690 100%
जाना: 8.2520 111%

लेकिन का उपयोग कर:

package main

import (
    "math"
    "fmt"
 )

// Sieve of Eratosthenes
func PrimesBelow(limit int) []int {
    switch {
        case limit < 2:
            return []int{}
        case limit == 2:
            return []int{2}
    }
    sievebound := (limit - 1) / 2
    sieve := make([]bool, sievebound+1)
    crosslimit := int(math.Sqrt(float64(limit))-1) / 2
    for i := 1; i <= crosslimit; i++ {
        if !sieve[i] {
            for j := 2 * i * (i + 1); j <= sievebound; j += 2*i + 1 {
                sieve[j] = true
            }
        }
    }
    plimit := int(1.3*float64(limit)) / int(math.Log(float64(limit)))
    primes := make([]int, plimit)
    p := 1
    primes[0] = 2
    for i := 1; i <= sievebound; i++ {
        if !sieve[i] {
            primes[p] = 2*i + 1
            p++
            if p >= plimit {
                break
            }
        }
    }
    last := len(primes) - 1
    for i := last; i > 0; i-- {
        if primes[i] != 0 {
            break
        }
        last = i
    }
    return primes[0:last]
}



func main() {
    fmt.Println(p12())
}
// Requires PrimesBelow from utils.go
func p12() int {
    n, dn, cnt := 3, 2, 0
    primearray := PrimesBelow(1000000)
    for cnt <= 1001 {
        n++
        n1 := n
        if n1%2 == 0 {
            n1 /= 2
        }
        dn1 := 1
        for i := 0; i < len(primearray); i++ {
            if primearray[i]*primearray[i] > n1 {
                dn1 *= 2
                break
            }
            exponent := 1
            for n1%primearray[i] == 0 {
                exponent++
                n1 /= primearray[i]
            }
            if exponent > 1 {
                dn1 *= exponent
            }
            if n1 == 1 {
                break
            }
        }
        cnt = dn * dn1
        dn = dn1
    }
    return n * (n - 1) / 2
}

मुझे मिला:

मूल सी संस्करण: 9.1690 100%
thaumkid का c संस्करण: 0.1060 8650%
पहला जाना संस्करण: 8.2520 111%
दूसरा जाना संस्करण: 0.0230 39865%

मैंने पायथन 3.6 और pypy3.3-5.5- अल्फा की भी कोशिश की:

मूल c संस्करण: 8.629 100%
thaumkid का c संस्करण: 0.109 7916%
Python3.6: 54.795 16%
pypy3.3-5.5- अल्फा: 13.291 65%

और फिर निम्नलिखित कोड के साथ मुझे मिला:

मूल c संस्करण: 8.629 100%
thaumkid का c संस्करण: 0.109 8650%
Python3.6: 1.489 580%
pypy3.3-5.5- अल्फा: 0.582 1483%

def D(N):
    if N == 1: return 1
    sqrtN = int(N ** 0.5)
    nf = 1
    for d in range(2, sqrtN + 1):
        if N % d == 0:
            nf = nf + 1
    return 2 * nf - (1 if sqrtN**2 == N else 0)

L = 1000
Dt, n = 0, 0

while Dt <= L:
    t = n * (n + 1) // 2
    Dt = D(n/2)*D(n+1) if n%2 == 0 else D(n)*D((n+1)/2)
    n = n + 1

print (t)

1

परिवर्तन: case (divisor(T,round(math:sqrt(T))) > 500) of

सेवा: case (divisor(T,round(math:sqrt(T))) > 1000) of

यह एर्लांग बहु-प्रक्रिया उदाहरण के लिए सही उत्तर का उत्पादन करेगा।


2
क्या इस उत्तर पर टिप्पणी के रूप में इसका इरादा था ? क्योंकि यह स्पष्ट नहीं है, और यह अपने आप में एक जवाब नहीं है।
शैडो रेंजर

1

मैंने यह धारणा बनाई कि यदि संख्या में छोटे कारक होते हैं तो कारकों की संख्या बड़ी होती है। इसलिए मैंने थाउमकिड के उत्कृष्ट एल्गोरिथ्म का उपयोग किया, लेकिन पहले कारक गणना के लिए एक सन्निकटन का उपयोग किया जो कभी बहुत छोटा नहीं था। यह काफी सरल है: 29 तक के प्रमुख कारकों की जाँच करें, फिर शेष संख्या की जाँच करें और कारकों की संख्या के लिए एक ऊपरी बाध्य गणना करें। कारकों की संख्या के लिए एक ऊपरी बाध्य गणना करने के लिए इसका उपयोग करें, और यदि यह संख्या पर्याप्त है, तो कारकों की सटीक संख्या की गणना करें।

नीचे दिए गए कोड को शुद्धता के लिए इस धारणा की आवश्यकता नहीं है, लेकिन तेज होने के लिए। यह काम करने लगता है; केवल 100,000 में से एक संख्या एक अनुमान देती है जो एक पूर्ण जांच की आवश्यकता के लिए पर्याप्त है।

यहाँ कोड है:

// Return at least the number of factors of n.
static uint64_t approxfactorcount (uint64_t n)
{
    uint64_t count = 1, add;

#define CHECK(d)                            \
    do {                                    \
        if (n % d == 0) {                   \
            add = count;                    \
            do { n /= d; count += add; }    \
            while (n % d == 0);             \
        }                                   \
    } while (0)

    CHECK ( 2); CHECK ( 3); CHECK ( 5); CHECK ( 7); CHECK (11); CHECK (13);
    CHECK (17); CHECK (19); CHECK (23); CHECK (29);
    if (n == 1) return count;
    if (n < 1ull * 31 * 31) return count * 2;
    if (n < 1ull * 31 * 31 * 37) return count * 4;
    if (n < 1ull * 31 * 31 * 37 * 37) return count * 8;
    if (n < 1ull * 31 * 31 * 37 * 37 * 41) return count * 16;
    if (n < 1ull * 31 * 31 * 37 * 37 * 41 * 43) return count * 32;
    if (n < 1ull * 31 * 31 * 37 * 37 * 41 * 43 * 47) return count * 64;
    if (n < 1ull * 31 * 31 * 37 * 37 * 41 * 43 * 47 * 53) return count * 128;
    if (n < 1ull * 31 * 31 * 37 * 37 * 41 * 43 * 47 * 53 * 59) return count * 256;
    if (n < 1ull * 31 * 31 * 37 * 37 * 41 * 43 * 47 * 53 * 59 * 61) return count * 512;
    if (n < 1ull * 31 * 31 * 37 * 37 * 41 * 43 * 47 * 53 * 59 * 61 * 67) return count * 1024;
    if (n < 1ull * 31 * 31 * 37 * 37 * 41 * 43 * 47 * 53 * 59 * 61 * 67 * 71) return count * 2048;
    if (n < 1ull * 31 * 31 * 37 * 37 * 41 * 43 * 47 * 53 * 59 * 61 * 67 * 71 * 73) return count * 4096;
    return count * 1000000;
}

// Return the number of factors of n.
static uint64_t factorcount (uint64_t n)
{
    uint64_t count = 1, add;

    CHECK (2); CHECK (3);

    uint64_t d = 5, inc = 2;
    for (; d*d <= n; d += inc, inc = (6 - inc))
        CHECK (d);

    if (n > 1) count *= 2; // n must be a prime number
    return count;
}

// Prints triangular numbers with record numbers of factors.
static void printrecordnumbers (uint64_t limit)
{
    uint64_t record = 30000;

    uint64_t count1, factor1;
    uint64_t count2 = 1, factor2 = 1;

    for (uint64_t n = 1; n <= limit; ++n)
    {
        factor1 = factor2;
        count1 = count2;

        factor2 = n + 1; if (factor2 % 2 == 0) factor2 /= 2;
        count2 = approxfactorcount (factor2);

        if (count1 * count2 > record)
        {
            uint64_t factors = factorcount (factor1) * factorcount (factor2);
            if (factors > record)
            {
                printf ("%lluth triangular number = %llu has %llu factors\n", n, factor1 * factor2, factors);
                record = factors;
            }
        }
    }
}

यह लगभग 0.7 सेकंड में 13824 कारकों के साथ 14,753,024 वें त्रिकोणीय को पाता है, 879,207,615 वां त्रिकोणीय संख्या 34 सेकंड में 61,440 कारकों के साथ, 12,524,486,975 वां त्रिकोणीय संख्या 10 मिनट 5 सेकंड में और 26,467,792,064 वां त्रिकोणीय संख्या 172,02 कारकों के साथ। 21 मिनट 25 सेकंड (2.4GHz Core2 Duo), इसलिए यह कोड औसतन केवल 116 प्रोसेसर चक्र प्रति नंबर लेता है। अंतिम त्रिकोणीय संख्या स्वयं 2 ^ 68 से बड़ी है, इसलिए


0

मैंने 500 के बजाय 1000 में "जनिच ब्रेंडल" संस्करण को संशोधित किया। और euler12.bin, euler12.erl, p12dist.erl के परिणाम को सूचीबद्ध करें। दोनों erl कोड संकलन के लिए '+ देशी' का उपयोग करते हैं।

zhengs-MacBook-Pro:workspace zhengzhibin$ time erl -noshell -s p12dist start
The result is: 842161320.

real    0m3.879s
user    0m14.553s
sys     0m0.314s
zhengs-MacBook-Pro:workspace zhengzhibin$ time erl -noshell -s euler12 solve
842161320

real    0m10.125s
user    0m10.078s
sys     0m0.046s
zhengs-MacBook-Pro:workspace zhengzhibin$ time ./euler12.bin 
842161320

real    0m5.370s
user    0m5.328s
sys     0m0.004s
zhengs-MacBook-Pro:workspace zhengzhibin$

0
#include <stdio.h>
#include <math.h>

int factorCount (long n)
{
    double square = sqrt (n);
    int isquare = (int) square+1;
    long candidate = 2;
    int count = 1;
    while(candidate <= isquare && candidate<=n){
        int c = 1;
        while (n % candidate == 0) {
           c++;
           n /= candidate;
        }
        count *= c;
        candidate++;
    }
    return count;
}

int main ()
{
    long triangle = 1;
    int index = 1;
    while (factorCount (triangle) < 1001)
    {
        index ++;
        triangle += index;
    }
    printf ("%ld\n", triangle);
}

gcc -lm -Ofast euler.c

समय ।/a.out

2.79 s उपयोगकर्ता 0.00s सिस्टम 99% cpu 2.794 कुल

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