Board logo

标题: [讨论][共同参与]求1到1000所有素数的和 [打印本页]

作者: lxmxn     时间: 2007-1-24 11:01    标题: [讨论][共同参与]求1到1000所有素数的和


  无聊的时候玩了一把黑客游戏,有一关是一个编程题,题目是求1到1000所有的素数的和,然后加一个sixtoseven.asp就是下一关的地址。

  想来自己不会其它的编程语言,于是就用批处理脚本写了一个出来了,但是效率实在是令人不敢恭维,于是放出来让大家也来参与一下,应该用怎样的算法使这个批处理脚本的运行效率提高。如果遇到好的算法,我会酌情加分的,以表鼓励。
 
  我写的代码如下:
@echo %db% off&setlocal enabledelayedexpansion
set t1=%time% %=开始计时=%
set ss=5
%=这里设置5是忽略了2和3,所以这里要加上=%
for /l %%a in (1,2,1000) do (
        %=for里面设置步长(step)为2是为了剔除偶数,这样效率可能要高些=%
        set /a num=%%a
        set /a tc3=!num! "%%" 3
        if !tc3! neq  0 (
        set /a n=!num!-1
        call :sss !n!
        if [!flag!]==[bbbb] (echo !num!&set /a ss+=!num!)
        )
)
echo 1—1000 中所有的素数加起来的和为: [ %ss% ]%=显示结果=%
set t2=%time%%=计时结束=%
echo/
echo 开始时间:%t1%
echo 结束时间:%t2%
echo/
pause&exit/b0
::::::::::::::::::Sub Function::::::::::::::::::::
:sss
        set a=
        for /l %%a in (2,1,%1) do (
        set /a a=!num! "%%" %%a
        if !a! equ 0 (
                set flag=aaaa
                goto :eof
        ) else (
        set flag=bbbb)
        )

作者: vkill     时间: 2007-1-24 11:18
回去想想

[ Last edited by vkill on 2007-1-24 at 11:20 AM ]
作者: lxmxn     时间: 2007-1-24 11:24


  Quote:
Originally posted by vkill at 2007-1-23 22:18:
回去想想
[ Last edited by vkill on 2007-1-24 at 11:20 AM ]


  帖一个网上的解释吧:来自http://sjweb.hhit.edu.cn/article/show.aspx?id=933&cid=71

  Quote:
素数是这样的整数,它除了能表示为它自己和1的乘积以外,不能表示为任
何其它两个整数的乘积。例如,15=3*5,所以15不是素数;又如,12
=6*2=4*3,所以12也不是素数。另一方面,13除了等于13*1以
外,不能表示为其它任何两个整数的乘积,所以13是一个素数。


作者: namejm     时间: 2007-1-24 12:25
  寻找素数在2005年似乎有了新的方法,这种方法出奇地简单,简单到足以让数学家们警觉起来,请看这篇文章:http://www.irgoc.org/Article/ShowArticle.asp?ArticleID=85。奈何本人不懂那些代码的具体含义,难以转化为批处理来解决,哪位懂得的不妨来转换一下。不过,里面用到了log运算,估计批处理解决起来会相当的麻烦。

  另外,lxmxn 在3楼给出的链接中提到了“从2开始,是则留下,不是则去掉”的方法,经过实地演算(仅测试了1~30这个范围的数),当处理到5这个素数的时候,会去掉19,看来这种方法并不能成立。

[ Last edited by namejm on 2007-1-23 at 11:27 PM ]
作者: youxi01     时间: 2007-1-24 12:48
如果纯粹是处理1000以内的数字的话,采用以下的代码可以提高效率:
@echo off&setlocal enabledelayedexpansion
set t1=%time% %=开始计时=%
echo 2
echo 3
set/a sum=5
for /l %%a in (5,2,1000) do (
    set/a temp=%%a %% 3
    if !temp! NEQ 0 (
       set/a temp=%%a-1
       set flag=1
       for /l %%b in (3 2 !temp!) do (
          set/a num=%%a %% %%b
          if !num! EQU 0 set flag=0)) else set flag=0
     if !flag! EQU 1 echo %%a & set/a sum+=%%a)
set t2=%time%%=计时结束=%
echo/
echo 开始时间:%t1%
echo 结束时间:%t2%
echo 1000以内的素数和为:%sum%
pause>nul

作者: lxmxn     时间: 2007-1-24 12:51

  嗯,效率的确提高了不少,现在平均是5秒左右的时间完成计算,而我的代码要大约18秒。

作者: qzwqzw     时间: 2007-1-24 13:57
因为在考虑第三种方案

