关于并发:并发Prime Generator

关于并发:并发Prime Generator

Concurrent Prime Generator

我正在研究projecteuler.net上的问题,以学习如何在Erlang中进行编程,而我最难的是创建一个素数生成器,该生成器可以在不到一分钟的时间内创建所有200万以下的素数。 使用顺序样式,我已经编写了三种类型的生成器,包括Eratosthenes的Sieve,但是它们中的任何一个都不能表现良好。

我认为并发的Sieve可以很好地工作,但是我收到bad_arity消息,而且不确定为什么。 关于我为什么有问题或如何正确编码的任何建议?

这是我的代码,注释掉的部分是我尝试使事情并发的地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
-module(primeserver).
-compile(export_all).

start() ->
    register(primes, spawn(fun() -> loop() end)).

is_prime(N) -> rpc({is_prime,N}).

rpc(Request) ->
    primes ! {self(), Request},
    receive
        {primes, Response} ->
            Response
    end.

loop() ->
    receive
        {From, {is_prime, N}} ->
            if
                N  From ! {primes, false};
                N =:= 2 -> From ! {primes, true};
                N rem 2 =:= 0 -> From ! {primes, false};
                true ->
                    Values = is_not_prime(N),
                    Val = not(lists:member(true, Values)),
                    From ! {primes, Val}
            end,
            loop()
    end.

for(N,N,_,F) -> [F(N)];
for(I,N,S,F) when I + S  [F(I)|for(I+S, N, S, F)];
for(I,N,S,F) when I + S =:= N -> [F(I)|for(I+S, N, S, F)];
for(I,N,S,F) when I + S > N -> [F(I)].

