প্রজেক্ট অলারের সাথে গতির তুলনা: সি বনাম পাইথন বনাম এরলং বনাম হাস্কেল


672

আমি প্রজেক্ট অলারের কাছ থেকে সমস্যা # 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

Haskell,:

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% (পাইপি সহ 118%)
  • এরং: 436% (রিচার্ডসকে 135% ধন্যবাদ)
  • হাস্কেল: 1421%

আমি মনে করি যে সি এর একটি বড় সুবিধা রয়েছে কারণ এটি গণনার জন্য দীর্ঘ ব্যবহার করে অন্য তিনটি হিসাবে নির্বিচার দৈর্ঘ্যের পূর্ণসংখ্যার নয়। এছাড়াও প্রথমে রানটাইম লোড করার দরকার নেই (অন্যরা কি করবেন?)।

প্রশ্ন 1: স্বেচ্ছাসেবী দৈর্ঘ্যের পূর্ণসংখ্যার ব্যবহারের কারণে এরলং, পাইথন এবং হাস্কেল কি গতি হারাবে বা মানগুলি যতক্ষণ কম তার চেয়ে কম হবে না MAXINT?

প্রশ্ন 2: হাস্কেল এত ধীর কেন? কোনও সংকলক পতাকা আছে যা ব্রেক বন্ধ করে দেয় বা এটি আমার বাস্তবায়ন? (দ্বিতীয়টি সম্ভবত সম্ভাব্য কারণ হ্যাস্কেল আমার কাছে সাতটি সীলযুক্ত একটি বই is)

প্রশ্ন 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]। Hurray!
tsvikas

35
সি এবং সমাবেশের মধ্যে এই যুদ্ধগুলির কথা মনে আছে এমন অন্য কেউ আছে? "অবশ্যই! আপনি সি কোডে আপনার কোড 10x দ্রুত লিখতে পারেন, তবে কী আপনার সি কোডটি এই দ্রুত চালাতে পারে? ..." আমি নিশ্চিত যে মেশিন-কোড এবং সমাবেশের মধ্যে একই লড়াই হয়েছিল।
জেএস।

39
@ জেএস: সম্ভবত তা নয়, অ্যাসেম্বলি হ'ল কাঁচা বাইনারি মেশিন কোডের পরিবর্তে টাইপ করা স্মৃতিবিদ্যার একটি সেট - সাধারণত তাদের মধ্যে 1-1 টি চিঠিপত্র থাকে।
কলম রজার্স

9
উপসংহার, হাস্কেলের জন্য: -O2 এটিকে প্রায় 3x স্পিডআপ দেয় এবং 12x-14x এবং আরও বেশি গতির মোট গতির জন্য 4x-6x এর জন্য পূর্ণসংখ্যার পরিবর্তে ইন্ট ব্যবহার করে।
নেস

উত্তর:


794

ব্যবহার GHC 7.0.3, gcc 4.4.6, Linux 2.6.29, একটি, x86_64 Core2 মানিকজোড় (2.5GHz) মেশিনে ব্যবহার সংকলন ghc -O2 -fllvm -fforce-recompHaskell, এবং gcc -O3 -lmসি জন্য

  • আপনার সি রুটিন 8.4 সেকেন্ডে চলবে (সম্ভবত আপনার রানের চেয়ে দ্রুত কারণ -O3)
  • হাস্কেল সলিউশনটি 36 সেকেন্ডে চালিত হয় ( -O2পতাকার কারণে )
  • আপনার factorCount'কোডটি স্পষ্টভাবে টাইপ করা এবং ডিফল্ট করা হয়নি Integer(এখানে আমার ভুল রোগ নির্ধারণের জন্য ড্যানিয়েলকে ধন্যবাদ!)। ব্যবহার করে একটি স্পষ্ট ধরণের স্বাক্ষর প্রদান (যা যাইহোক স্ট্যান্ডার্ড অনুশীলন) Intএবং সময়টি ১১.১ সেকেন্ডে পরিবর্তিত হয়
  • মধ্যে 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কত ধীর গতিতে সঞ্চালিত গণনার উপর নির্ভর করে। ভাগ্যক্রমে (bit৪ বিট মেশিনের জন্য) Intযথেষ্ট। বহনযোগ্যতার স্বার্থে আপনার সম্ভবত আমার কোডটি ব্যবহারের জন্য আবার লিখতে হবে Int64বা Word64(সি এর সাথে একমাত্র ভাষা নয় long)।

প্রশ্ন 2: হ্যাশেল এত ধীর কেন? কোনও সংকলক পতাকা আছে যা ব্রেক বন্ধ করে দেয় বা এটি আমার বাস্তবায়ন? (দ্বিতীয়টি যথেষ্ট সম্ভাবনাময় হ্যাশেল আমার কাছে সাতটি সীলযুক্ত একটি বই))

প্রশ্ন 3: আপনি কীভাবে আমি বিষয়গুলি নির্ধারণ করি সেগুলি পরিবর্তন না করে কীভাবে এই বাস্তবায়নগুলি অনুকূল করা যায় আপনি আমাকে কিছু ইঙ্গিত দিতে পারেন? কোনও উপায়ে অপ্টিমাইজেশন: ভাষাতে আরও ভাল, দ্রুত, আরও "নেটিভ"।

আমি উপরের উত্তর কি ছিল। উত্তর ছিল

  • 0) মাধ্যমে অপ্টিমাইজেশন ব্যবহার করুন -O2
  • 1) সম্ভব হলে দ্রুত (উল্লেখযোগ্যভাবে: আনবক্স-সক্ষম) প্রকারগুলি ব্যবহার করুন
  • 2) remনা mod(একটি ঘন ঘন ভুলে যাওয়া অপ্টিমাইজেশন) এবং
  • 3) কর্মী / মোড়ক রূপান্তর (সম্ভবত সবচেয়ে সাধারণ অপ্টিমাইজেশন)।

প্রশ্ন 4: আমার কার্যকরী বাস্তবায়নগুলি কি এলসিওকে অনুমতি দেয় এবং তাই কল স্ট্যাকের মধ্যে অপ্রয়োজনীয় ফ্রেম যুক্ত করা এড়ানো যায়?

হ্যাঁ, বিষয়টি ছিল না। ভাল কাজ এবং আপনি এটি বিবেচনা করে খুশি।