所以这第二种与5楼有些重复

区别仅在于没有在外层循环对3取模

算法的效率没有明显的提高
@echo off&setlocal enabledelayedexpansion
set t1=%time%
set sum=5
for /l %%i in (5,2,2000) do (
    set /a limit=%%i / 2
    set isPrime=true
    for /l %%j in (3,2,!limit!) do (
        set /a mod=%%i %% %%j
        if "!mod!"=="0" set isPrime=false
    )
    if "!isPrime!"=="true" (echo %%i&set /a sum+=%%i)
)
echo 1—1000 中所有的素数加起来的和为: [ %sum% ]
echo.
echo 开始时间:%t1%
echo 结束时间:%time%
echo.
pause

作者: qzwqzw     时间: 2007-1-24 14:00
这是第3种方案

仅拿小于所判断数的平方根的质数去除判断数

以缩小比较范围

可以计算到1~10000的规模
@echo off
setlocal enabledelayedexpansion
set t1=%time%
set sum=5
echo 2 >prime.txt
echo 3 >>prime.txt
for /l %%i in (5,2,10000) do (
    set /a limit=%%i / 2
    call :isPrime %%i
    if "!isPrime!"=="true" (
        echo %%i
        set /a sum+=%%i
    )
)
echo 1—10000 中所有的素数加起来的和为: [ %sum% ]%=显示结果=%
echo.
echo 开始时间:%t1%
echo 结束时间:%time%
echo.
pause
goto :eof

:isPrime
set isPrime=false
for /f %%j in (prime.txt) do (
    set /a mod=%1 %% %%j
    if !mod! equ 0 goto :eof
    set /a prd=%%j * %%j
    if !prd! geq %1 (
        echo %1 >>prime.txt
        set isPrime=true
        goto :eof
    )
)

作者: 0401     时间: 2007-1-24 14:14
很漂亮也,计算速度几乎不受大数的影响。不过一时半刻还看不太懂具体的原理。
作者: 0401     时间: 2007-1-24 14:19
这个算法数越大,速度就越慢。
@echo off
setlocal enabledelayedexpansion

set t=%time%
set s=2 3 5 7 %--1不是素数--%
for /l %%l in (1,1,99) do ( %--从十位开始计算,所以等下要加上2,3,5,7--%
        for %%i in (1 3 7 9) do ( %--素数的个位数只能是1,3,7,9--%
                set/a m=%%l%%i"%%"3
                if !m! neq 0 (
                        set flag=1
                        for %%m in (!s!) do ( %--这里用来取余的数是被取余数之前的素数集--% %--ps:我也不清楚这样算对不对--%
                                set/a b=%%l%%i"%%"%%m
                                if !b! equ 0 set flag=0
                        )
                ) else (set flag=0)
                if !flag! equ 1 (
                        echo %%l%%i
                        set/a sum+=%%l%%i
                        set s=!s! %%l%%i
                )
        )
)
set/a sum=%sum%+2+3+5+7
echo 开始于[%t%]
echo 结束于[%time%]
echo 1000以内的素数和为[%sum%]
lxmxn兄的注释很有意思,以前没发觉,我也借来用用,呵呵。
作者: youxi01     时间: 2007-1-24 22:28
如果是纯粹的10000以内,用以下的算法可能要快点(测试时间7秒内)
@echo off&setlocal enabledelayedexpansion
set t1=%time% %=开始计时=%
set sum_=5
echo.>Result.txt
echo 2
echo 3
for /l %%a in (5,2,100) do (
       set/a temp=%%a-1
       set flag=1
       for /l %%b in (3 2 !temp!) do (
          set/a num=%%a %% %%b
          if !num! EQU 0 set flag=0)
     if !flag! EQU 1 (
        echo %%a
        echo %%a>>Result.txt
        set/a sum_+=%%a))

for /l %%a in (101 2 10000) do (
   set/a mod=%%a %% 3
   if !mod! Neq 0 (
      set flag=1
      for /f  %%i in (Result.txt) do (
        set/a mod=%%a %% %%i
        if !mod! EQU 0 set flag=0)
      if !flag! EQU 1 echo %%a & set/a sum+=%%a))
set/a sum=!sum!+!sum_!
set t2=%time%%=计时结束=%
echo/
echo 开始时间:%t1%
echo 结束时间:%t2%
echo 1000以内的素数和为:%sum%
pause>nul

