Я взял задачу № 12 от Project Euler как упражнение по программированию и сравнил свои (безусловно, не оптимальные) реализации на C, Python, Erlang и Haskell. Чтобы получить большее время выполнения, я ищу первый номер треугольника с более чем 1000 делителями вместо 500, как указано в исходной задаче.
Результат следующий:
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 с 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
Резюме:
- C: 100%
- Python: 692% (118% с PyPy)
- Erlang: 436% (135% благодаря RichardC)
- Haskell: 1421%
Я предполагаю, что C имеет большое преимущество, так как он использует long для вычислений, а не произвольные целые числа длины, как остальные три. Также не нужно сначала загружать среду выполнения (другие?).
Вопрос 1:
теряют ли Erlang, Python и Haskell скорость из-за использования целых чисел произвольной длины или нет, если значения меньше MAXINT
?
Вопрос 2: Почему Haskell такой медленный? Есть флаг компилятора, который отключает тормоза, или это моя реализация? (Последнее вполне вероятно, поскольку Хаскелл для меня - книга с семью печатями.)
Вопрос 3: Можете ли вы дать мне несколько советов, как оптимизировать эти реализации, не меняя способ определения факторов? Оптимизация любым способом: приятнее, быстрее, более «родной» для языка.
РЕДАКТИРОВАТЬ:
Вопрос 4: разрешают ли мои функциональные реализации LCO (оптимизация последнего вызова, устранение хвостовой рекурсии) и, следовательно, избегают добавления ненужных кадров в стек вызовов?
Я действительно пытался реализовать один и тот же алгоритм, насколько это возможно, на четырех языках, хотя я должен признать, что мои знания Хаскелла и Эрланга очень ограничены.
Используемые исходные коды:
#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]
. Ура!Ответы:
Используя
GHC 7.0.3
,gcc 4.4.6
,Linux 2.6.29
на x86_64 Core2 Duo (2,5 ГГц) машины, компиляции , используяghc -O2 -fllvm -fforce-recomp
для Haskell иgcc -O3 -lm
для C.-O3
)-O2
флага)factorCount'
код не напечатан явно и по умолчаниюInteger
(спасибо Дэниелу за исправление моего неправильного диагноза здесь!). Предоставление явной подписи типа (что в любом случае является стандартной практикой) с использованиемInt
времени, изменяющегося на 11,1 секунды.factorCount'
тебя без надобности позвонилиfromIntegral
. Исправление не приводит к изменениям (компилятор умен, вам повезло).mod
гдеrem
быстрее и достаточно. Это изменяет время до 8,5 секунд .factorCount'
постоянно применяет два дополнительных аргумента, которые никогда не меняются (number
,sqrt
). Преобразование рабочий / оболочка дает нам:Это верно, 7,95 секунды . Последовательно полсекунды быстрее , чем решение C . Без
-fllvm
флага я все еще получаю8.182 seconds
, так что бэкэнд NCG хорошо себя чувствует и в этом случае.Вывод: Haskell потрясающий.
Результирующий код
РЕДАКТИРОВАТЬ: Итак, теперь, когда мы это изучили, давайте рассмотрим вопросы
В Haskell использование
Integer
медленнее, чем,Int
но насколько медленнее, зависит от выполненных вычислений. К счастью (для 64-битных машин)Int
достаточно. Ради переносимости вам, вероятно, следует переписать мой код для использованияInt64
илиWord64
(C не единственный язык сlong
).Это было то, что я ответил выше. Ответ был
-O2
rem
нетmod
(часто забытая оптимизация) иДа, это не проблема. Хорошая работа и рада, что вы обдумали это.
источник
rem
что на самом деле является подкомпонентомmod
операции (они не одинаковы). Если вы заглянете в базовую библиотеку GHC, вы увидитеmod
тесты для нескольких условий и соответственно измените знак. (см.modInt#
вBase.lhs
)mod
сrem
после прочтения этого ответа (Хех, ой). Смотрите ссылку на мои сроки, но короткая версия "почти идентична C".factorCount'
был хвостовой рекурсией, я бы подумал, что компилятор может обнаружить дополнительные параметры, которые не были изменены, и оптимизировать хвостовую рекурсию только для меняющихся параметров (в конце концов, Haskell - чистый язык, это должно быть легко). Кто-нибудь думает, что компилятор может сделать это, или я должен вернуться, чтобы прочитать больше теоретических работ?Есть некоторые проблемы с реализацией Erlang. В качестве базового показателя для следующего, мое измеренное время выполнения вашей немодифицированной программы Erlang составило 47,6 секунды, по сравнению с 12,7 секундами для кода C.
Первое, что вы должны сделать, если хотите запустить вычислительный код Erlang, это использовать нативный код. Компиляция с
erlc +native euler12
полученным временем до 41,3 секунд. Это, однако, намного более низкое ускорение (всего 15%), чем ожидалось от нативной компиляции для такого рода кода, и проблема заключается в том, как вы его используете-compile(export_all)
. Это полезно для экспериментов, но тот факт, что все функции потенциально доступны извне, приводит к тому, что нативный компилятор очень консервативен. (Обычный эмулятор BEAM не так сильно затронут.) Замена этого объявления-export([solve/0]).
дает гораздо лучшее ускорение: 31,5 секунды (почти 35% от базовой линии).Но сам код имеет проблему: для каждой итерации в цикле factorCount вы выполняете этот тест:
Код C не делает этого. В общем, может быть сложно провести справедливое сравнение между различными реализациями одного и того же кода, и в частности, если алгоритм является числовым, потому что вы должны быть уверены, что они на самом деле делают одно и то же. Небольшая ошибка округления в одной реализации из-за некоторой передачи типа где-то может привести к тому, что она выполнит намного больше итераций, чем другая, даже если обе в конечном итоге достигнут одного и того же результата.
Чтобы устранить этот возможный источник ошибок (и избавиться от дополнительного теста в каждой итерации), я переписал функцию factorCount следующим образом, тесно смоделированный на коде C:
Эта перезапись, нет
export_all
, и нативная компиляция, дали мне следующее время выполнения:что не так уж плохо по сравнению с кодом C:
учитывая, что Эрланг вовсе не ориентирован на написание числового кода, он на 50% медленнее, чем C в такой программе, как это, довольно хорошо.
Наконец, по поводу ваших вопросов:
Вопрос 1: Erlang, python и haskell теряют скорость из-за использования целых чисел произвольной длины или нет, если значения меньше MAXINT?
Да, немного. В Erlang нет никакого способа сказать «использовать 32/64-битную арифметику с циклическим изменением», поэтому, если компилятор не может доказать некоторые ограничения для ваших целых чисел (а это обычно не может), он должен проверить все вычисления, чтобы увидеть могут ли они вписаться в одно помеченное слово или если оно должно превратить их в бигнумы, выделенные для кучи. Даже если на практике во время выполнения не используются никакие бигнумы, эти проверки должны быть выполнены. С другой стороны, это означает, что вы знаете, что алгоритм никогда не выйдет из строя из-за неожиданного целочисленного переноса, если вы вдруг дадите ему больше входных данных, чем раньше.
Вопрос 4: разрешают ли мои функциональные реализации LCO и, следовательно, избегают добавления ненужных кадров в стек вызовов?
Да, ваш код Erlang верен в отношении оптимизации последнего вызова.
источник
Что касается оптимизации Python, в дополнение к использованию PyPy (для довольно впечатляющего ускорения с нулевым изменением кода), вы можете использовать набор инструментов перевода PyPy для компиляции версии, совместимой с RPython, или Cython для создания модуля расширения. которые быстрее, чем версия C в моем тестировании, с модулем Cython почти в два раза быстрее . Для справки я также включил результаты тестов C и PyPy:
C (составлено с
gcc -O3 -lm
)PyPy 1.5
RPython (используя последнюю версию PyPy,
c2f583445aee
)Cython 0,15
Версия RPython имеет несколько ключевых изменений. Чтобы перевести в отдельную программу, вам нужно определить свою
target
, которая в данном случае являетсяmain
функцией. Ожидается,sys.argv
что он будет единственным аргументом и должен возвращать int. Вы можете перевести его, используя translate.py,% translate.py euler12-rpython.py
который переводит на C и компилирует его для вас.Версия Cython была переписана как модуль расширения
_euler12.pyx
, который я импортирую и вызываю из обычного файла Python. По_euler12.pyx
сути, это то же самое, что и ваша версия, с некоторыми дополнительными статическими объявлениями типов. У setup.py есть нормальный шаблон для построения расширения, используяpython setup.py build_ext --inplace
.Честно говоря, у меня очень мало опыта работы с RPython или Cython, и я был приятно удивлен результатами. Если вы используете CPython, запись кода, интенсивно использующего процессор, в модуле расширения Cython кажется действительно простым способом оптимизации вашей программы.
источник
Реализация C является неоптимальной (как намекает Томас М. Дюбюссон), в версии используются 64-битные целые числа (то есть тип данных long ). Я расскажу о листинге сборки позже, но, опираясь на осознанное предположение, в скомпилированном коде происходит несколько обращений к памяти, которые значительно замедляют использование 64-битных целых чисел. Это тот или иной сгенерированный код (будь то факт, что вы можете поместить меньше 64-битных целых в регистр SSE или округлить от двойного до 64-битного целого числа медленнее).
Вот модифицированный код (просто замените long на int, и я явно указал factorCount, хотя я не думаю, что это необходимо с gcc -O3):
Бег + время дает:
Для справки, реализация haskell Томаса в предыдущем ответе дает:
Вывод: ничего не забирая у ghc, это отличный компилятор, но gcc обычно генерирует более быстрый код.
источник
2.5 seconds
то время как аналогичная модификация кода на Haskell (переход на Word32, добавление прагмы INLINE) приводит к времени выполнения4.8 seconds
. Возможно, что-то можно сделать (кажется, не тривиально) - результат gcc, безусловно, впечатляет.Посмотрите на этот блог . За последний год или около того он сделал несколько проблем с Project Euler в Haskell и Python, и он, как правило, обнаружил, что Haskell работает намного быстрее. Я думаю, что между этими языками это больше связано с вашей беглостью и стилем кодирования.
Что касается скорости Python, вы используете неправильную реализацию! Попробуйте PyPy , и для таких вещей вы обнаружите, что это будет намного быстрее.
источник
Ваша реализация на Haskell может быть значительно ускорена за счет использования некоторых функций из пакетов Haskell. В этом случае я использовал простые числа, которые просто устанавливаются с помощью 'cabal install primes';)
Тайминги:
Ваша оригинальная программа:
Улучшенная реализация
Как вы можете видеть, этот работает за 38 миллисекунд на той же машине, где ваш работал за 16 секунд :)
Команды компиляции:
источник
Просто для удовольствия. Ниже приведена более «родная» реализация Haskell:
Используя
ghc -O3
, это последовательно работает в 0,55-0,58 секунды на моей машине (1,73 ГГц Core i7).Более эффективная функция factorCount для версии C:
Изменяя long в ints в main, используя
gcc -O3 -lm
, это последовательно выполняется за 0,31-0,35 секунды.И то, и другое можно заставить работать еще быстрее, если вы воспользуетесь преимуществом того факта, что номер n-го треугольника = n * (n + 1) / 2, а n и (n + 1) имеют совершенно несопоставимые простые факторизации, поэтому число факторов каждой половины можно умножить, чтобы найти количество факторов целого. Последующий:
сократит время выполнения кода c до 0,17–0,19 секунды, и он сможет обрабатывать гораздо большие запросы - более 10000 факторов занимает около 43 секунд на моей машине. Я оставляю аналогичное ускорение haskell для заинтересованного читателя.
источник
Это маловероятно. Я не могу много сказать об Эрланге и Хаскелле (ну, может, немного о Хаскелле ниже), но я могу указать много других узких мест в Python. Каждый раз, когда программа пытается выполнить операцию с некоторыми значениями в Python, она должна проверить, соответствуют ли значения правильному типу, и это стоит немного времени. Ваша
factorCount
функция просто выделяет список сrange (1, isquare + 1)
разным временем, аmalloc
выделение памяти в стиле времени выполнения намного медленнее, чем итерация по диапазону со счетчиком, как этоfactorCount()
делается в C. В частности, он вызывается несколько раз и поэтому выделяет много списков. Кроме того, давайте не будем забывать, что Python интерпретируется, а интерпретатор CPython не уделяет большого внимания оптимизации.РЕДАКТИРОВАТЬ : о хорошо, я отмечаю, что вы используете Python 3, поэтому
range()
не возвращает список, но генератор. В этом случае моя точка зрения о распределении списков наполовину неверна: функция просто выделяетrange
объекты, которые, тем не менее, неэффективны, но не настолько неэффективны, как распределение списка с большим количеством элементов.Вы используете Hugs ? Объятия - довольно медленный переводчик. Если вы используете его, возможно, вы сможете лучше провести время с GHC - но я только размышляю о гипотезе, то, что хороший компилятор Haskell делает под капотом, довольно увлекательно и выходит за рамки моего понимания :)
Я бы сказал, что вы играете в несмешную игру. Лучшая часть знания различных языков - это использовать их самыми разными способами :) Но я отвлекся, у меня просто нет рекомендаций по этому вопросу. Извините, я надеюсь, что кто-то может помочь вам в этом случае :)
Насколько я помню, вам просто нужно убедиться, что ваш рекурсивный вызов является последней командой перед возвратом значения. Другими словами, функция, подобная приведенной ниже, может использовать такую оптимизацию:
Однако у вас не было бы такой оптимизации, если бы ваша функция была такой, как приведенная ниже, потому что после рекурсивного вызова есть операция (умножение):
Я разделил операции в некоторых локальных переменных, чтобы было понятно, какие операции выполняются. Тем не менее, самое обычное состоит в том, чтобы увидеть эти функции, как показано ниже, но они эквивалентны для пункта, который я делаю:
Обратите внимание, что именно компилятор / интерпретатор должен решить, будет ли он выполнять хвостовую рекурсию. Например, интерпретатор Python не делает этого, если я хорошо помню (я использовал Python в своем примере только из-за его свободного синтаксиса). В любом случае, если вы найдете странные вещи, такие как факторные функции с двумя параметрами (и у одного из параметров есть имена, такие как
acc
иaccumulator
т. Д.), Теперь вы знаете, почему люди делают это :)источник
С Haskell вам действительно не нужно явно думать о рекурсиях.
В приведенном выше коде я заменил явные рекурсии в ответе @Thomas на общие операции со списком. Код все еще делает то же самое, не беспокоясь о хвостовой рекурсии. Он работает (~ 7,49 с ) примерно на 6% медленнее, чем версия из ответа @Thomas (~ 7,04 с ), на моей машине с GHC 7.6.2, в то время как версия C из @Raedwulf работает ~ 3,15 с . Кажется, что GHC улучшилось за год.
PS. Я знаю, что это старый вопрос, и я наткнулся на него из поисков Google (я забыл, что я искал, сейчас ...). Просто хотел прокомментировать вопрос о LCO и выразить свои чувства по поводу Haskell в целом. Я хотел прокомментировать верхний ответ, но комментарии не позволяют блоки кода.
источник
Еще несколько цифр и объяснений для версии C. Очевидно, никто не делал этого в течение всех этих лет. Не забудьте высказать этот ответ, чтобы он мог быть на вершине, чтобы все могли видеть и учиться.
Шаг первый: эталон программ автора
Характеристики ноутбука:
Команды:
,
Имена файлов:
integertype_architecture_compiler.exe
Шаг второй: исследовать, улучшать и снова тестировать
VS на 250% быстрее, чем gcc. Два компилятора должны дать одинаковую скорость. Очевидно, что-то не так с кодом или параметрами компилятора. Давайте исследуем!
Первый интерес представляет целочисленные типы. Преобразования могут быть дорогими, а согласованность важна для лучшей генерации и оптимизации кода. Все целые числа должны быть одного типа.
Это смешанный беспорядок
int
иlong
прямо сейчас. Мы собираемся улучшить это. Какой тип использовать? Быстрейший. Должен сравнять их все!Целочисленные типы есть
int
long
int32_t
uint32_t
int64_t
иuint64_t
от#include <stdint.h>
Есть много целочисленных типов в C, плюс некоторые со знаком / без знака для игры, плюс возможность компилировать как x86 или x64 (не путать с фактическим целочисленным размером). Это много версий для компиляции и запуска ^^
Шаг третий: понимание чисел
Окончательные выводы:
Вопрос с подвохом: «Каковы размеры int и long в C?»
Правильный ответ: размер int и long в C не определены четко!
Из спецификации C:
Со страницы руководства gcc (флаги -m32 и -m64):
Из документации MSDN (диапазоны типов данных) https://msdn.microsoft.com/en-us/library/s3f49ktz%28v=vs.110%29.aspx :
В заключение: извлеченные уроки
32-разрядные целые числа быстрее, чем 64-разрядные целые числа.
Стандартные целочисленные типы плохо определены в C и C ++, они различаются в зависимости от компиляторов и архитектур. Когда вам нужна последовательность и предсказуемость, используйте
uint32_t
целочисленное семейство из#include <stdint.h>
.Проблемы со скоростью решены. Все остальные языки отстают на сотни процентов, C & C ++ снова побеждает! Они всегда делают. Следующим улучшением будет многопоточность с использованием OpenMP: D
источник
INT_MIN
иINT_MAX
(-32767 и 32767, которые практически накладывают требование, котороеint
должно быть не менее 16 бит).long
должно быть как минимум такого же размера, какint
и значение диапазона должно бытьlong
не менее 32 бит.Глядя на вашу реализацию Erlang. Время включает в себя запуск всей виртуальной машины, запуск вашей программы и остановку виртуальной машины. Я уверен, что установка и остановка erlang vm займет некоторое время.
Если бы время было выполнено в самой виртуальной машине erlang, результаты были бы другими, так как в этом случае у нас было бы реальное время только для рассматриваемой программы. В противном случае, я считаю, что общее время, затрачиваемое на процесс запуска и загрузки Erlang Vm плюс время его остановки (как вы положили это в своей программе), все включено в общее время, которое метод, который вы используете, чтобы рассчитать время Программа выводит. Рассмотрите возможность использования времени erlang, которое мы используем, когда хотим синхронизировать наши программы с самой виртуальной машиной
timer:tc/1 or timer:tc/2 or timer:tc/3
. Таким образом, результаты erlang исключают время, необходимое для запуска и остановки / остановки / остановки виртуальной машины. Это мое рассуждение, подумайте об этом, а затем снова попробуйте свой тест.На самом деле я предлагаю, чтобы мы попытались синхронизировать программу (для языков, которые имеют время выполнения), в пределах времени выполнения этих языков, чтобы получить точное значение. C, например, не имеет никаких издержек запуска и выключения системы времени выполнения, как это делают Erlang, Python и Haskell (98% уверены в этом - я исправляюсь). Поэтому (основываясь на этом рассуждении) я в заключение говорю, что этот тест был недостаточно точным / справедливым для языков, работающих поверх системы времени выполнения. Давайте сделаем это снова с этими изменениями.
РЕДАКТИРОВАТЬ: кроме того, даже если бы все языки имели системы времени выполнения, накладные расходы на запуск каждого из них и его остановку отличались бы. поэтому я предлагаю время изнутри систем времени исполнения (для языков, для которых это применимо). Erlang VM, как известно, имеет значительные накладные расходы при запуске!
источник
На первый вопрос можно ответить отрицательно для Эрланга. На последний вопрос отвечаем, используя Erlang соответствующим образом, как в:
http://bredsaal.dk/learning-erlang-using-projecteuler-net
Так как это быстрее, чем ваш первоначальный пример C, я бы предположил, что есть множество проблем, которые другие уже подробно рассмотрели.
Этот модуль Erlang выполняется на дешевом нетбуке примерно за 5 секунд ... Он использует модель сетевых потоков в erlang и, как таковой, демонстрирует, как воспользоваться моделью событий. Это может быть распределено по многим узлам. И это быстро. Не мой код
Приведенный ниже тест проводился на: Intel (R) Atom (TM) CPU N270 @ 1.60GHz
источник
C ++ 11, <20 мс для меня - запустите здесь
Я понимаю, что вам нужны советы, которые помогут улучшить ваши знания по конкретному языку, но, поскольку это хорошо освещено здесь, я подумал, что хотел бы добавить некоторый контекст для людей, которые, возможно, посмотрели комментарий mathematica по вашему вопросу и т. Д., И задавался вопросом, почему это код был намного медленнее.
Этот ответ в основном предназначен для обеспечения контекста, который, как мы надеемся, поможет людям легче оценить код вашего вопроса / других ответов.
Этот код использует только несколько (некрасивых) оптимизаций, не связанных с используемым языком, на основе:
Это занимает в среднем около 19 мс для моего настольного компьютера и 80 мс для моего ноутбука, что сильно отличается от большинства других кодов, которые я видел здесь. И есть, без сомнения, много оптимизаций еще доступны.
источник
Попытка GO:
Я получил:
оригинальная версия c: 9.1690 100%
go: 8.2520 111%
Но используя:
Я получил:
оригинальная версия c: 9.1690 версия c 100%
thaumkid: 0.1060 8650%
версия первого хода: 8.2520 111%
вторая версия go: 0.0230 39865%
Я также попробовал Python3.6 и pypy3.3-5.5-alpha:
оригинальная c версия: 8.629 100%
thaumkid's c версия: 0.109 7916%
Python3.6: 54.795 16%
pypy3.3-5.5-alpha: 13.291 65%
а затем со следующим кодом я получил:
оригинальная c версия: 8.629 100%
thaumkid's c версия: 0.109 8650%
Python3.6: 1.489 580%
pypy3.3-5.5-alpha: 0.582 1483%
источник
Изменить:
case (divisor(T,round(math:sqrt(T))) > 500) of
Для того, чтобы:
case (divisor(T,round(math:sqrt(T))) > 1000) of
Это даст правильный ответ для примера с несколькими процессами Erlang.
источник
Я сделал предположение, что число факторов велико только в том случае, если в числе участвующих факторов много мелких факторов. Поэтому я использовал отличный алгоритм thaumkid, но сначала использовал приближение к числу факторов, которое никогда не бывает слишком маленьким. Все довольно просто: проверьте простые факторы до 29, затем проверьте оставшееся число и вычислите верхнюю границу для числа факторов. Используйте это, чтобы вычислить верхнюю границу для числа факторов, и, если это число достаточно велико, рассчитайте точное количество факторов.
Приведенный ниже код не нуждается в этом допущении для правильности, но чтобы быть быстрым. Это похоже на работу; только один из 100 000 номеров дает оценку, которая достаточно высока, чтобы потребовать полной проверки.
Вот код:
Это находит 14 753 024-й треугольник с 13824 факторами за 0,7 секунды, 879 207 615-й треугольный номер с 61 440 факторами за 34 секунды, 12 524 486 975-й треугольный номер с 138 240 факторами за 10 минут 5 секунд и 26 467 792 064-й треугольный номер с 172 032 факторами в 21 минута 25 секунд (2,4 ГГц Core2 Duo), поэтому этот код в среднем занимает всего 116 циклов процессора на число. Последнее само треугольное число больше 2 ^ 68, поэтому
источник
Я изменил версию «Jannich Brendle» на 1000 вместо 500. И перечислим результат euler12.bin, euler12.erl, p12dist.erl. Оба кода erl для компиляции используют «+ native».
источник
gcc -lm -Ofast euler.c
время ./a.out
2.79s пользователь 0.00s система 99% процессор 2.794 всего
источник