25
@ কার্ল কারন remআসলে modঅপারেশনের একটি উপ-উপাদান (এগুলি একই নয়)। আপনি যদি জিএইচসি বেস লাইব্রেরিতে সন্ধান করেন তবে আপনি modবেশ কয়েকটি শর্তের জন্য পরীক্ষা দেখতে পাবেন এবং সেই অনুসারে সাইনটি সামঞ্জস্য করুন। (দেখুন modInt#Base.lhs)
থমাস এম DuBuisson

20
আরেকটি ডেটা পয়েন্ট: আমি @ হাইপারবোরিয়াসের হাস্কেলকে না দেখে সি প্রোগ্রামটির একটি দ্রুত হাস্কেল অনুবাদ লিখেছি । সুতরাং এটি স্ট্যান্ডার্ড ইডিয়োমেটিক হাস্কেলের সাথে কিছুটা কাছাকাছি, এবং আমি জেনে বুঝে যুক্ত হওয়া একমাত্র অপ্টিমাইজেশনটি এই উত্তরটি পড়ার পরে প্রতিস্থাপন modকরছে rem(হিঁ, উফস)। আমার সময়ের জন্য লিঙ্কটি দেখুন, তবে সংক্ষিপ্ত সংস্করণটি "সি এর সাথে প্রায় অভিন্ন"।
সিএ ম্যাক্যান

106
এমনকি ভেবেছিল সি সংস্করণটি আমার মেশিনে দ্রুত চলেছে, হাস্কেলের প্রতি আমার এখন নতুন সম্মান রয়েছে। +1
শেঠ কার্নেগি

11
এটি আমার কাছে বেশ অবাক করার বিষয়, যদিও আমার এখনও এটি চেষ্টা করা হয়নি। যেহেতু মূলটি factorCount'লেজটি পুনরাবৃত্ত ছিল আমি ভাবতাম যে সংকলকটি অতিরিক্ত পরামিতিগুলি পরিবর্তন করা হচ্ছে না এবং কেবল পরিবর্তনশীল প্যারামিটারগুলির জন্য লেজ পুনরাবৃত্তিকে অনুকূল করতে পারে (হাস্কেল সর্বোপরি খাঁটি ভাষা হওয়ায় এটি সহজ হওয়া উচিত)। যে কেউ ভাবেন যে সংকলকটি এটি করতে পারে বা আমি আরও তত্ত্বের কাগজপত্র পড়তে ফিরে যেতে পারি?
kizzx2

22
@ কিজএক্সএক্স 2: এটি যুক্ত করার জন্য একটি জিএইচসি টিকিট রয়েছে । আমি যা বুঝতে পেরেছি, এই রূপান্তরটির ফলে ক্লোজার অবজেক্টগুলির অতিরিক্ত বরাদ্দ হতে পারে। এর অর্থ কিছু ক্ষেত্রে আরও খারাপ পারফরম্যান্স, তবে জোহান টিবেল তাঁর ব্লগ পোস্টে যেমন পরামর্শ দিয়েছেন ফলস্বরূপ মোড়কে অন্তর্ভুক্ত করা গেলে এটি এড়ানো যায়।
হামার

224

এরলং বাস্তবায়নে কিছু সমস্যা রয়েছে। নিম্নলিখিতটির বেসলাইন হিসাবে, আপনার অবিস্মরণীয় এরলং প্রোগ্রামের জন্য আমার পরিমাপের কার্যকর সময় সি কোডের জন্য 12.7 সেকেন্ডের তুলনায় 47.6 সেকেন্ড ছিল।

আপনি যদি গণনামূলকভাবে নিবিড় এরলং কোডটি চালাতে চান তবে আপনার প্রথম কাজটি হ'ল দেশীয় কোডটি ব্যবহার করা। সংকলনটি erlc +native euler12সময়টি ৪১.৩ সেকেন্ডে নেমে যায়। এই ধরণের কোডের নেটিভ সংকলন থেকে প্রত্যাশার চেয়ে এটি অনেক কম গতিবেগ (মাত্র 15%) এবং সমস্যাটি আপনার ব্যবহার -compile(export_all)। এটি পরীক্ষার জন্য দরকারী, তবে সমস্ত ফাংশন বাইরে থেকে সম্ভাব্যরূপে পৌঁছনীয় এই সত্যটি যে দেশীয় সংকলকটি খুব রক্ষণশীল হতে পারে। (সাধারণ বিএএম এমুলেটরটি তেমন প্রভাবিত হয় না)) এই ঘোষণাকে প্রতিস্থাপনের সাথে -export([solve/0]).আরও ভাল গতিবেগ পাওয়া যায়: ৩১.৫ সেকেন্ড (বেসলাইন থেকে প্রায় 35%)।

তবে কোডটিতে নিজেই একটি সমস্যা রয়েছে: ফ্যাক্টর কাউন্ট লুপের প্রতিটি পুনরাবৃত্তির জন্য , আপনি এই পরীক্ষাটি সম্পাদন করেন:

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

সি কোড এটি করে না। সাধারণভাবে, একই কোডের বিভিন্ন বাস্তবায়নের মধ্যে সুষ্ঠু তুলনা করা জটিল এবং বিশেষত যদি অ্যালগরিদম সংখ্যাসূচক হয় তবে আপনার অবশ্যই নিশ্চিত হওয়া উচিত যে তারা আসলে একই জিনিসটি করছে। কোথাও কোথাও কিছু টাইপকাস্টের কারণে একটি বাস্তবায়নে একটি সামান্য বৃত্তাকার ত্রুটি এটিকে অন্যটির চেয়ে অনেক বেশি পুনরাবৃত্তি করতে পারে যদিও উভয়ই শেষ পর্যন্ত একই ফলাফলে পৌঁছে।

এই সম্ভাব্য ত্রুটি উত্সটি (এবং প্রতিটি পুনরাবৃত্তির অতিরিক্ত পরীক্ষা থেকে মুক্তি পাওয়ার জন্য) নির্মূল করার জন্য, আমি সি কোডের সাথে ঘনিষ্ঠভাবে মডেল করে ফ্যাক্টরকাউন্ট ফাংশনটি আবার লিখেছি:

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: আমার কার্যকরী বাস্তবায়নগুলি কি এলসিওকে অনুমতি দেয় এবং তাই কল স্ট্যাকের মধ্যে অপ্রয়োজনীয় ফ্রেম যুক্ত করা এড়ানো যায়?

হ্যাঁ, আপনার এরলং কোডটি সর্বশেষ কল অপ্টিমাইজেশনের ক্ষেত্রে সঠিক।


2
আমি আপনার সাথে একমত. এই বেঞ্চমার্কটি বেশ কয়েকটি কারণে বিশেষত
এর্লংয়ের

156

PyPy, আপনি ব্যবহার করতে পারে PyPy এর (আপনার কোডে শূন্য পরিবর্তনের সঙ্গে চমত্কার চিত্তাকর্ষক গতি আপগুলি জন্য) ব্যবহার করে পাইথন অপ্টিমাইজেশান শুভেচ্ছা, উপরন্তু ইন অনুবাদ টুলচেইন একটি RPython-অনুবর্তী সংস্করণ কম্পাইল করার, বা Cython একটি এক্সটেনশন মডিউল দেখবেন, উভয় গড়ে তুলতে যা আমার পরীক্ষার সি সংস্করণের তুলনায় দ্রুত, সাইথন মডিউলটি প্রায় দ্বিগুণ দ্রুত । রেফারেন্সের জন্য আমি সি এবং পাইপাই বেঞ্চমার্ক ফলাফলগুলিও অন্তর্ভুক্ত করি:

সি (সংকলিত gcc -O3 -lm)

% time ./euler12-c 
842161320

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

পিপাই 1.5

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

আরপিথন (সর্বশেষ পিপিপি রিভিশন ব্যবহার করে 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

আরপিথন সংস্করণে কয়েকটি কী পরিবর্তন হয়েছে। একটি স্বতন্ত্র প্রোগ্রামে অনুবাদ করার জন্য আপনাকে আপনার সংজ্ঞা দেওয়া দরকার targetযা এই ক্ষেত্রে mainফাংশন। এটি sys.argvকেবল যুক্তি হিসাবে মেনে নেবে বলে আশা করা হচ্ছে এবং এটির জন্য একটি পূর্বাবস্থায় ফিরে আসতে হবে। আপনি ট্রান্সলেট.পি ব্যবহার করে এটি অনুবাদ করতে পারেন, % translate.py euler12-rpython.pyযা সিতে অনুবাদ করে এবং এটি আপনার জন্য সংকলন করে।

# 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মূলত, আপনার সংস্করণ হিসাবে একই কিছু অতিরিক্ত স্ট্যাটিক টাইপ ঘোষণা সাথে। সেটআপ.পি ব্যবহার করে এক্সটেনশনটি তৈরি করতে সাধারণ বয়লারপ্লেট রয়েছে 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
)

আমার সত্যিকার অর্থেই RPYthon বা Cython এর সাথে খুব কম অভিজ্ঞতা আছে এবং ফলাফলগুলি দেখে আনন্দিতভাবে অবাক হয়েছিল। আপনি যদি সিপিথন ব্যবহার করছেন, আপনার সিপিইউ-নিবিড় বিটগুলি সাইথন এক্সটেনশন মডিউলে লিখে আপনার প্রোগ্রামটিকে অনুকূলিত করার পক্ষে খুব সহজ উপায় বলে মনে হচ্ছে।


6
আমি কৌতূহলী, সি সংস্করণটি কমপক্ষে সিপিথনের চেয়ে দ্রুততর হওয়ার জন্য কী অনুকূলিত হতে পারে?
নাম

4
@ সার্জবর্চ যে সাইথন সংস্করণটি এত দ্রুত, কারণ এটি একটি উচ্চতর অনুকূল সি উত্সকে সংকলন করে, যার অর্থ আপনি নিশ্চিত যে সি এর বাইরে এই পারফরম্যান্সটি পেতে পারেন
এলি কোরভিগো

72

প্রশ্ন 3: আপনি কীভাবে আমি বিষয়গুলি নির্ধারণ করি সেগুলি পরিবর্তন না করে কীভাবে এই বাস্তবায়নগুলি অনুকূল করা যায় আপনি আমাকে কিছু ইঙ্গিত দিতে পারেন? কোনও উপায়ে অপ্টিমাইজেশন: ভাষাতে আরও ভাল, দ্রুত, আরও "নেটিভ"।

সি বাস্তবায়নটি সাবমোটিমাল (যেমন টমাস এম ডুবুইসন ইঙ্গিত করেছেন) সংস্করণটিতে -৪-বিট ইন্টিজার (অর্থাত্ লম্বা ডেটাটাইপ) ব্যবহার করা হয়েছে। আমি পরে সমাবেশের তালিকাটি তদন্ত করব, তবে একটি শিক্ষিত অনুমানের সাথে সংকলিত কোডটিতে কিছু মেমরি অ্যাক্সেস চলছে যা 64৪-বিট পূর্ণসংখ্যার উল্লেখযোগ্যভাবে ধীর করে দেয়। এটি বা উত্পন্ন কোড (এটি সত্য যে আপনি কোনও এসএসই রেজিস্ট্রারে কম -৪-বিট ইনট ফিট করতে পারেন বা একটি ডাবলকে inte৪-বিট পূর্ণসংখ্যার চেয়ে ধীর গতিতে পারেন))