作者: youxi01     时间: 2007-1-24 22:30
8F的方案中 在循环中 “引用”了 call ,会大大降低效率的!
我的看法是宁愿牺牲一些不必要的数字(比如,在计算101是不是质数时,根据你的意思是没必要算到它能不能被97整除),不过因为可以不使用call,却倒使效率更高(因为纯粹只使用for循环)

[ Last edited by youxi01 on 2007-1-24 at 10:33 PM ]
作者: willsion     时间: 2007-1-24 23:12
建议版主多搞一些类似的小活动,吸引更多人参与讨论。

因为DOS论坛,基本属于技术性论坛,人气相对较少。
作者: lxmxn     时间: 2007-1-25 00:25

  呵呵,一晚上的时间又有这么多好的算法啊。

  特别是qzwqzw兄的第三个方案、0401以及youxi01在11楼的方案,效率都有明显的提高,看来要多花点时间来研究一下算法了,多谢各位的参与,今天加不成分,改天给各位加分鼓励。

作者: pengfei     时间: 2007-1-25 04:42
5F先除去奇数和偶数的方法使运行效率提高不少,  11F采用这种去奇偶数再判断的方法, 同时采用临时文件计算大范围的数的效率大大提高.

筛法求素数:
列出要求的所有素数, 然后逐个判断它们是否素数, 找出一个非素数, 就把它挖掉, 最后剩下的就是素数.

算法:
1. 先将1挖掉;
2. 用2去除它后面的各个数, 把能被2整除的数挖掉, 即把2的倍数挖掉.
3. 用3去除它后面各数, 把3的倍数挖掉.
4. 分别用4,5... 各数作为除数去除这些数以后的各数. 直到除数后面的数已被挖掉为止(事实上, 只需进行到除数n的平方根止).
5. 余下的数都为素数.

上面的算法还可以改进, 以提高效率. 批处理中通过构建数组, 完整地实现用筛法求素数.
@echo off
setlocal enabledelayedexpansion
set t=%time%
set sqrt=31
set n=1000
for /l %%i in (1,1,%n%) do set num%%i=%%i
for /l %%i in (2,1,%sqrt%) do (
    set /a i=%%i+1
    if not "!num%%i!"=="0" (
        for /l %%j in (!i!,1,%n%) do (
            if not "!num%%j!"=="0" (
                set /a x=!num%%j! %% %%i
                if "!x!"=="0" set num%%j=0
            )
        )
    )
)
for /l %%i in (2,1,%n%) do (
    if not "!num%%i!"=="0" (
        set /p =!num%%i!<nul
        set /a s+=%%i
    )
)
echo.
echo.
echo 1-%n%的素数和=%s%
echo 起始时间=%t%
echo 结束时间=%time%
pause
此代码当处理范围较小时效率比较理想, 我也用C写了一个, 看来C写出来的程序效率的确很高.
附件 1: prime.rar (2007-1-25 12:21, 4.25 K, 下载附件所需积分 1点 ,下载次数: 10)

作者: tao0610     时间: 2007-1-25 04:48
其实不得不爱版主原来写过一个,改改就能用了.
@echo off&setlocal enabledelayedexpansion
set t1=%time% %=开始计时=%
set sum=17&set n=3&set n1=3&set n2=5&set n3=7
set/p a= 2 3 5 7 <nul
for /l %%a in (9 2 99) do (
set/a a=%%a%%3,b=%%a%%5,c=%%a%%7
if not !a!==0 if not !b!==0 if not !c!==0 set/p= %%a<nul&set/a sum+=%%a&set/a n+=1&&set n!n!=%%a)
for /l %%a in (101 2 9999) do (
for /l %%b in (1,1,%n%) do (
set/a a=%%a%%n%%b
if !a!==0 set b=1)
if not !b!==1 set/p= %%a<nul&set/a sum+=%%a
set b=0)
set t2=%time%%=计时结束=%
echo/
echo 开始时间:%t1%
echo 结束时间:%t2%
echo 10000以内的素数和为:%sum%
pause
VB,C和批处理的运算机制应该不太一样.所以比较大的运算肯定高级语言效率高.
作者: pengfei     时间: 2007-1-25 05:38
楼上很不错的代码, 效率高, 规律找得好, 加分~~~
作者: lzmyst     时间: 2007-1-25 05:50
俺要COPY下来收藏慢慢看。
作者: qzwqzw     时间: 2007-1-25 09:47
从头至尾看下来

除了15楼的筛选法有些与众不同外

其它代码的核心算法都是大同小异的试除法

只是在利用的语法特性和剪枝上有些变化

这导致算法效率很难有数量级的提升

所以,我们还是应该多在核心算法上多下功夫

