Saya telah mengambil Soal # 12 dari Project Euler sebagai latihan pemrograman dan untuk membandingkan implementasi (pasti tidak optimal) saya dalam C, Python, Erlang dan Haskell. Untuk mendapatkan waktu eksekusi yang lebih tinggi, saya mencari nomor segitiga pertama dengan lebih dari 1000 pembagi daripada 500 seperti yang dinyatakan dalam masalah asli.
Hasilnya adalah sebagai berikut:
C:
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
Python:
lorenzo@enzo:~/erlang$ time ./euler12.py
842161320
real 1m16.632s
user 1m16.370s
sys 0m0.250s
Python dengan PyPy:
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
Ringkasan:
- C: 100%
- Python: 692% (118% dengan PyPy)
- Erlang: 436% (135% terima kasih untuk RichardC)
- Haskell: 1421%
Saya mengira bahwa C memiliki keuntungan besar karena menggunakan panjang untuk perhitungan dan bukan bilangan bulat panjang sewenang-wenang seperti tiga lainnya. Juga tidak perlu memuat runtime terlebih dahulu (Lakukan yang lain?).
Pertanyaan 1:
Apakah Erlang, Python dan Haskell kehilangan kecepatan karena menggunakan bilangan bulat panjang sewenang-wenang atau tidak mereka asalkan nilainya kurang dari MAXINT
?
Pertanyaan 2: Mengapa Haskell begitu lambat? Apakah ada flag compiler yang mematikan rem atau implementasi saya? (Yang terakhir sangat mungkin karena Haskell adalah buku dengan tujuh meterai bagi saya.)
Pertanyaan 3: Dapatkah Anda menawarkan saya beberapa petunjuk bagaimana mengoptimalkan implementasi ini tanpa mengubah cara saya menentukan faktor-faktornya? Optimalisasi dengan cara apa pun: lebih baik, lebih cepat, lebih "asli" ke bahasa.
EDIT:
Pertanyaan 4: Apakah implementasi fungsional saya memungkinkan LCO (optimasi panggilan terakhir, alias penghapusan rekursi ekor) dan karenanya menghindari menambahkan frame yang tidak perlu ke tumpukan panggilan?
Saya benar-benar mencoba menerapkan algoritma yang sama dengan semirip mungkin dalam empat bahasa, meskipun saya harus mengakui bahwa pengetahuan Haskell dan Erlang saya sangat terbatas.
Kode sumber yang digunakan:
#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
Euler12[x_Integer] := Module[{s = 1}, For[i = 2, DivisorSigma[0, s] < x, i++, s += i]; s]
. Hore!Jawaban:
Menggunakan
GHC 7.0.3
,gcc 4.4.6
,Linux 2.6.29
pada x86_64 Core2 Duo (2.5GHz) mesin, kompilasi menggunakanghc -O2 -fllvm -fforce-recomp
untuk Haskell dangcc -O3 -lm
untuk C.-O3
)-O2
bendera)factorCount'
Kode Anda tidak secara eksplisit diketik dan default keInteger
(terima kasih kepada Daniel untuk memperbaiki kesalahan diagnosis saya di sini!). Memberikan tanda tangan jenis eksplisit (yang merupakan praktik standar) menggunakanInt
dan waktu berubah menjadi 11,1 detikfactorCount'
kamu telah memanggil sia-siafromIntegral
. Perbaikan menghasilkan tidak ada perubahan sekalipun (kompiler pintar, beruntung untuk Anda).mod
tempatrem
yang lebih cepat dan memadai. Ini mengubah waktu menjadi 8,5 detik .factorCount'
terus menerapkan dua argumen tambahan yang tidak pernah berubah (number
,sqrt
). Transformasi pekerja / pembungkus memberi kita:Benar, 7,95 detik . Secara konsisten setengah detik lebih cepat dari solusi C . Tanpa
-fllvm
bendera saya masih mendapatkan8.182 seconds
, jadi backend NCG baik dalam kasus ini juga.Kesimpulan: Haskell luar biasa.
Kode yang dihasilkan
EDIT: Jadi sekarang kita sudah menjelajahi itu, mari kita jawab pertanyaannya
Di Haskell, menggunakan
Integer
lebih lambat daripadaInt
tetapi berapa banyak lebih lambat tergantung pada perhitungan yang dilakukan. Untungnya (untuk mesin 64 bit)Int
sudah cukup. Demi portabilitas, Anda mungkin harus menulis ulang kode saya untuk digunakanInt64
atauWord64
(C bukan satu-satunya bahasa dengan along
).Itulah yang saya jawab di atas. Jawabannya adalah
-O2
rem
tidakmod
(optimasi yang sering dilupakan) danYa, bukan itu masalahnya. Kerja bagus dan senang Anda menganggap ini.
sumber
rem
sebenarnya adalah sub-komponenmod
operasi (mereka tidak sama). Jika Anda melihat di pangkalan GHC Base Anda melihatmod
tes untuk beberapa kondisi dan menyesuaikan tanda sesuai. (lihatmodInt#
diBase.lhs
)mod
denganrem
setelah membaca jawaban ini (heh, oops). Lihat tautan untuk timing saya, tetapi versi singkatnya "hampir identik dengan C".factorCount'
adalah ekor rekursif saya akan berpikir kompiler dapat melihat parameter tambahan tidak diubah dan mengoptimalkan rekursi ekor hanya untuk parameter yang berubah (Haskell menjadi bahasa murni setelah semua, ini seharusnya mudah). Adakah yang mengira kompiler dapat melakukan itu atau haruskah saya kembali untuk membaca lebih banyak makalah teori?Ada beberapa masalah dengan implementasi Erlang. Sebagai garis dasar untuk yang berikut ini, waktu eksekusi terukur saya untuk program Erlang Anda yang tidak dimodifikasi adalah 47,6 detik, dibandingkan dengan 12,7 detik untuk kode C.
Hal pertama yang harus Anda lakukan jika Anda ingin menjalankan kode Erlang intensif komputasi adalah menggunakan kode asli. Kompilasi dengan
erlc +native euler12
mendapat waktu ke 41,3 detik. Namun ini adalah speedup yang jauh lebih rendah (hanya 15%) dari yang diharapkan dari kompilasi asli pada kode semacam ini, dan masalahnya adalah penggunaan Anda-compile(export_all)
. Ini berguna untuk eksperimen, tetapi fakta bahwa semua fungsi berpotensi dapat dijangkau dari luar menyebabkan kompiler asli menjadi sangat konservatif. (Emulator BEAM normal tidak terlalu terpengaruh.) Mengganti deklarasi ini dengan-export([solve/0]).
memberikan speedup yang jauh lebih baik: 31,5 detik (hampir 35% dari baseline).Tetapi kode itu sendiri memiliki masalah: untuk setiap iterasi dalam loop factorCount, Anda melakukan tes ini:
Kode C tidak melakukan ini. Secara umum, mungkin sulit untuk membuat perbandingan yang adil antara implementasi berbeda dari kode yang sama, dan khususnya jika algoritma ini numerik, karena Anda perlu memastikan bahwa mereka benar-benar melakukan hal yang sama. Kesalahan pembulatan sedikit dalam satu implementasi karena beberapa typecast di suatu tempat dapat menyebabkannya melakukan lebih banyak iterasi daripada yang lain meskipun keduanya akhirnya mencapai hasil yang sama.
Untuk menghilangkan kemungkinan sumber kesalahan ini (dan menyingkirkan tes tambahan di setiap iterasi), saya menulis ulang fungsi factorCount sebagai berikut, dimodelkan dengan cermat pada kode C:
Penulisan ulang ini, tidak
export_all
, dan kompilasi asli, memberi saya jangka waktu berikut:yang tidak terlalu buruk dibandingkan dengan kode C:
mengingat bahwa Erlang sama sekali tidak diarahkan untuk menulis kode numerik, karena hanya 50% lebih lambat daripada C pada program seperti ini cukup bagus.
Akhirnya, mengenai pertanyaan Anda:
Pertanyaan 1: Apakah erlang, python dan haskell kehilangan kecepatan karena menggunakan bilangan bulat panjang sewenang-wenang atau tidak mereka asalkan nilainya kurang dari MAXINT?
Ya, agak. Di Erlang, tidak ada cara untuk mengatakan "gunakan aritmatika 32/64-bit dengan wrap-around", jadi kecuali jika kompiler dapat membuktikan beberapa batasan pada bilangan bulat Anda (dan biasanya tidak bisa), ia harus memeriksa semua perhitungan untuk melihat jika mereka dapat memuat satu kata yang ditandai atau jika harus mengubahnya menjadi bignum yang dialokasikan untuk tumpukan. Bahkan jika tidak ada bignum yang pernah digunakan dalam praktek saat runtime, pemeriksaan ini harus dilakukan. Di sisi lain, itu berarti Anda tahu bahwa algoritma tidak akan pernah gagal karena pembungkus integer yang tidak terduga jika Anda tiba-tiba memberikan input yang lebih besar daripada sebelumnya.
Pertanyaan 4: Apakah implementasi fungsional saya mengizinkan LCO dan karenanya menghindari menambahkan frame yang tidak perlu ke dalam tumpukan panggilan?
Ya, kode Erlang Anda sudah benar sehubungan dengan optimasi panggilan terakhir.
sumber
Sehubungan dengan optimasi Python, selain menggunakan PyPy (untuk percepatan yang cukup mengesankan dengan nol perubahan pada kode Anda), Anda bisa menggunakan toolchain terjemahan PyPy untuk mengkompilasi versi yang sesuai dengan RPython, atau Cython untuk membangun modul ekstensi, keduanya yang lebih cepat daripada versi C dalam pengujian saya, dengan modul Cython hampir dua kali lebih cepat . Untuk referensi saya menyertakan hasil benchmark C dan PyPy juga:
C (dikompilasi dengan
gcc -O3 -lm
)PyPy 1.5
RPython (menggunakan revisi PyPy terbaru,
c2f583445aee
)Cython 0.15
Versi RPython memiliki beberapa perubahan utama. Untuk menerjemahkan ke dalam program mandiri, Anda perlu mendefinisikan Anda
target
, yang dalam hal ini adalahmain
fungsinya. Itu diharapkan untuk menerimasys.argv
karena itu hanya argumen, dan diperlukan untuk mengembalikan int. Anda dapat menerjemahkannya dengan menggunakan translate.py,% translate.py euler12-rpython.py
yang diterjemahkan menjadi C dan mengkompilasinya untuk Anda.Versi Cython ditulis ulang sebagai modul ekstensi
_euler12.pyx
, yang saya impor dan panggil dari file python normal. Pada_euler12.pyx
dasarnya sama dengan versi Anda, dengan beberapa deklarasi tipe statis tambahan. Setup.py memiliki boilerplate normal untuk membangun ekstensi, menggunakanpython setup.py build_ext --inplace
.Jujur saya punya sedikit pengalaman dengan RPython atau Cython, dan terkejut dengan hasilnya. Jika Anda menggunakan CPython, menulis bit kode intensif CPU Anda dalam modul ekstensi Cython sepertinya cara yang sangat mudah untuk mengoptimalkan program Anda.
sumber
Implementasi C adalah suboptimal (seperti yang ditunjukkan oleh Thomas M. DuBuisson), versi menggunakan integer 64-bit (yaitu datatype panjang ). Saya akan menyelidiki daftar perakitan nanti, tetapi dengan tebakan yang berpendidikan, ada beberapa akses memori yang terjadi dalam kode yang dikompilasi, yang membuat penggunaan bilangan bulat 64-bit secara signifikan lebih lambat. Itu atau kode yang dihasilkan (baik itu fakta bahwa Anda dapat memasukkan int 64-bit kurang dalam register SSE atau bulat ganda ke integer 64-bit lebih lambat).
Berikut ini adalah kode yang dimodifikasi (cukup ganti panjang dengan int dan saya secara eksplisit menyebutkan faktorCount, meskipun saya tidak berpikir bahwa ini perlu dengan gcc -O3):
Running + timing memberikan:
Sebagai referensi, implementasi haskell oleh Thomas pada jawaban sebelumnya memberikan:
Kesimpulan: Mengambil apa pun dari ghc, kompiler yang hebat, tetapi gcc biasanya menghasilkan kode yang lebih cepat.
sumber
2.5 seconds
sementara modifikasi yang mirip dengan kode Haskell (pindah ke Word32, menambahkan pragma INLINE) menghasilkan runtime dari4.8 seconds
. Mungkin sesuatu dapat dilakukan (bukan trivaily, tampaknya) - hasil gcc tentu mengesankan.Lihatlah blog ini . Sekitar setahun terakhir ini dia telah melakukan beberapa masalah Project Euler di Haskell dan Python, dan dia umumnya menemukan Haskell jauh lebih cepat. Saya pikir antara bahasa-bahasa itu lebih berkaitan dengan kelancaran dan gaya pengkodean Anda.
Ketika datang ke kecepatan Python, Anda menggunakan implementasi yang salah! Coba PyPy , dan untuk hal-hal seperti ini Anda akan merasa jauh lebih cepat.
sumber
Implementasi Haskell Anda dapat dipercepat dengan menggunakan beberapa fungsi dari paket Haskell. Dalam hal ini saya menggunakan primes, yang baru saja diinstal dengan 'cabal install primes';)
Pengaturan waktu:
Program asli Anda:
Peningkatan implementasi
Seperti yang Anda lihat, ini berjalan dalam 38 milidetik pada mesin yang sama dengan yang Anda jalankan dalam 16 detik :)
Perintah kompilasi:
sumber
Hanya untuk bersenang-senang. Berikut ini adalah implementasi Haskell yang lebih 'asli':
Menggunakan
ghc -O3
, ini secara konsisten berjalan dalam 0,55-0,58 detik pada mesin saya (1,73GHz Core i7).Fungsi FactCount yang lebih efisien untuk versi C:
Mengubah rindu menjadi int, menggunakan
gcc -O3 -lm
, ini secara konsisten berjalan dalam 0,31-0,35 detik.Keduanya dapat dibuat untuk berjalan lebih cepat jika Anda mengambil keuntungan dari kenyataan bahwa angka segitiga ke-n = n * (n + 1) / 2, dan n dan (n + 1) memiliki faktorisasi prima yang sama sekali berbeda, sehingga jumlah faktor dari masing-masing setengah dapat dikalikan untuk menemukan jumlah faktor keseluruhan. Pengikut:
akan mengurangi waktu menjalankan kode c menjadi 0,17-0,19 detik, dan dapat menangani pencarian yang jauh lebih besar - lebih dari 10.000 faktor membutuhkan waktu sekitar 43 detik pada mesin saya. Saya meninggalkan speedup mirip haskell ke pembaca yang tertarik.
sumber
Ini tidak mungkin. Saya tidak bisa mengatakan banyak tentang Erlang dan Haskell (well, mungkin sedikit tentang Haskell di bawah) tetapi saya bisa menunjukkan banyak kemacetan lainnya dengan Python. Setiap kali program mencoba untuk menjalankan operasi dengan beberapa nilai dalam Python, itu harus memverifikasi apakah nilai-nilai tersebut berasal dari tipe yang tepat, dan biayanya sedikit waktu.
factorCount
Fungsi Anda hanya mengalokasikan daftar denganrange (1, isquare + 1)
berbagai waktu, dan runtime,malloc
alokasi memori -styled jauh lebih lambat daripada iterasi pada rentang dengan penghitung seperti yang Anda lakukan dalam C. Khususnya,factorCount()
disebut beberapa kali dan karenanya mengalokasikan banyak daftar. Juga, jangan lupa bahwa Python ditafsirkan dan juru bahasa CPython tidak memiliki fokus besar untuk dioptimalkan.EDIT : oh, well, saya perhatikan bahwa Anda menggunakan Python 3 jadi
range()
tidak mengembalikan daftar, tetapi generator. Dalam hal ini, poin saya tentang mengalokasikan daftar adalah setengah salah: fungsi hanya mengalokasikanrange
objek, yang tidak efisien namun tidak seefisien mengalokasikan daftar dengan banyak item.Apakah Anda menggunakan Pelukan ? Pelukan adalah penerjemah yang sangat lambat. Jika Anda menggunakannya, mungkin Anda bisa mendapatkan waktu yang lebih baik dengan GHC - tetapi saya hanya merenungkan hipotesis, jenis hal yang dilakukan oleh kompiler Haskell yang baik di bawah tenda cukup menarik dan jauh melampaui pemahaman saya :)
Saya akan mengatakan Anda memainkan game yang tidak lucu. Bagian terbaik dari mengetahui berbagai bahasa adalah menggunakannya dengan cara yang paling berbeda :) Tapi saya ngelantur, saya tidak punya rekomendasi untuk poin ini. Maaf, saya harap seseorang dapat membantu Anda dalam hal ini :)
Sejauh yang saya ingat, Anda hanya perlu memastikan bahwa panggilan rekursif Anda adalah perintah terakhir sebelum mengembalikan nilai. Dengan kata lain, fungsi seperti yang di bawah ini dapat menggunakan pengoptimalan tersebut:
Namun, Anda tidak akan memiliki optimasi seperti itu jika fungsi Anda seperti di bawah ini, karena ada operasi (perkalian) setelah panggilan rekursif:
Saya memisahkan operasi di beberapa variabel lokal untuk memperjelas operasi mana yang dijalankan. Namun, yang paling umum adalah melihat fungsi-fungsi ini seperti di bawah ini, tetapi mereka setara dengan poin yang saya buat:
Perhatikan bahwa terserah kompiler / juru bahasa untuk memutuskan apakah akan membuat rekursi ekor. Sebagai contoh, interpreter Python tidak melakukannya jika saya ingat dengan baik (saya menggunakan Python dalam contoh saya hanya karena sintaksnya yang lancar). Lagi pula, jika Anda menemukan hal-hal aneh seperti fungsi faktorial dengan dua parameter (dan salah satu parameter memiliki nama seperti
acc
,accumulator
dll.) Sekarang Anda tahu mengapa orang melakukannya :)sumber
Dengan Haskell, Anda benar-benar tidak perlu berpikir dalam rekursi secara eksplisit.
Dalam kode di atas, saya telah mengganti rekursi eksplisit dalam jawaban @ Thomas dengan operasi daftar umum. Kode masih melakukan hal yang persis sama tanpa kita khawatir tentang rekursi ekor. Ini berjalan (~ 7.49s ) sekitar 6% lebih lambat dari versi dalam jawaban @ Thomas (~ 7.04s ) pada mesin saya dengan GHC 7.6.2, sedangkan versi C dari @Raedwulf berjalan ~ 3.15s . Tampaknya GHC telah meningkat dari tahun ke tahun.
PS. Saya tahu ini adalah pertanyaan lama, dan saya menemukannya dari pencarian google (saya lupa apa yang saya cari, sekarang ...). Hanya ingin mengomentari pertanyaan tentang LCO dan mengungkapkan perasaan saya tentang Haskell secara umum. Saya ingin mengomentari jawaban teratas, tetapi komentar tidak mengizinkan blok kode.
sumber
Beberapa angka lagi dan penjelasan untuk versi C. Rupanya tidak ada yang melakukannya selama bertahun-tahun. Ingatlah untuk meningkatkan jawaban ini sehingga dapat menjadi yang teratas bagi semua orang untuk melihat dan belajar.
Langkah Satu: Tolok ukur program penulis
Spesifikasi Laptop:
Perintah:
.
Nama file adalah:
integertype_architecture_compiler.exe
Langkah Dua: Selidiki, Tingkatkan, dan Tolok Ukur Lagi
VS adalah 250% lebih cepat dari gcc. Kedua kompiler harus memberikan kecepatan yang sama. Jelas, ada yang salah dengan kode atau opsi kompilator. Mari selidiki!
Poin pertama yang menarik adalah tipe integer. Konversi dapat menjadi mahal dan konsistensi penting untuk menghasilkan & mengoptimalkan kode yang lebih baik. Semua bilangan bulat harus bertipe sama.
Ini berantakan
int
danlong
sekarang. Kami akan memperbaikinya. Jenis apa yang digunakan? Tercepat. Harus benchmark mereka semua!Jenis integer berasal
int
long
int32_t
uint32_t
int64_t
danuint64_t
dari#include <stdint.h>
Ada BANYAK jenis integer di C, ditambah beberapa yang ditandatangani / tidak ditandatangani untuk dimainkan, ditambah pilihan untuk mengkompilasi sebagai x86 atau x64 (jangan bingung dengan ukuran integer yang sebenarnya). Itu banyak versi untuk dikompilasi dan dijalankan ^^
Langkah Tiga: Memahami Angka
Kesimpulan pasti:
Pertanyaan tipuan: "Berapa ukuran int dan panjang dalam C?"
Jawaban yang benar adalah: Ukuran int dan panjang dalam C tidak terdefinisi dengan baik!
Dari spesifikasi C:
Dari halaman manual gcc (flag -m32 dan -m64):
Dari dokumentasi MSDN (Rentang Tipe Data) https://msdn.microsoft.com/en-us/library/s3f49ktz%28v=vs.110%29.aspx :
Untuk Menyimpulkan Ini: Pelajaran Yang Dipetik
Bilangan bulat 32 bit lebih cepat daripada bilangan bulat 64 bit.
Jenis bilangan bulat standar tidak didefinisikan dengan baik dalam C atau C ++, mereka bervariasi tergantung pada kompiler dan arsitektur. Ketika Anda membutuhkan konsistensi dan prediktabilitas, gunakan
uint32_t
keluarga integer dari#include <stdint.h>
.Masalah kecepatan terpecahkan. Semua bahasa lain kembali ketinggalan ratusan persen, C & C ++ menang lagi! Mereka selalu melakukannya. Perbaikan selanjutnya akan multithreading menggunakan OpenMP: D
sumber
INT_MIN
danINT_MAX
(-32767 dan 32767, yang secara praktis memaksakan persyaratan yangint
setidaknya 16 bit).long
harus paling tidak sebesarint
, dan ratalong
- rata persyaratan kisaran setidaknya 32 bit.Melihat implementasi Erlang Anda. Waktunya sudah termasuk memulai dari seluruh mesin virtual, menjalankan program Anda dan menghentikan mesin virtual. Saya cukup yakin bahwa pengaturan dan penghentian erlang vm membutuhkan waktu.
Jika waktunya dilakukan di dalam mesin virtual erlang itu sendiri, hasilnya akan berbeda karena dalam kasus itu kami akan memiliki waktu yang sebenarnya untuk hanya program yang dimaksud. Kalau tidak, saya percaya bahwa total waktu yang dibutuhkan oleh proses memulai dan memuat Erlang Vm plus penghentian itu (seperti yang Anda masukkan ke dalam program Anda) semuanya termasuk dalam total waktu yang metode yang Anda gunakan untuk mengatur waktu program menghasilkan. Pertimbangkan untuk menggunakan erlang timing itu sendiri yang kami gunakan saat kami ingin mengatur waktu program kami di dalam mesin virtual itu sendiri
timer:tc/1 or timer:tc/2 or timer:tc/3
. Dengan cara ini, hasil dari erlang akan mengecualikan waktu yang dibutuhkan untuk memulai dan menghentikan / membunuh / menghentikan mesin virtual. Itulah alasan saya di sana, pikirkanlah, dan kemudian coba tanda bangku Anda lagi.Saya sebenarnya menyarankan agar kami mencoba mengatur waktu program (untuk bahasa yang memiliki runtime), di dalam runtime bahasa tersebut untuk mendapatkan nilai yang tepat. C misalnya tidak memiliki overhead untuk memulai dan mematikan sistem runtime seperti halnya Erlang, Python dan Haskell (98% yakin akan hal ini - saya tahan koreksi). Jadi (berdasarkan alasan ini) saya menyimpulkan dengan mengatakan bahwa tolok ukur ini tidak tepat / cukup adil untuk bahasa yang berjalan di atas sistem runtime. Mari kita lakukan lagi dengan perubahan ini.
EDIT: selain itu bahkan jika semua bahasa memiliki sistem runtime, overhead memulai masing-masing dan menghentikannya akan berbeda. jadi saya sarankan kita waktu dari dalam sistem runtime (untuk bahasa yang ini berlaku). Erlang VM dikenal memiliki overhead yang cukup saat start up!
sumber
Pertanyaan satu dapat dijawab dalam negatif untuk Erlang. Pertanyaan terakhir dijawab dengan menggunakan Erlang dengan tepat, seperti pada:
http://bredsaal.dk/learning-erlang-using-projecteuler-net
Karena ini lebih cepat daripada contoh C awal Anda, saya kira ada banyak masalah karena yang lain sudah dibahas secara rinci.
Modul Erlang ini dijalankan pada netbook murah dalam waktu sekitar 5 detik ... Menggunakan model jaringan thread di erlang dan, dengan demikian menunjukkan bagaimana cara mengambil keuntungan dari model acara. Itu dapat didistribusikan melalui banyak node. Dan itu cepat. Bukan kode saya.
Tes di bawah ini berlangsung pada: Intel (R) Atom (TM) CPU N270 @ 1.60GHz
sumber
C ++ 11, <20ms untuk saya - Jalankan di sini
Saya mengerti bahwa Anda ingin kiat untuk membantu meningkatkan pengetahuan khusus bahasa Anda, tetapi karena itu telah dibahas dengan baik di sini, saya pikir saya akan menambahkan beberapa konteks untuk orang-orang yang mungkin telah melihat komentar matematik pada pertanyaan Anda, dll, dan bertanya-tanya mengapa ini kode jauh lebih lambat.
Jawaban ini terutama untuk memberikan konteks agar semoga membantu orang mengevaluasi kode dalam pertanyaan Anda / jawaban lain dengan lebih mudah.
Kode ini hanya menggunakan beberapa optimisasi (jelek), tidak terkait dengan bahasa yang digunakan, berdasarkan:
Itu memakan waktu rata-rata sekitar 19 ms untuk desktop saya dan 80 ms untuk laptop saya, jauh dari kebanyakan kode lain yang saya lihat di sini. Dan tidak diragukan lagi, masih banyak optimisasi yang tersedia.
sumber
Mencoba GO:
Saya mendapat:
versi c asli: 9.1690 100%
go: 8.2520 111%
Tetapi menggunakan:
Saya mendapat:
versi c asli: 9.1690 100%
versi c thaumkid: 0.1060 8650%
versi go pertama: 8.2520 111%
versi go kedua: 0.0230 39865%
Saya juga mencoba Python3.6 dan pypy3.3-5.5-alpha:
versi c asli: 8.629 100%
versi c parakid: 0.109 7916%
Python3.6: 54.795 16%
pypy3.3-5.5-alpha: 13.291 65%
dan kemudian dengan kode berikut saya dapat:
versi c asli: 8.629 100%
versi c thaumkid: 0.109 8650%
Python3.6: 1.489 580%
pypy3.3-5.5-alpha: 0.582 1483%
sumber
Perubahan:
case (divisor(T,round(math:sqrt(T))) > 500) of
Untuk:
case (divisor(T,round(math:sqrt(T))) > 1000) of
Ini akan menghasilkan jawaban yang benar untuk contoh multi-proses Erlang.
sumber
Saya membuat asumsi bahwa jumlah faktor hanya besar jika jumlah yang terlibat memiliki banyak faktor kecil. Jadi saya menggunakan algoritma thaumkid yang sangat baik, tetapi pertama-tama menggunakan perkiraan jumlah faktor yang tidak pernah terlalu kecil. Ini cukup sederhana: Periksa faktor prima hingga 29, lalu periksa angka yang tersisa dan hitung batas atas untuk nmber faktor. Gunakan ini untuk menghitung batas atas untuk sejumlah faktor, dan jika angka itu cukup tinggi, hitung jumlah faktor yang tepat.
Kode di bawah ini tidak memerlukan asumsi ini untuk kebenaran, tetapi untuk menjadi cepat. Tampaknya berhasil; hanya sekitar satu dari 100.000 angka yang memberikan perkiraan yang cukup tinggi sehingga memerlukan pemeriksaan penuh.
Berikut kodenya:
Hal ini menemukan segitiga 14.753.024 dengan 13824 faktor dalam waktu sekitar 0,7 detik, angka segitiga 879.207.615 dengan 61.440 faktor dalam 34 detik, angka segitiga 12.524.486.975 dengan 138.240 faktor dalam 10 menit 5 detik, dan 26.467.792.064 angka segitiga dengan 172.032 faktor dalam 21 menit 25 detik (2.4GHz Core2 Duo), jadi kode ini rata-rata hanya membutuhkan 116 siklus prosesor per angka. Angka segitiga terakhir itu sendiri lebih besar dari 2 ^ 68, jadi
sumber
Saya mengubah versi "Jannich Brendle" menjadi 1000, bukannya 500. Dan mencantumkan hasil euler12.bin, euler12.erl, p12dist.erl. Kedua kode erl menggunakan '+ asli' untuk dikompilasi.
sumber
gcc -lm -Ouler euler.c
waktu ./a.out
2,79 pengguna 0,00 sistem sistem 99% cpu 2,794 total
sumber