এখানে পরিবর্তিত কোড (কেবল প্রতিস্থাপন দীর্ঘ সঙ্গে int- এ এবং আমি স্পষ্টভাবে factorCount inlined, যদিও আমি মনে করি না এই জিসিসি -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

রেফারেন্সের জন্য, থমাসের পূর্বের উত্তরে হ্যাকেল বাস্তবায়ন দেয়:

$ 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

উপসংহার: জিএইচসি থেকে দূরে কিছু না নেওয়া, এটি একটি দুর্দান্ত সংকলক, তবে জিসিসি সাধারণত দ্রুত কোড উত্পন্ন করে।


22
খুব সুন্দর! তুলনার জন্য, আমার মেশিনে আপনার সি 2.5 secondsসলিউশনটি চলছে যখন হাস্কেল কোডে একই রকম পরিবর্তন (ওয়ার্ড 32 এ সরানো, ইনলাইন প্রাগমা যুক্ত করে) এর একটি রানটাইমের ফলাফল 4.8 seconds। সম্ভবত কিছু করা সম্ভব (তুচ্ছভাবে নয়, মনে হয়) - জিসিসির ফলাফল অবশ্যই চিত্তাকর্ষক।
টমাস এম ডুবুইসন

1
ধন্যবাদ! সম্ভবত প্রশ্নটি প্রকৃত ভাষার চেয়ে বিভিন্ন সংকলক দ্বারা সংকলিত আউটপুটটির গতি হওয়া উচিত। তারপরে আবারও, ইন্টেল ম্যানুয়ালগুলি টেনে আনতে এবং হাত দ্বারা অনুকূলকরণ করা এখনও পুরোপুরি জিততে পারে (আপনি জ্ঞান এবং সময় (প্রচুর) পেয়ে থাকেন)।
রেডউফ

56

এই ব্লগ একবার দেখুন । বিগত বছরে বা তাই তিনি Haskell, এবং পাইথন মধ্যে Project ইউলার সমস্যার কয়েক সম্পন্ন, এবং তিনি সাধারণত পাওয়া যায় Haskell, অনেক দ্রুত হতে হবে। আমি মনে করি যে এই ভাষাগুলির মধ্যে এটির আপনার সাবলীলতা এবং কোডিং শৈলীর সাথে আরও বেশি সম্পর্ক রয়েছে।

পাইথন গতির কথা এলে আপনি ভুল বাস্তবায়নটি ব্যবহার করছেন! ব্যবহার করে দেখুন 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
সর্বশেষে আমি হাস্কেলকে পরীক্ষা করেছিলাম "প্রাইমস" হ'ল প্রাক্পম্পিউটেড প্রাইমগুলির একটি বিশাল তালিকা - কোনও গণনা নয়, কেবল অনুসন্ধান। তাই হ্যাঁ, অবশ্যই এই দ্রুততর হবে, কিন্তু এটা তোমার গণনীয় গতি সম্পর্কে কিছুই বলে আহরিত মধ্যে Haskell মৌলিক।
zxq9

21
@ zxq9 আপনি কি আমাকে ইঙ্গিত করতে পারবেন যে প্রাইমস প্যাকেজ উত্সে ( hackage.haskell.org/package/primes-0.2.1.0/docs/src/… ) প্রাথমিক সংখ্যাগুলির তালিকাটি অবস্থিত?
ফ্রেজারে

4
যদিও উত্সটি দেখায় যে প্রাইমগুলি প্রাক্পম্পিউটেড নয়, এই গতিটি পুরোপুরি উন্মাদ, সি সংস্করণের চেয়ে মাইল মাইল দ্রুত, তাই হেক কী চলছে?
সেমিকোলন

1
@ সেমিকোলন মুখস্ত। এই ক্ষেত্রে আমি মনে করি যে হাস্কেল রানটাইমের সময় সমস্ত প্রাইম মুখস্থ করেছিল তাই এটি প্রতিটি পুনরাবৃত্তি পুনরায় সংশোধন করার প্রয়োজন হয় না।
হাউলেথ

5
এটা তোলে 1000 ভাজক, না 500 এর
ক্যাসপার Færgemand

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.55-0.58 সেকেন্ডে ধারাবাহিকভাবে চালিত হয় (1.73GHz কোর আই 7)।

সি সংস্করণের জন্য আরও কার্যকরী ফ্যাক্টরকাউন্ট ফাংশন:

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 সেকেন্ডে চলে।

নবম ত্রিভুজ সংখ্যা = এন * (এন + 1) / 2, এবং এন এবং (এন + 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 উন্নতি।
থোনো

কি দারুন. আপনি inferred প্রকারগুলি এড়ানোর পরে হাস্কেল দুর্দান্ত অভিনয় করে
পিয়ুশ কাটারিয়া

আসলে এটি যে এটি অনুকরণ করে তা নয়। এটি কেবল আপনাকে A) ডিবাগ করতে বা টাইপ সমস্যা এবং টাইপক্লাস উদাহরণ নির্বাচনের সমস্যাগুলি এড়াতে খ) ডিবাগ করতে এবং কয়েকটি আধুনিক ভাষা বর্ধনের সাথে কিছু অনস্বীকার্য ধরণের সমস্যা এড়াতে সহায়তা করে। এটি আপনাকে আপনার প্রোগ্রামগুলিকে অপ্রয়োজনীয় করতে সহায়তা করে যাতে আপনি কখনই আপনার উন্নয়নের প্রচেষ্টা বাড়িয়ে তুলতে পারবেন না।
কোডশট