我建议下一目标应该是:有效时间内完成单个任意32位整数是否素数的判定

--------------------------------------------------------------------------------------------

下面的代码仍是基于16楼代码的变化形式

本来不想发的,只是觉得仍然有些新东西
@echo off
setlocal enabledelayedexpansion
set t1=%time% %=开始计时=%
set /a s=17, n=3, n1=3, n2=5, n3=7
for /l %%a in (9,2,99) do (
    set/a p=%%a%%3 * %%a%%5 * %%a%%7
    if !p! neq 0 (
        set/a s+=%%a, n+=1
        set n!n!=%%a
    )
)
for /l %%a in (101,2,9999) do (
    set p=1
    for /l %%b in (1,1,%n%) do (
        if !p! neq 0 set/a "p=(%%a%%n%%b)*p/p"
    )
    if !p! neq 0 set/a s+=%%a
)
set t2=%time%%=计时结束=%
echo/
echo 开始时间:%t1%
echo 结束时间:%t2%
echo 10000以内的素数和为:%s%
pause

作者: pengfei     时间: 2007-1-25 10:03
16楼的代码在找规律上花了不少心血, 是不得不爱版主写的, 欣赏~~~

暂时叫这种方法为"素数相除法"吧, 也不知道对不对.

找到的这个规律还是有局限性的, 而且当取素数的范围超过1-10000, 就会出错, 必须再用101-1000以内或其他高位更多的素数除以10000以后的各数才能保证取到的为素数, 显然这种处理方法就不适宜处理更大的数了. 但求1-10000以内的素数效率还是很高的.
素数相除法: 先用10以内的素数除以100以内有各数, 得到1-100的素数, 再用1-100内的素数除以101-10000以内的数, 得到1-10000以内的素数.


下面是两种算法对求1-11000以内素数和的结果, 素数相除法因为没有采用更高位的素数除10000以后的数, 把一些非素数也判断成了素数. 素数和也更大.

筛法: 1-11000范围内素数和为:的6848586
素数相除法: 1-11000范围内素数和为:6890606
平时我们常用的是筛法求素数(算法在15楼), 但看了namejm版主4楼给出的页面链接印度计算机专家给出的新的算法, 说在处理更大的数时要快很多.

可是网页中给出的算法好像是用伪代码写的, 怎么看也看不懂. 先存好, 再细细去想吧~~~
素数判定算法

(当且仅当n为素数时,最终输出数才为素数)

(当且仅当n为素数时,最终输出数才为素数)
Input: integer n&gt;1
1. if ( n is the form of a^b, b&gt;1) output Composite;
2. r = 2;
3. while (r)
4. if (gcd(n,r)1) output Composite;
5. if (r is the prime)
6. let q be the largest factor of r-1;
7. if (q&gt;= 4 * (r^(1/2)) * log(n)) and (n^((r-1)/q))) mod r 1)
8. break;
9. r:=r+1;
10. {
11. for a := 1 to 2*(r^(1/2))*log(n);
12.if ((x-a)^n mod n(x^n-a)) and ((x-a)^n mod (x^r -1)(x^n-a))output Composite;
13.output Prime;
[ Last edited by pengfei on 2007-1-25 at 10:53 AM ]
作者: qzwqzw     时间: 2007-1-25 10:31
关于以素数相除的算法起源很早,现在已很难考证

不过我是从willsort的代码中获取的素数相除的思想的

不得不爱的代码
http://www.cn-dos.net/forum/view ... ghlight=&page=6

willsort的代码
http://www.cn-dos.net/forum/viewthread.php?tid=14580

----------------------------------------------------------------------

至于namejm给出链接

似乎有C语言的实现

但是国内根本就找不到

Google到的国外网址地震原因无法访问

------------------------------------------------------------

不过从国内讨论的结果来看

实现起来将很复杂

而且这是个概率算法

即判定结果是非确定性的

同时它的性能与实现方式严格相关

所以我基本上已不再考虑此算法
作者: pengfei     时间: 2007-1-25 10:48
按qzwqzw兄来说, 这种概率算法没有他们讲的那么容易实现.

给出的伪代码算法, 看起来也是一知半解, 有点头疼的感觉~~~
作者: stornager     时间: 2007-4-2 04:38
if !a! equ 0 (
                set flag=aaaa
                goto :eof
        ) else (
        set flag=bbbb)
        )
请问楼主这段代码是什吗意思?
作者: stornager     时间: 2007-4-2 04:46
::::::::::::::::::Sub Function::::::::::::::::::::
:sss
请楼主告知这段代码是什么意思.??????????