get_list(I, Limit) ->
    if
        I
            [I*A || A
            []
    end.

is_not_prime(N) ->
    for(3, N, 2,
        fun(I) ->
            List = get_list(I,trunc(N/I)),
            lists:member(N,lists:flatten(List))
        end
        ).

    %%L = for(1,N, fun() -> spawn(fun(I) -> wait(I,N) end) end),
    %%SeedList = [A || A  
    %%      lists:foreach(fun(X) ->
    %%              Pid ! {in_list, X}
    %%                end, SeedList)
    %%        end, L).

%%wait(I,N) ->
%%  List = [I*A || A  lists:member(X,List)
%%  end.

我使用Go和channel编写了Eratosthenesque并发初筛。

这是代码:http://github.com/aht/gosieve

我在这里写了关于它的博客:http://blog.onideas.ws/eratosthenes.go

该程序可以在大约10秒钟内筛选出前一百万个素数(所有素数最高为15,485,863)。 筛是并发的,但是该算法主要是同步的:goroutines(" actor",如果您愿意的话)之间需要太多同步点,因此它们不能并行自由漫游。


" badarity"错误表示您正在尝试使用错误数量的参数来调用" fun"。在这种情况下...

%% L = for(1,N,fun()-> spawn(fun(I)-> wait(I,N)end)end),

for / 3函数期望有趣的是1,spawn / 1函数希望有趣的是0。请尝试以下操作:

1
L = for(1, N, fun(I) -> spawn(fun() -> wait(I, N) end) end),

传递给spawn的乐趣继承了环境的必要部分(即I),因此无需显式传递它。

尽管计算素数总是很有趣,但是请记住,这不是Erlang旨在解决的问题。 Erlang是为大型actor风格的并发设计的。在所有数据并行计算示例上,它极有可能表现不佳。在许多情况下,例如ML中的顺序解决方案将是如此之快,以至于任何数量的内核都无法满足Erlang的需求,例如对于此类操作,F#和.NET Task Parallel Library无疑是更好的工具。


您可以在此处找到四个不同的Erlang实现来查找质数(其中两个基于Eratosthenes的Sieve)。该链接还包含比较这4个解决方案的性能的图表。


要考虑的另一种选择是使用概率素数生成。在Joe的书("主要服务器")中有一个例子,我认为它使用了Miller-Rabin。


两个快速的单过程erlang素生成器; sprimes会在约2.7秒内在2m以下生成所有素数,在我的计算机(配备2.4 GHz Core 2 Duo的Macbook)上约f3秒钟生成fprimes。两者都基于Eratosthenes的Sieve,但是由于Erlang最适合列表而不是数组,因此它们都保留一个未消除质数的列表,按当前磁头检查可除性,并保留经过验证的质数的累加器。两者都还实施了原轮以初步减少清单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
-module(primes).
-export([sprimes/1, wheel/3, fprimes/1, filter/2]).    

sieve([H|T], M) when H=< M -> [H|sieve([X || X<- T, X rem H /= 0], M)];
sieve(L, _) -> L.
sprimes(N) -> [2,3,5,7|sieve(wheel(11, [2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10], N), math:sqrt(N))].

wheel([X|Xs], _Js, M) when X > M ->
    lists:reverse(Xs);
wheel([X|Xs], [J|Js], M) ->
    wheel([X+J,X|Xs], lazy:next(Js), M);
wheel(S, Js, M) ->
    wheel([S], lazy:lazy(Js), M).

fprimes(N) ->
    fprimes(wheel(11, [2,4,2,4,6,2,6,4,2,4,6,6,2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,6,4,2,4,6,2,6,4,2,4,2,10,2,10], N), [7,5,3,2], N).
fprimes([H|T], A, Max) when H*H =< Max ->
    fprimes(filter(H, T), [H|A], Max);
fprimes(L, A, _Max) -> lists:append(lists:reverse(A), L).

filter(N, L) ->
    filter(N, N*N, L, []).
filter(N, N2, [X|Xs], A) when X < N2 ->
    filter(N, N2, Xs, [X|A]);
filter(N, _N2, L, A) ->
    filter(N, L, A).
filter(N, [X|Xs], A) when X rem N /= 0 ->
    filter(N, Xs, [X|A]);
filter(N, [_X|Xs], A) ->
    filter(N, Xs, A);
filter(_N, [], A) ->
    lists:reverse(A).

lazy:lazy / 1和lazy:next / 1是指伪惰性无限列表的简单实现:

1
2
3
4
5
6
7
lazy(L) ->
    repeat(L).

repeat(L) -> L++[fun() -> L end].

next([F]) -> F()++[F];
next(L) -> L.

筛子的质数生成不是并发的好地方(但是它可以使用并行性检查可除性,尽管操作还不够复杂,不足以证明我到目前为止编写的所有并行过滤器的额外开销。)

`


素数并行算法:http://www.cs.cmu.edu/~scandal/cacm/node8.html


Eratosthenes筛网非常容易实现,但是-正如您所发现的-效率最高。您是否尝试过阿特金筛子?

阿特金筛子@维基百科


这是vb版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    'Sieve of Eratosthenes
'
http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
'1. Create a contiguous list of numbers from two to some highest number n.
'
2. Strike out from the list all multiples of two (4, 6, 8 etc.).
'3. The list's next number that has not been struck out is a prime number.
'4. Strike out from the list all multiples of the number you identified in the previous step.
'
5. Repeat steps 3 and 4 until you reach a number that is greater than the square root of n (the highest number in the list).
'6. All the remaining numbers in the list are prime.
Private Function Sieve_of_Eratosthenes(ByVal MaxNum As Integer) As List(Of Integer)
    '
tested to MaxNum = 10,000,000 - on 1.8Ghz Laptop it took 1.4 seconds
    Dim thePrimes As New List(Of Integer)
    Dim toNum As Integer = MaxNum, stpw As New Stopwatch
    If toNum > 1 Then 'the first prime is 2
        stpw.Start()
        thePrimes.Capacity = toNum '
size the list
        Dim idx As Integer
        Dim stopAT As Integer = CInt(Math.Sqrt(toNum) + 1)
        '1. Create a contiguous list of numbers from two to some highest number n.
        '
2. Strike out from the list all multiples of 2, 3, 5.
        For idx = 0 To toNum
            If idx > 5 Then
                If idx Mod 2 <> 0 _
                AndAlso idx Mod 3 <> 0 _
                AndAlso idx Mod 5 <> 0 Then thePrimes.Add(idx) Else thePrimes.Add(-1)
            Else
                thePrimes.Add(idx)
            End If
        Next
        'mark 0,1 and 4 as non-prime
        thePrimes(0) = -1
        thePrimes(1) = -1
        thePrimes(4) = -1
        Dim aPrime, startAT As Integer
        idx = 7 '
starting at 7 check for primes and multiples
        Do
            '3. The list's next number that has not been struck out is a prime number.
            '4. Strike out from the list all multiples of the number you identified in the previous step.
            '
5. Repeat steps 3 and 4 until you reach a number that is greater than the square root of n (the highest number in the list).
            If thePrimes(idx) <> -1 Then ' if equal to -1 the number is not a prime
                '
not equal to -1 the number is a prime
                aPrime = thePrimes(idx)
                'get rid of multiples
                startAT = aPrime * aPrime
                For mltpl As Integer = startAT To thePrimes.Count - 1 Step aPrime
                    If thePrimes(mltpl) <> -1 Then thePrimes(mltpl) = -1
                Next
            End If
            idx += 2 '
increment index
        Loop While idx < stopAT
        '6. All the remaining numbers in the list are prime.
        thePrimes = thePrimes.FindAll(Function(i As Integer) i <> -1)
        stpw.Stop()
        Debug.WriteLine(stpw.ElapsedMilliseconds)
    End If
    Return thePrimes
End Function

欧拉计划的问题(我想说的是前50个问题中的大多数,如果不是更多的话)主要是关于蛮力的问题,在选择边界时会出现一些独创性。

记住要测试N是否为素数(通过蛮力),您只需要查看N是否可被楼数(sqrt(N))+ 1整除,而不是N / 2。

祝好运


我爱欧拉计划。

在主要发生器方面,我是Eratosthenes筛网的忠实拥护者。

对于2,000,000以下的数字,您可以尝试一个简单的isPrime check实现。我不知道您将如何使用erlang进行操作,但是逻辑很简单。

1
2
3
4
5
6
7
8
9
For Each NUMBER in LIST_OF_PRIMES
     If TEST_VALUE % NUMBER == 0
          Then FALSE
END
TRUE

if isPrime == TRUE add TEST_VALUE to your LIST_OF_PRIMES

iterate starting at 14 or so with a preset list of your beginning primes.

C#在不到1分钟的时间里就为2,000,000运行了这样的列表

编辑:在一个侧面说明,Eratosthenes的筛子可以很容易地实现并快速运行,但是当您进入庞大的清单时变得笨拙。使用布尔数组和int值的最简单实现运行得非常快。问题在于您开始遇到限制值大小和数组长度的问题。 -切换到字符串或位数组实现很有帮助,但是您仍然面临着以较大的值遍历列表的挑战。


推荐阅读

    linux控制台编程命令?

    linux控制台编程命令?,系统,工具,环境,命令,名称,标准,不了,工作,发行,基础,s

    linux编程常用命令?

    linux编程常用命令?,系统,工作,信息,命令,地址,管理,工具,网络,基础,目录,lin

    学习linux命令记不住?

    学习linux命令记不住?,电脑,基础,工作,信息,命令,系统,标准,数字,服务,参数,

    想系统学习linux命令?

    想系统学习linux命令?,系统,基础,基础知识,管理,技术,软件,命令,脚本,高效,

    编程解析linux命令?

    编程解析linux命令?,系统,标准,基础,设备,发行,电脑,工具,密码,名字,适当,如

    查看linux类型命令?

    查看linux类型命令?,系统,信息,命令,状态,数据,数字,情况,地址,类型,文件,lin

    linux删除类型命令?

    linux删除类型命令?,系统,档案,命令,文件,名称,环境,数据,不了,目录,文件夹,

    linux命令行图形编程?

    linux命令行图形编程?,系统,不了,情况,密码,工具,地方,百度,管理,图形界面,

    linux编程执行命令?

    linux编程执行命令?,电脑,系统,环境,命令,基础,发行,工具,代码,地址,名称,lin

    查看linux库类型命令?

    查看linux库类型命令?,系统,工作,信息,状态,电脑,命令,工具,代码,地址,发行,

    linux终端命令行编程?

    linux终端命令行编程?,系统,工作,命令,终端,概念,时间,第一,代码,发行,地方,L

    linux网卡类型命令?

    linux网卡类型命令?,网络,系统,地址,信息,设备,状态,服务,名称,名字,网卡,如

    linux编程调用命令?

    linux编程调用命令?,系统,标准,管理,工作,基础知识,情况,环境,设备,基础,首

    linux编程所需的命令?

    linux编程所需的命令?,工作,地址,档案,系统,命令,管理,标准,信息,目录,文件,L

    linux命令行编程乱码?

    linux命令行编程乱码?,环境,统一,乱码,中文,状态,软件,数据,系统,字符集,文

    查看linux并发量命令?

    查看linux并发量命令?,系统,第一,数字,对比,状态,数据,命令,报告,服务,环境,l

    linux并发测试命令?

    linux并发测试命令?,网络,地址,系统,工作,服务,网址,检测,软件,标准,信息,lin

    linux查看命令类型用?

    linux查看命令类型用?,信息,系统,情况,命令,实时,工作,设备,电脑,文件,类型,

    linux命令三种类型?

    linux命令三种类型?,工作,地址,系统,标准,时间,管理,命令,目录,信息,文件,lin

    linux编程c命令符?

    linux编程c命令符?,工具,代码,系统,保险,环境,文件,程序,命令,终端,编辑,到底