সি সংস্করণ 0.11 গুলি ইনটেল খুলি উপত্যকায়
কোডশট

13
প্রশ্ন 1: স্বেচ্ছাসেবী দৈর্ঘ্যের পূর্ণসংখ্যার ব্যবহারের কারণে কি অ্যালাংগ, অজগর এবং হ্যাশেল শিথিল গতি আছে বা মানগুলি MAXINT এর চেয়ে কম হবে ততক্ষণ সেগুলি হয় না?

এটি অসম্ভব। আমি এরলং এবং হাস্কেল সম্পর্কে খুব বেশি বলতে পারি না (ভাল, নীচে হাস্কেল সম্পর্কে কিছুটা সম্ভবত) তবে আমি পাইথনের অনেকগুলি বাধাও বলতে পারি। প্রোগ্রামটি প্রতিবার পাইথনের কিছু মান সহ কোনও ক্রিয়াকলাপ চালানোর চেষ্টা করে, মানগুলি যথাযথ ধরণের কিনা তা যাচাই করা উচিত এবং এতে কিছুটা সময় ব্যয় হয়। আপনার factorCountফাংশনটি কেবলমাত্র range (1, isquare + 1)বিভিন্ন সময় সহ একটি তালিকা বরাদ্দ করে , এবং রানটাইম, mallocস্টাইলযুক্ত মেমরি বরাদ্দকটি আপনি সি হিসাবে যেমন একটি কাউন্টার সহ একটি পরিসরে পুনরাবৃত্তি চেয়ে ধীরে ধীরে লক্ষণীয়, factorCount()একাধিক বার বলা হয় এবং তাই প্রচুর তালিকাগুলি বরাদ্দ করে। এছাড়াও, আসুন আমরা ভুলে যাব না যে পাইথনকে ব্যাখ্যা করা হয়েছে এবং সিপিথন দোভাষীর অনুকূলিতকরণের ক্ষেত্রে কোনও দুর্দান্ত ফোকাস নেই।

সম্পাদনা : ওহ, ভাল, আমি নোট করছি যে আপনি পাইথন 3 ব্যবহার করছেন তাই range()কোনও তালিকা ফেরত দেয় না, তবে একটি জেনারেটর। এই ক্ষেত্রে, তালিকা বরাদ্দ সম্পর্কে আমার বক্তব্যটি অর্ধ-ভুল: ফাংশনটি কেবলমাত্র rangeঅবজেক্টগুলিকে বরাদ্দ দেয় , তবে তা অকার্যকর তবে অনেক আইটেম সহ একটি তালিকা বরাদ্দ করার মতো অদক্ষ নয়।

প্রশ্ন 2: হ্যাশেল এত ধীর কেন? কোনও সংকলক পতাকা আছে যা ব্রেক বন্ধ করে দেয় বা এটি আমার বাস্তবায়ন? (দ্বিতীয়টি যথেষ্ট সম্ভাবনাময় হ্যাশেল আমার কাছে সাতটি সীলযুক্ত একটি বই))

আপনি কি আলিঙ্গন ব্যবহার করছেন ? আলিঙ্গন একটি যথেষ্ট ধীর দোভাষী। আপনি যদি এটি ব্যবহার করে থাকেন তবে সম্ভবত আপনি জিএইচসি -র সাথে আরও ভাল সময় পেতে পারেন - তবে আমি কেবল অনুমানমূলক কল্পিতই আছি, একটি ভাল হাস্কেল সংকলক হুডের নীচে যে ধরণের জিনিসগুলি করে তা বেশ আকর্ষণীয় এবং আমার বোধগম্যতার বাইরেও থাকে :)

প্রশ্ন 3: আপনি কীভাবে আমি বিষয়গুলি নির্ধারণ করি সেগুলি পরিবর্তন না করে কীভাবে এই বাস্তবায়নগুলি অনুকূল করা যায় আপনি আমাকে কিছু ইঙ্গিত দিতে পারেন? কোনও উপায়ে অপ্টিমাইজেশন: ভাষাতে আরও ভাল, দ্রুত, আরও "নেটিভ"।

আমি বলব আপনি একটি অস্বাভাবিক খেলা খেলছেন। বিভিন্ন ভাষাগুলি জানার সর্বোত্তম অংশটি হ'ল তাদের সবচেয়ে আলাদাভাবে সম্ভব উপায় ব্যবহার করা :) তবে আমি এই বিষয়টির জন্য আমার কাছে কোনও প্রস্তাবনা রাখি না dig দুঃখিত, আমি আশা করি কেউ আপনাকে এই ক্ষেত্রে সহায়তা করতে পারে :)

প্রশ্ন 4: আমার কার্যকরী বাস্তবায়নগুলি কি এলসিওকে অনুমতি দেয় এবং তাই কল স্ট্যাকের মধ্যে অপ্রয়োজনীয় ফ্রেম যুক্ত করা এড়ানো যায়?

যতদূর আমি মনে করি, আপনাকে কেবল এটি নিশ্চিত করতে হবে যে কোনও মান ফেরত দেওয়ার আগে আপনার পুনরাবৃত্ত কলটি সর্বশেষ আদেশ command অন্য কথায়, নীচের মত একটি ফাংশন যেমন অপ্টিমাইজেশন ব্যবহার করতে পারে:

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ইত্যাদি) এখন তুমি জানো মানুষ কেন এটা করতে :)


@ হাইপারবোরাস আপনাকে ধন্যবাদ! এছাড়াও, আমি আপনার পরবর্তী প্রশ্ন সম্পর্কে সত্যিই আগ্রহী। তবে, আমি আপনাকে সতর্ক করে দিয়েছি যে আমার জ্ঞান সীমিত তাই আমি আপনার প্রতিটি প্রশ্নের উত্তর দিতে পারিনি। এটির ক্ষতিপূরণ দেওয়ার চেষ্টা করার জন্য আমি আমার উত্তর সম্প্রদায়ে উইকি তৈরি করেছি যাতে লোকেরা আরও সহজেই এর পরিপূরক হয়।
ব্র্যান্ডিজি

ব্যাপ্তি ব্যবহার সম্পর্কে। আমি যখন বর্ধিতকরণের সাথে সামান্য লুপের সাথে পরিসরটি প্রতিস্থাপন করি (সি এর লুপটির জন্য নকল করছি), কার্যকর করার সময়টি আসলে দ্বিগুণ হয়। আমার ধারণা জেনারেটরগুলি বেশ অনুকূলিত হয়েছে।
হাইপারবোরাস

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

উপরের কোডে, আমি @ থমাসের জবাবগুলিতে সাধারণ তালিকা ক্রিয়াকলাপের সাথে স্পষ্ট পুনরাবৃত্তিগুলি প্রতিস্থাপন করেছি। লেজ পুনরাবৃত্তি সম্পর্কে চিন্তা না করে কোডটি এখনও ঠিক একই কাজ করে। এটা তোলে (~ রান 7.49s সম্বন্ধে) 6% @Thomas 'উত্তরে সংস্করণ (~ তুলনায় ধীর 7.04s ,) GHC 7.6.2 সঙ্গে আমার মেশিনে যখন @Raedwulf থেকে সি সংস্করণ ~ রান 3.15s । এটি বছরের পর বছর ধরে জিএইচসি উন্নত হয়েছে বলে মনে হচ্ছে।

পুনশ্চ. আমি জানি এটি একটি পুরানো প্রশ্ন, এবং আমি গুগল অনুসন্ধানগুলি থেকে এটিকে হোঁচট খাচ্ছি (আমি যা খুঁজছিলাম তা ভুলে গিয়েছি, এখন ...)। কেবলমাত্র এলসিও সম্পর্কে প্রশ্নটিতে মন্তব্য করতে এবং সাধারণভাবে হাস্কেল সম্পর্কে আমার অনুভূতি প্রকাশ করতে চেয়েছিলাম। আমি উপরের উত্তরে মন্তব্য করতে চেয়েছিলাম, কিন্তু মন্তব্যগুলি কোড ব্লককে অনুমতি দেয় না।


9

সি সংস্করণের জন্য আরও কিছু নম্বর এবং ব্যাখ্যা। স্পষ্টতই এই সমস্ত বছরগুলিতে কেউ তা করেনি। এই উত্তরটিকে আপভোট করতে ভুলবেন না যাতে এটি দেখতে এবং শেখার জন্য সবার উপরে উঠতে পারে।

প্রথম ধাপ: লেখকের প্রোগ্রামগুলির বেঞ্চমার্ক

ল্যাপটপ বিশেষ উল্লেখ:

  • সিপিইউ আই 3 এম 380 (931 মেগাহার্টজ - সর্বাধিক ব্যাটারি সাশ্রয় মোড)
  • 4 জিবি মেমরি
  • Win7 64 বিট
  • মাইক্রোসফ্ট ভিজ্যুয়াল স্টুডিও 2012 আলটিমেট
  • জিসিসি 4.9.3 সহ সিগউইন win
  • পাইথন 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
  • কম্পাইলার জিসিসি বা vs2012 হয়

দ্বিতীয় ধাপ: তদন্ত, উন্নতি এবং আবার বেঞ্চমার্ক

ভিসি জিসিসির চেয়ে 250% দ্রুত is দুটি সংকলককে একই গতি দেওয়া উচিত। স্পষ্টতই, কোড বা সংকলক বিকল্পগুলির সাথে কিছু ভুল। আসুন তদন্ত করা যাক!

আগ্রহের প্রথম পয়েন্টটি হল পূর্ণসংখ্যার প্রকারগুলি। রূপান্তরগুলি ব্যয়বহুল হতে পারে এবং আরও ভাল কোড উত্পন্নকরণ এবং অনুকূলিতকরণের জন্য ধারাবাহিকতা গুরুত্বপূর্ণ। সমস্ত পূর্ণসংখ্যা একই ধরণের হওয়া উচিত।

এটা একটি মিশ্র জগাখিচুড়ি 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>

সি তে প্রচুর সংখ্যক পূর্ণসংখ্যার প্রকার রয়েছে, এর সাথে খেলতে স্বাক্ষরিত / স্বাক্ষরিত কিছু নাও x86 বা x64 (প্রকৃত পূর্ণসংখ্যার আকারের সাথে বিভ্রান্ত হওয়ার দরকার নেই) হিসাবে সংকলন করার পছন্দ রয়েছে। এটি সংকলন ও চালনার জন্য অনেকগুলি সংস্করণ ^^

তৃতীয় ধাপ: নম্বরগুলি বোঝা

সংক্ষিপ্ত সিদ্ধান্ত:

  • 32 বিট ইন্টিজারগুলি 64 বিটের সমমানের তুলনায় 200% faster দ্রুত
  • স্বাক্ষরবিহীন 64 বিট ইন্টিজার 25% তুলনায় দ্রুততর হয় স্বাক্ষরিত 64 বিট (দুর্ভাগ্যবশত, আমি যে জন্য কোন ব্যাখ্যা আছে)

কৌশল কৌশল: "সি-তে দীর্ঘ এবং দীর্ঘ দীর্ঘ মাপগুলি কী?"
সঠিক উত্তরটি হ'ল: সি এর ইনট এবং লম্বার আকারটি সঠিকভাবে সংজ্ঞায়িত হয় না!

সি স্পেক থেকে:

কমপক্ষে অন্তত 32 বিট
দীর্ঘ ন্যূনতম কোনও int

Gcc ম্যান পৃষ্ঠা থেকে (-m32 এবং -m64 পতাকা):

32-বিট পরিবেশটি 32 বিটগুলিতে ইনট, লম্বা এবং পয়েন্টার সেট করে এবং কোনও আই 386 সিস্টেমে চলে এমন কোড উত্পন্ন করে।
64-বিট পরিবেশটি 32 বিট এবং দীর্ঘ এবং 64 বিটগুলিতে পয়েন্টার নির্ধারণ করে এবং এএমডি এর x86-64 আর্কিটেকচারের জন্য কোড উত্পন্ন করে।

এমএসডিএন ডকুমেন্টেশন (ডেটা টাইপ রেঞ্জ) থেকে https://msdn.microsoft.com/en-us/library/s3f49ktz%28v=vs.110%29.aspx :

int, 4 বাইট, স্বাক্ষরিত
দীর্ঘ হিসাবেও জানে , 4 বাইট, লং ইন্ট এবং স্বাক্ষরিত দীর্ঘ অন্তর্ হিসাবেও জানে

এটি উপসংহারে: পাঠ শিখেছি

  • 32 বিট পূর্ণসংখ্যার 64 বিট পূর্ণসংখ্যার চেয়ে দ্রুত হয়।

  • স্ট্যান্ডার্ড পূর্ণসংখ্যার প্রকারগুলি সি বা সি ++ তে ভালভাবে সংজ্ঞায়িত হয় না, তারা সংকলক এবং আর্কিটেকচারের উপর নির্ভর করে পরিবর্তিত হয়। আপনার যখন ধারাবাহিকতা এবং পূর্বাভাসের দরকার হয় তখন uint32_tথেকে পূর্ণসংখ্যার পরিবারটি ব্যবহার করুন #include <stdint.h>

  • গতির সমস্যাগুলি সমাধান করা। অন্যান্য সমস্ত ভাষা শতভাগ পিছনে পিছনে, সি ও সি ++ আবার জিতে! তারা সবসময় না। পরবর্তী উন্নতিটি ওপেনএমপি: ডি ব্যবহার করে মাল্টিথ্রেডিং হবে


কৌতূহলের বাইরে, ইন্টেল সংকলকরা কীভাবে করবেন? তারা সাধারণত সংখ্যার কোডটি অনুকূল করে তোলার ক্ষেত্রে খুব ভাল।
kirbyfan64sos

সি স্পিকে "ইন্ট কমপক্ষে 32 বিট" গ্যারান্টি দিয়ে বলে কোন রেফারেন্স আপনি কোথায় পাবেন? আমি কেবলমাত্র গ্যারান্টি সম্পর্কে জানি তা হ'ল ন্যূনতম মাত্রা INT_MINএবং INT_MAX(-32767 এবং 32767, যা কার্যত intকমপক্ষে 16 বিট হতে হবে এমন একটি প্রয়োজনীয়তা আরোপ করে)। longকমপক্ষে একটি হিসাবে বৃহত্তর intহওয়া প্রয়োজন, এবং পরিসীমা প্রয়োজনীয়তাগুলির অর্থ longকমপক্ষে 32 বিট।
শ্যাডোর্যাঞ্জার

আপনি সঠিক বলে মনে হচ্ছে। stackoverflow.com/questions/1231147/is-int-in-c-always-32-bit
user5994461

8

আপনার এরলং বাস্তবায়নের দিকে তাকিয়ে আছেন। সময়টিতে পুরো ভার্চুয়াল মেশিনের শুরু, আপনার প্রোগ্রামটি চালানো এবং ভার্চুয়াল মেশিনটি থামানো অন্তর্ভুক্ত করা হয়েছে। আমি নিশ্চিত যে এরং ভিএম সেট আপ এবং থামাতে কিছুটা সময় লাগে।

যদি সময়টি নিজেই এরলং ভার্চুয়াল মেশিনের মধ্যে করা হয়ে থাকে, তবে ফলাফলটি অন্যরকম হবে যেহেতু আমাদের কেবলমাত্র প্রোগ্রামে আসল সময় আসবে। অন্যথায়, আমি বিশ্বাস করি যে এরলং ভিএম এর শুরু এবং লোড করার প্রক্রিয়া দ্বারা নেওয়া মোট সময় এবং এটি বন্ধ করার সময় (আপনি আপনার প্রোগ্রামে এটি যেমন রেখেছিলেন) সমস্ত সময় আপনি যে পদ্ধতিতে সময় ব্যবহার করছেন তা মোট সময়টিতে অন্তর্ভুক্ত রয়েছে প্রোগ্রাম আউটপুট হয়। আমরা যখন আমাদের প্রোগ্রামগুলিকে ভার্চুয়াল মেশিনের মধ্যেই সময় দিতে চাই তখন ব্যবহার করি এমন ত্রুটিযুক্ত সময়টি ব্যবহার করার বিষয়টি বিবেচনা করুন timer:tc/1 or timer:tc/2 or timer:tc/3। এইভাবে, এরলং থেকে প্রাপ্ত ফলাফলগুলি ভার্চুয়াল মেশিনটি শুরু এবং বন্ধ / হত্যা / থামাতে নেওয়া সময়কে বাদ দেবে। এটি সেখানে আমার যুক্তি, এটি সম্পর্কে চিন্তা করুন এবং তারপরে আবার আপনার বেঞ্চ চিহ্নটি ব্যবহার করে দেখুন।

আমি প্রকৃতপক্ষে প্রস্তাব দিচ্ছি যে আমরা একটি নির্দিষ্ট মান পাওয়ার জন্য প্রোগ্রামটি (একটি রানটাইম রয়েছে এমন ভাষাগুলির জন্য), সেই ভাষাগুলির রানটাইমের মধ্যেই চেষ্টা করার চেষ্টা করব। উদাহরণস্বরূপ সি এরলং, পাইথন এবং হাস্কেল যেমন রানটাইম সিস্টেম শুরু এবং বন্ধ করার কোনও ওভারহেড নেই (98% এটি সম্পর্কে নিশ্চিত - আমি সংশোধন করি)। সুতরাং (এই যুক্তির ভিত্তিতে) আমি এই বলে শেষ করি যে একটি রানটাইম সিস্টেমের শীর্ষে চলমান ভাষাগুলির জন্য এই বেঞ্চমার্কটি যথাযথ / ন্যায্য ছিল না। এই পরিবর্তনগুলি দিয়ে এটি আবার করতে দিন।

সম্পাদনা: এ ছাড়াও সমস্ত ভাষায় রানটাইম সিস্টেম থাকলেও প্রতিটি শুরু করার ওভারহেড এটিকে পৃথক করে রাখে। সুতরাং আমি রানটাইম সিস্টেমে (যে ভাষার জন্য এটি প্রযোজ্য সেগুলির জন্য) আমরা সময় দেওয়ার পরামর্শ দিই। এরলং ভিএম শুরুতে যথেষ্ট ওভারহেড রাখে বলে জানা যায়!


আমি আমার পোস্টে এটি উল্লেখ করতে ভুলে গেছি, তবে আমি সিস্টেমটি শুরু করতে যে সময় লাগে তার পরিমাপ করেছি (এরেল -নোশেল-এস এরলং হোল্ট) - আমার মেশিনে প্রায় 0.1 সেকেন্ড। প্রোগ্রামটির রান সময় (প্রায় 10 সেকেন্ড) এর তুলনায় এটি যথেষ্ট ছোট যা এ সম্পর্কে কোয়ে ফেলার মতো নয়।
রিচার্ডসি

আপনার মেশিনে! আপনি সান ফায়ার সার্ভারে কাজ করছেন কিনা তা আমরা জানি না! যেহেতু সময়টি মেশিনের চশমাগুলির সাথে একটি পরিবর্তনশীল আনুপাতিক, তাই এটি বিবেচনায় নেওয়া উচিত .... বকবক করা?
মুজায়া জোশুয়া

2
@ রিচার্ডসি কোথাও উল্লেখ করেন নি যে এর্লং দ্রুতগতিযুক্ত :) এর আলাদা লক্ষ্য রয়েছে, গতি নয়!
ব্যতিক্রম

7

প্রশ্ন 1: স্বেচ্ছাসেবী দৈর্ঘ্যের পূর্ণসংখ্যার ব্যবহারের কারণে এরলং, পাইথন এবং হাস্কেল কী গতি হারাবে বা মানগুলি ম্যাক্সিন্টের চেয়ে কম হবে না কেন?

এর্ল্যাংয়ের জন্য নেতিবাচক প্রশ্নের উত্তর একটির দেওয়া যেতে পারে। সর্বশেষ প্রশ্নের উত্তর যথাযথভাবে এরংকে ব্যবহার করে দেওয়া হয়েছে:

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

যেহেতু এটি আপনার প্রাথমিক সি উদাহরণের চেয়ে দ্রুত, সুতরাং আমি অনুমান করব যে অন্যান্য সমস্যাগুলি ইতিমধ্যে বিশদটি আওতায় এসেছে বলে অনেকগুলি সমস্যা রয়েছে।

এই এরলং মডিউলটি প্রায় 5 সেকেন্ডের মধ্যে সস্তা নেটবুকটিতে চালিত করে ... এটি ইরংয়ে নেটওয়ার্ক থ্রেড মডেল ব্যবহার করে এবং যেমন ইভেন্ট মডেলের সুবিধা কীভাবে নেওয়া যায় তা প্রদর্শন করে। এটি অনেকগুলি নোডের উপরে বিতরণ করা যেতে পারে। এবং এটি দ্রুত। আমার কোড নয়

-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.

নীচের পরীক্ষাটি একটিতে অনুষ্ঠিত হয়েছিল: ইন্টেল (আর) এটম (টিএম) সিপিইউ এন 270 @ 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 হিসাবে, সর্বাধিক নতুন পরীক্ষা: ইন্টেলকোরি 2 সিপিইউ 6600 @ 2.40GHz আসল 0 এম 2.370 সেকেন্ডে এসেছেন
মার্ক ওয়াশিম

আপনার ফলাফল: 76576500 অন্য সবাই: 842161320 আপনার ফলাফলের সাথে কিছুটা সঙ্কীর্ণ
ডেভিড ডেভিডসন

যেহেতু আমি অন্যান্য কিছু এলিউর সমস্যা ডং ছিলাম, তাই আমি কেবল আমার ফলাফলটি পরীক্ষা করেছিলাম। উত্তর projecteuler.net/problem=12 76576500 এটি সম্পর্কে কোন প্রশ্ন। আমি জানি এটি অদ্ভুত বলে মনে হচ্ছে, তবে আমি কেবল পরীক্ষা করেছি।
মার্ক ওয়াশিম

তুলনার জন্য আমি মূল সি সংস্করণটির সাথে 9.03 পেয়েছি যখন মার্কের কোড সহ এরলং 19 ব্যবহার করার সাথে সাথে আমি 5.406, 167.0366% দ্রুত পেয়েছি।
thanos

5

সি ++ 11, <আমার জন্য 20 মিমি - এটি এখানে চালান

আমি বুঝতে পেরেছি যে আপনি আপনার ভাষা নির্দিষ্ট জ্ঞানের উন্নতিতে সহায়তা করার জন্য টিপস চান, তবে যেহেতু এখানে ভালভাবে আচ্ছাদিত হয়েছে, আমি ভেবেছিলাম যে আমি আপনার প্রশ্নে ইত্যাদি বিষয়ে গণিতের মন্তব্যে তাকিয়ে থাকতে পারি তাদের জন্য কিছু প্রসঙ্গ যুক্ত করব এবং ভাবছি কেন এটি কোড এত ধীর ছিল।

এই উত্তরটি মূলত আপনার প্রশ্নে / অন্যান্য উত্তরগুলিতে কোডটি আরও সহজেই মূল্যায়ণে সহায়তা করতে প্রসঙ্গটি সরবরাহ করার জন্য।

এই কোডটি কেবলমাত্র (কুরুচিপূর্ণ) অপ্টিমাইজেশান ব্যবহার করে, ব্যবহৃত ভাষার সাথে সম্পর্কিত নয়, এর উপর ভিত্তি করে:

  1. প্রতিটি ট্রেজিং নম্বর এন (এন + 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 এবং আমার ল্যাপটপের জন্য 80 মিমি নিয়ে যায়, আমি এখানে অন্যান্য কোডগুলি দেখেছি from এবং সন্দেহ আছে যে অনেক অপটিমাইজেশন এখনও উপলব্ধ।


7
এটি বরং স্পষ্টভাবে নয় যা প্রশ্নকর্তা অনুরোধ করেছিলেন, "আমি চারটি ভাষায় যথাসম্ভব একই অ্যালগরিদম বাস্তবায়নের চেষ্টা করেছি"। আপনার অনুরূপ অনেক মুছে ফেলা উত্তরের একটিতে মন্তব্যটির উদ্ধৃতি দিতে "এটি ভাষাটি নির্বিশেষে আপনি আরও ভাল অ্যালগরিদমের সাথে দ্রুত গতি পেতে পারেন তা বেশ স্পষ্ট।"
থমাস এম ডুবুইসন

2
@ ThomasM.DuBuisson। আমি এটাই পাচ্ছি প্রশ্নটির উত্তরগুলি ভারীভাবে বোঝায় যে অ্যালগরিদমিক গতির আপগুলি উল্লেখযোগ্য (এবং অবশ্যই ওপি তাদের জিজ্ঞাসা করছে না), তবে এর সুস্পষ্ট উদাহরণ নেই। আমি মনে করি এই উত্তরটি - যা একেবারে ভারী অনুকূলিতকরণের কোড নয় - নিজের মতো কারও জন্যই কিছুটা দরকারী প্রসঙ্গ সরবরাহ করে, যিনি ওপি'র কোডটি কতটা ধীর / গতিতে ভাবছিলেন।
ব্যবহারকারী3125280

জিসিসি এমনকি অনেকগুলি নিদর্শনগুলি প্রাক-গণনা করতে পারে। int a = 0; (int i = 0; i <10000000; ++ i) {a + = i; comp সংকলনের সময় গণনা করা হবে, সুতরাং রানটাইমে <1 এমএস নিন। এটা খুবই বড়, মোট ছাত্র
আর্থার

@ থমাস: আমি অবশ্যই ব্যবহারকারীর সাথে একমত হতে পারি: ১২৫৫২৮০ - ভাষাগুলিকে তুলনা করা উচিত যে তারা বোবা কিছু করতে সত্যিকারের প্রোগ্রামিং ভাষাকে কীভাবে পরাজিত করতে ব্যর্থ হয় তার পরিবর্তে তারা কীভাবে কিছু স্মার্ট করার চেষ্টা করে। স্মার্ট অ্যালগরিদমগুলি সাধারণত নমনীয়তা, জিনিসগুলি তারের ক্ষমতা (তাদের সংমিশ্রণ) এবং পরিকাঠামোর চেয়ে মাইক্রোস্কোপিক দক্ষতার বিষয়ে কম যত্ন করে। পয়েন্টটি 20 এমএস বা 50 এমএস পায় কিনা তা 8 টি নয় , এটি 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%
থুমকিডের সি সংস্করণ: 0.1060 8650%
প্রথম গো সংস্করণ: 8.2520 111%
দ্বিতীয় গো সংস্করণ: 0.0230 39865%

আমি পাইথন 3.6 এবং পাইপি 3.3-5.5-আলফা চেষ্টা করেছি:

মূল সি সংস্করণ: 8.629 100%
থৌমকিডের সি সংস্করণ: 0.109 7916%
পাইথন 3.6: 54.795 16%
পাইপাই 3.3-5.5-আলফা: 13.291 65%

এবং তারপরে নিম্নলিখিত কোড সহ আমি পেয়েছি:

আসল সি সংস্করণ: 8.629 100%
থৌমকিডের সি সংস্করণ: 0.109 8650%
পাইথন 3.6: 1.489 580%
পাইপি 3.3.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 তম ত্রিভুজাকার, 34 সেকেন্ডে 61,440 ফ্যাক্টর সহ 879,207,615 তম ত্রিভুজাকার সংখ্যা, 12 মিনিট 5 সেকেন্ডে 138,240 গুণক সহ 12,524,486,975 তম ত্রিভুজাকার সংখ্যা এবং 17,467,792,0642 সংখ্যার সাথে 26,467,792,0642 সংখ্যার সাথে 21 মিনিট 25 সেকেন্ড (2.4GHz কোর 2 ডুও), সুতরাং এই কোডটি গড়ে প্রতি সংখ্যা 116 প্রসেসরের চক্র নেয়। শেষ ত্রিভুজাকার সংখ্যা নিজেই 2 ^ 68 এর চেয়ে বড়


0

আমি "জান্নিচ ব্রেন্ডল" সংস্করণটি 500 এর পরিবর্তে 1000 এ পরিবর্তিত করেছি e উভয়ইরেল কোডগুলি সংকলনের জন্য '+ নেটিভ' ব্যবহার করে।

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);
}

জিসিসি-এলএম-প্রাতঃরাশ euler.c

সময় ./a.out

2.79s ব্যবহারকারীর 0.00s সিস্টেম 99% সিপিইউ 2.794 মোট

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.